diff --git a/poc-apps/IPK/Python_packages/bs4_0.0.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/bs4_0.0.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..372f26d Binary files /dev/null and b/poc-apps/IPK/Python_packages/bs4_0.0.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/google-api-core_1.26.3-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/google-api-core_1.26.3-r0_raspberrypi4.ipk new file mode 100644 index 0000000..c052696 Binary files /dev/null and b/poc-apps/IPK/Python_packages/google-api-core_1.26.3-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/google-api-python-client_1.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/google-api-python-client_1.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..f4a8720 Binary files /dev/null and b/poc-apps/IPK/Python_packages/google-api-python-client_1.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/google-auth-oauthlib_1.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/google-auth-oauthlib_1.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..abf98c8 Binary files /dev/null and b/poc-apps/IPK/Python_packages/google-auth-oauthlib_1.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/google-auth_1.30.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/google-auth_1.30.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..191d9bd Binary files /dev/null and b/poc-apps/IPK/Python_packages/google-auth_1.30.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/google-cloud-speech_2.9.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/google-cloud-speech_2.9.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..3636cc2 Binary files /dev/null and b/poc-apps/IPK/Python_packages/google-cloud-speech_2.9.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/googleapis-common-protos_1.53.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/googleapis-common-protos_1.53.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..c3a9e58 Binary files /dev/null and b/poc-apps/IPK/Python_packages/googleapis-common-protos_1.53.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/gtts_1.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/gtts_1.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..32d29af Binary files /dev/null and b/poc-apps/IPK/Python_packages/gtts_1.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/httplib2_0.19.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/httplib2_0.19.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..26890b3 Binary files /dev/null and b/poc-apps/IPK/Python_packages/httplib2_0.19.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libglog0_0.3.5-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libglog0_0.3.5-r0_raspberrypi4.ipk new file mode 100644 index 0000000..7ff450f Binary files /dev/null and b/poc-apps/IPK/Python_packages/libglog0_0.3.5-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libjack_1.19.14-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libjack_1.19.14-r0_raspberrypi4.ipk new file mode 100644 index 0000000..d2897b2 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libjack_1.19.14-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-aruco4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-aruco4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..2e14560 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-aruco4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-bgsegm4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-bgsegm4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..5fdf563 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-bgsegm4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-bioinspired4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-bioinspired4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..d9dbf97 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-bioinspired4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-calib3d4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-calib3d4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..f90267e Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-calib3d4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-ccalib4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-ccalib4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..3f42923 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-ccalib4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-core4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-core4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..6f4d9cf Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-core4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-datasets4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-datasets4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..bb9f2d2 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-datasets4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-dnn-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-dnn-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..2fb9438 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-dnn-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-dnn4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-dnn4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..d5b57ed Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-dnn4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-dpm4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-dpm4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..4ffef79 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-dpm4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-face4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-face4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..c703198 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-face4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-features2d4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-features2d4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..bfb89e4 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-features2d4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-flann4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-flann4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..81b7dd1 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-flann4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-fuzzy4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-fuzzy4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..d242434 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-fuzzy4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-gapi4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-gapi4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..729e1b0 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-gapi4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-hfs4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-hfs4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..bbb0e6b Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-hfs4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-highgui4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-highgui4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..592a002 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-highgui4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-img-hash4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-img-hash4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..b3672d7 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-img-hash4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-imgcodecs4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-imgcodecs4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..63f3e94 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-imgcodecs4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-ml4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-ml4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..f192e93 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-ml4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..9a3fda6 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-optflow4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-optflow4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..c946a18 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-optflow4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-phase-unwrapping4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-phase-unwrapping4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..568a869 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-phase-unwrapping4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-photo4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-photo4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..bcf6822 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-photo4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-plot4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-plot4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..c076c4b Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-plot4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-quality4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-quality4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..dc3ecea Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-quality4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-reg4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-reg4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..936a91f Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-reg4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-rgbd4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-rgbd4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..7b9435d Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-rgbd4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-saliency4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-saliency4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..47806a4 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-saliency4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-sfm4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-sfm4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..dfbc2fb Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-sfm4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-shape4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-shape4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..c90140e Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-shape4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-stereo4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-stereo4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..f3145c4 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-stereo4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-stitching4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-stitching4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..d8187f1 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-stitching4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-structured-light4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-structured-light4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..15cafb8 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-structured-light4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-superres4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-superres4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..4308995 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-superres4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-surface-matching4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-surface-matching4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..5b627b8 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-surface-matching4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-tracking4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-tracking4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..72294ca Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-tracking4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-video4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-video4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..4dcf99b Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-video4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-videoio4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-videoio4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..404913c Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-videoio4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-videostab4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-videostab4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..b18d06b Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-videostab4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-xfeatures2d4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-xfeatures2d4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..7542f18 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-xfeatures2d4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-ximgproc4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-ximgproc4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..d0e4db7 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-ximgproc4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-xobjdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-xobjdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..f244ddb Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-xobjdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libopencv-xphoto4.1_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libopencv-xphoto4.1_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..bf6af91 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libopencv-xphoto4.1_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libprotobuf26_3.15.2-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libprotobuf26_3.15.2-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..8439555 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libprotobuf26_3.15.2-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libsamplerate0_0.1.9-r1_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libsamplerate0_0.1.9-r1_raspberrypi4.ipk new file mode 100644 index 0000000..48d9648 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libsamplerate0_0.1.9-r1_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libtiff5_4.1.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libtiff5_4.1.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..358304e Binary files /dev/null and b/poc-apps/IPK/Python_packages/libtiff5_4.1.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libunwind_1.3.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libunwind_1.3.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..4563011 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libunwind_1.3.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/libxslt_1.1.34-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/libxslt_1.1.34-r0_raspberrypi4.ipk new file mode 100644 index 0000000..2dbe672 Binary files /dev/null and b/poc-apps/IPK/Python_packages/libxslt_1.1.34-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/oauth2client_1.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/oauth2client_1.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..8009bba Binary files /dev/null and b/poc-apps/IPK/Python_packages/oauth2client_1.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/opencv-apps_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/opencv-apps_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..e405b08 Binary files /dev/null and b/poc-apps/IPK/Python_packages/opencv-apps_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/opencv-samples_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/opencv-samples_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..0fc66ca Binary files /dev/null and b/poc-apps/IPK/Python_packages/opencv-samples_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/opencv_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/opencv_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..384cff5 Binary files /dev/null and b/poc-apps/IPK/Python_packages/opencv_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/portaudio-v19_v190600-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/portaudio-v19_v190600-r0_raspberrypi4.ipk new file mode 100644 index 0000000..1ff037f Binary files /dev/null and b/poc-apps/IPK/Python_packages/portaudio-v19_v190600-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/proto-plus_1.19.2-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/proto-plus_1.19.2-r0_raspberrypi4.ipk new file mode 100644 index 0000000..bd40206 Binary files /dev/null and b/poc-apps/IPK/Python_packages/proto-plus_1.19.2-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-atomicwrites_1.3.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-atomicwrites_1.3.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..f06c5d8 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-atomicwrites_1.3.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-attrs_19.3.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-attrs_19.3.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..5c6bf7d Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-attrs_19.3.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-beautifulsoup4_4.8.2-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-beautifulsoup4_4.8.2-r0_raspberrypi4.ipk new file mode 100644 index 0000000..1092e96 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-beautifulsoup4_4.8.2-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-cachetools_4.1.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-cachetools_4.1.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..befe927 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-cachetools_4.1.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-grpcio_1.37.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-grpcio_1.37.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..7c381cf Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-grpcio_1.37.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-html5lib_1.0.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-html5lib_1.0.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..7073588 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-html5lib_1.0.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-importlib-metadata_1.5.2-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-importlib-metadata_1.5.2-r0_raspberrypi4.ipk new file mode 100644 index 0000000..d5bd9cd Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-importlib-metadata_1.5.2-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-lxml_4.5.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-lxml_4.5.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..1a3a255 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-lxml_4.5.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-more-itertools_8.2.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-more-itertools_8.2.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..ad243cf Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-more-itertools_8.2.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-nose_1.3.7-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-nose_1.3.7-r0_raspberrypi4.ipk new file mode 100644 index 0000000..de75106 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-nose_1.3.7-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-numpy_1.17.4-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-numpy_1.17.4-r0_raspberrypi4.ipk new file mode 100644 index 0000000..90a74f7 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-numpy_1.17.4-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-opencv_4.1.0-r0webos2_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-opencv_4.1.0-r0webos2_raspberrypi4.ipk new file mode 100644 index 0000000..5dbdc79 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-opencv_4.1.0-r0webos2_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-packaging_20.3-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-packaging_20.3-r0_raspberrypi4.ipk new file mode 100644 index 0000000..5ced889 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-packaging_20.3-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pathlib2_2.3.5-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pathlib2_2.3.5-r0_raspberrypi4.ipk new file mode 100644 index 0000000..ec898d1 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pathlib2_2.3.5-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pip_20.0.2-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pip_20.0.2-r0_raspberrypi4.ipk new file mode 100644 index 0000000..5a6b277 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pip_20.0.2-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pluggy_0.13.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pluggy_0.13.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..c6200df Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pluggy_0.13.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-protobuf_3.14.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-protobuf_3.14.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..205e24d Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-protobuf_3.14.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-py_1.8.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-py_1.8.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..3280486 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-py_1.8.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pyaudio_0.2.11-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pyaudio_0.2.11-r0_raspberrypi4.ipk new file mode 100644 index 0000000..2975268 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pyaudio_0.2.11-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pyparsing_2.4.6-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pyparsing_2.4.6-r0_raspberrypi4.ipk new file mode 100644 index 0000000..9bd8910 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pyparsing_2.4.6-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pytest_5.3.5-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pytest_5.3.5-r0_raspberrypi4.ipk new file mode 100644 index 0000000..030fa2b Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pytest_5.3.5-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-pytz_2019.3-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-pytz_2019.3-r0_raspberrypi4.ipk new file mode 100644 index 0000000..9e3c8c2 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-pytz_2019.3-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-soupsieve_1.9.4-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-soupsieve_1.9.4-r0_raspberrypi4.ipk new file mode 100644 index 0000000..19a748c Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-soupsieve_1.9.4-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-speech-recognition_3.8.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-speech-recognition_3.8.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..cc79146 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-speech-recognition_3.8.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-uritemplate_3.0.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-uritemplate_3.0.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..e2ecc69 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-uritemplate_3.0.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-wcwidth_0.1.8-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-wcwidth_0.1.8-r0_raspberrypi4.ipk new file mode 100644 index 0000000..4ab8ff7 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-wcwidth_0.1.8-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-webencodings_0.5.1-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-webencodings_0.5.1-r0_raspberrypi4.ipk new file mode 100644 index 0000000..10eb905 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-webencodings_0.5.1-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/python3-zipp_0.6.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/python3-zipp_0.6.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..8bd65b1 Binary files /dev/null and b/poc-apps/IPK/Python_packages/python3-zipp_0.6.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/IPK/Python_packages/wikipedia_1.4.0-r0_raspberrypi4.ipk b/poc-apps/IPK/Python_packages/wikipedia_1.4.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..8f25770 Binary files /dev/null and b/poc-apps/IPK/Python_packages/wikipedia_1.4.0-r0_raspberrypi4.ipk differ diff --git a/poc-apps/README.md b/poc-apps/README.md new file mode 100644 index 0000000..b8c8f8f --- /dev/null +++ b/poc-apps/README.md @@ -0,0 +1,3 @@ +# Poc_Apps + +POC apps Source and Packages \ No newline at end of file diff --git a/poc-apps/com.app.objectviewer_0.0.1_all.ipk b/poc-apps/com.app.objectviewer_0.0.1_all.ipk new file mode 100644 index 0000000..eba783b Binary files /dev/null and b/poc-apps/com.app.objectviewer_0.0.1_all.ipk differ diff --git a/poc-apps/easy_setup.sh b/poc-apps/easy_setup.sh new file mode 100644 index 0000000..10d1912 --- /dev/null +++ b/poc-apps/easy_setup.sh @@ -0,0 +1,112 @@ +#!/bin/s + +wget --no-check-certificate https://github.com/webosose/samples/archive/refs/heads/master.zip +unzip master.zip -d poc +TEST=$(pwd) +cd poc/samples-master/poc-apps/ + +opkg install voiceui_1.0.0-r0_raspberrypi4.ipk +cd IPK/Python_packages +opkg install libopencv-core4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-flann4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-imgproc4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-features2d4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-calib3d4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-aruco4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-video4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-bgsegm4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-bioinspired4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-highgui4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libtiff5_4.1.0-r0_raspberrypi4.ipk +opkg install libopencv-imgcodecs4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-ccalib4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-datasets4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-dnn4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-dnn-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-dpm4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-objdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-face4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-fuzzy4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-gapi4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-hfs4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-img-hash4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-line-descriptor4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-ml4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-ximgproc4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-optflow4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-phase-unwrapping4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-photo4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-plot4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-quality4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-reg4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-rgbd4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-saliency4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libunwind_1.3.1-r0_raspberrypi4.ipk +opkg install libglog0_0.3.5-r0_raspberrypi4.ipk +opkg install libopencv-sfm4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-shape4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-stereo4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-stitching4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-structured-light4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-videoio4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-superres4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-surface-matching4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-tracking4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-videostab4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-xfeatures2d4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-xobjdetect4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install libopencv-xphoto4.1_4.1.0-r0webos2_raspberrypi4.ipk +opkg install opencv-apps_4.1.0-r0webos2_raspberrypi4.ipk +opkg install opencv-samples_4.1.0-r0webos2_raspberrypi4.ipk +opkg install python3-nose_1.3.7-r0_raspberrypi4.ipk +opkg install python3-numpy_1.17.4-r0_raspberrypi4.ipk +opkg install python3-opencv_4.1.0-r0webos2_raspberrypi4.ipk +opkg install opencv_4.1.0-r0webos2_raspberrypi4.ipk +opkg install wikipedia_1.4.0-r0_raspberrypi4.ipk +opkg install libxslt_1.1.34-r0_raspberrypi4.ipk --nodeps +opkg install python3-lxml_4.5.0-r0_raspberrypi4.ipk +opkg install python3-webencodings_0.5.1-r0_raspberrypi4.ipk +opkg install python3-html5lib_1.0.1-r0_raspberrypi4.ipk +opkg install python3-soupsieve_1.9.4-r0_raspberrypi4.ipk +opkg install python3-beautifulsoup4_4.8.2-r0_raspberrypi4.ipk +opkg install bs4_0.0.1-r0_raspberrypi4.ipk +opkg install libsamplerate0_0.1.9-r1_raspberrypi4.ipk +opkg install libjack_1.19.14-r0_raspberrypi4.ipk +opkg install portaudio-v19_v190600-r0_raspberrypi4.ipk +opkg install python3-pyaudio_0.2.11-r0_raspberrypi4.ipk +opkg install python3-speech-recognition_3.8.1-r0_raspberrypi4.ipk --nodeps +opkg install google-api-core_1.26.3-r0_raspberrypi4.ipk +opkg install google-api-python-client_1.0-r0_raspberrypi4.ipk +opkg install httplib2_0.19.1-r0_raspberrypi4.ipk +opkg install python3-pyparsing_2.4.6-r0_raspberrypi4.ipk +opkg install python3-uritemplate_3.0.1-r0_raspberrypi4.ipk +opkg install google-auth_1.30.1-r0_raspberrypi4.ipk +opkg install google-auth-oauthlib_1.0-r0_raspberrypi4.ipk +opkg install python3-cachetools_4.1.0-r0_raspberrypi4.ipk +opkg install oauth2client_1.0-r0_raspberrypi4.ipk +opkg install python3-protobuf_3.14.0-r0_raspberrypi4.ipk --nodeps +opkg install googleapis-common-protos_1.53.0-r0_raspberrypi4.ipk +opkg install proto-plus_1.19.2-r0_raspberrypi4.ipk +opkg install python3-packaging_20.3-r0_raspberrypi4.ipk +opkg install python3-atomicwrites_1.3.0-r0_raspberrypi4.ipk +opkg install python3-attrs_19.3.0-r0_raspberrypi4.ipk +opkg install python3-pathlib2_2.3.5-r0_raspberrypi4.ipk +opkg install python3-more-itertools_8.2.0-r0_raspberrypi4.ipk +opkg install python3-zipp_0.6.0-r0_raspberrypi4.ipk +opkg install python3-importlib-metadata_1.5.2-r0_raspberrypi4.ipk +opkg install python3-pluggy_0.13.1-r0_raspberrypi4.ipk +opkg install python3-py_1.8.1-r0_raspberrypi4.ipk +opkg install python3-wcwidth_0.1.8-r0_raspberrypi4.ipk +opkg install python3-pytest_5.3.5-r0_raspberrypi4.ipk +opkg install google-cloud-speech_2.9.1-r0_raspberrypi4.ipk +opkg install python3-grpcio_1.37.0-r0_raspberrypi4.ipk --nodeps +opkg install python3-pytz_2019.3-r0_raspberrypi4.ipk +opkg install libprotobuf26_3.15.2-r0webos2_raspberrypi4.ipk --nodeps +opkg install gtts_1.0-r0_raspberrypi4.ipk +opkg install python3-pip_20.0.2-r0_raspberrypi4.ipk +pip3 install oauthlib==3.1.1 +pip3 install google-auth-httplib2 + +cd $TEST +rm -rf poc +rm master.zip diff --git a/poc-apps/object-detection/CamImage.py b/poc-apps/object-detection/CamImage.py new file mode 100644 index 0000000..5876ae7 --- /dev/null +++ b/poc-apps/object-detection/CamImage.py @@ -0,0 +1,371 @@ +# Copyright (c) 2021-2022 LG Electronics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import cv2 +import numpy as np +import requests +import os +import pyaudio +import wave +from collections import Counter + +def talk(text): + from gtts import gTTS + import os + mytext = text + language = 'en' + myobj = gTTS(text=mytext, lang=language, slow=False) + myobj.save("welcome.mp3") + os.system("mpg123 " + "welcome.mp3") + + +def take_alter_command(): + def voicein(): + CHUNK = 515 + FORMAT = pyaudio.paInt16 + CHANNELS = 2 + RATE = 44100 + RECORD_SECONDS = 5 + WAVE_OUTPUT_FILENAME = "output.wav" + + p = pyaudio.PyAudio() + + stream = p.open(format=FORMAT, + channels=CHANNELS, + rate=RATE, + input=True, + frames_per_buffer=CHUNK) + print("* recording") + + frames = [] + + for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): + data = stream.read(CHUNK) + frames.append(data) + print("* done recording") + + stream.stop_stream() + stream.close() + p.terminate() + + wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') + wf.setnchannels(CHANNELS) + wf.setsampwidth(p.get_sample_size(FORMAT)) + wf.setframerate(RATE) + wf.writeframes(b''.join(frames)) + wf.close() + + from google.cloud import speech + import io + + client = speech.SpeechClient() + speech_file = "/home/root/ObjectDetection/output.wav" + + with io.open(speech_file, "rb") as audio_file: + content = audio_file.read() + + audio = speech.RecognitionAudio(content=content) + config = speech.RecognitionConfig( + encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16, + sample_rate_hertz=44100, + language_code="en-US", + audio_channel_count=2, + ) + + response = client.recognize(config=config, audio=audio) + + # Each result is for a consecutive portion of the audio. Iterate through + # them to get the transcripts for the entire audio file. + + if len(response.results) == 0: + return voicein() + else: + for result in response.results: + # The first alternative is the most likely one for this portion. + + print(u"Transcript: {}".format(result.alternatives[0].transcript)) + resultvoice = (format(result.alternatives[0].transcript)) + resultvoice = resultvoice.lower() + if 'lg' in resultvoice: + outputvoice = resultvoice.replace('lg', '') + return outputvoice + else: + return take_alter_command() + return resultvoice + text = voicein() + return text; + +def run_lg(): + InputCommand = take_alter_command() + InputCommand = InputCommand.lower() + if "potted plant" in InputCommand: + InputCommand = InputCommand.replace("potted plant", "pottedplant") + if "tv monitor" in InputCommand: + InputCommand = InputCommand.replace("tv monitor", "tvmonitor") + if "dining table" in InputCommand: + InputCommand = InputCommand.replace("dining table", "diningtable") + ObjectToFind = [] + ObjectToFind = (InputCommand.split()) + net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg') + + classes = [] + + with open('coco.names', 'r') as f: + classes = f.read().splitlines() + + os.system("luna-send -n 1 -f luna://com.webos.service.camera2/open '{"+'"id": "camera1"}'+"'"+">handle.txt") + f = open("handle.txt") + for i in f: + if "handle" in i: + h = i.replace('"handle":', '') + handle = h.replace(" ","") + + os.system("luna-send -n 1 -f luna://com.webos.service.camera2/setFormat '{"+'"handle": '+str(handle)+',"params":{"width": 1920,"height": 1080,"format": "JPEG", "fps": 30}}'+"'") + os.system("luna-send -n 1 -f luna://com.webos.service.camera2/startPreview '{"+'"handle": '+str(handle)+', "params": {"type":"sharedmemory","source":"0"}}'+"'") + os.system("rm /home/root/ObjectDetection/picture/*") + os.system("luna-send -n 1 -f luna://com.webos.service.camera2/startCapture '{"+'"handle": '+str(handle)+',"params":{"width": 1920,"height": 1080,"format": "JPEG","mode":"MODE_ONESHOT","nimage":6},"path":"/home/root/ObjectDetection/picture"}'+"'") + os.system("ls /home/root/ObjectDetection/picture >imgName.txt") + f = open("imgName.txt") + + val = 0 + for i in f: + val = i + + os.system("luna-send -n 1 -f luna://com.webos.service.camera2/stopPreview '{"+'"handle": '+str(handle)+"}'") + os.system("luna-send -n 1 -f luna://com.webos.service.camera2/close '{"+'"handle": '+str(handle)+"}'") + + if "\n" in val: + val = val.replace("\n", "") + + imgPath = '/home/root/ObjectDetection/picture/'+val + img = cv2.imread(imgPath) + height, width, _ = img.shape + center = (width/2) + + blob = cv2.dnn.blobFromImage(img, 1/255, (416, 416), (0,0,0), swapRB=True, crop=False) + net.setInput(blob) + output_layers_names = net.getUnconnectedOutLayersNames() + layesOutputs = net.forward(output_layers_names) + + boxes = [] + confidences = [] + class_ids = [] + + for output in layesOutputs: + for detection in output: + scores = detection[5:] + class_id = np.argmax(scores) + confidence = scores[class_id] + if confidence > 0.5: + center_x = int(detection[0]*width) + center_y = int(detection[1]*height) + w = int(detection[2]*width) + h = int(detection[3]*height) + + x = int(center_x - w/2) + y = int(center_y - h/2) + + boxes.append([x, y, w, h]) + confidences.append((float(confidence))) + class_ids.append(class_id) + + indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4) + font = cv2.FONT_HERSHEY_PLAIN + colors = np.random.uniform(0, 255, size=(len(boxes), 3)) + + items = [] + position = [] + position2 = [] + + for i in indexes.flatten(): + x, y, w, h = boxes[i] + label = str(classes[class_ids[i]]) + items.append(label) + position.append(x) + position2.append(x+w) + confidence = str(round(confidences[i],2)) + color = colors[i] + cv2.rectangle(img, (x,y), (x+w, y+h), color, 2) + cv2.putText(img, label +" "+ confidence, (x, y+20), font, 2, (255,255,255), 2) + + cv2.imwrite('/media/internal/ObjectDetectionImage/image.jpg',img) + temp = ("luna-send -n 1 luna://com.webos.applicationManager/launch '{"+'"id":"com.app.objectviewer", "params":{"imagePath": "/media/internal/ObjectDetectionImage/image.jpg", "imageLabel": "Test Image"}}'+"'") + temp2 = ("luna-send -n 1 -f luna://com.webos.applicationManager/closeByAppId '{"+'"id":"com.app.objectviewer"}'+"'") + os.system(temp2) + os.system(temp) + ObjectToFindLen = len(ObjectToFind) + ObjectToFindCount = 0 + f=0 + p1 = 0 + p2 = 0 + duplicate_dict = Counter(items) + dis = ((center*15)/100) + for i in ObjectToFind: + ObjectToFindCount = ObjectToFindCount + 1 + if i in classes: + if i in items: + f=1 + TotalObject = duplicate_dict[i] + if TotalObject == 1: + coord = items.index(i) + if((position[coord] <= center) and (position2[coord] >= center)): + talk(i+" found in the center") + if((position[coord] < center) and (position2[coord] <= center)): + if (position2[coord] >= (center - dis)): + talk(i+" found in the left but close to the center") + elif (position[coord] <= dis): + talk(i+" found towards the left most end") + else: + talk(i+" found in the left middle") + if((position[coord] >= center) and (position2[coord] > center)): + if (position[coord] <= (center + dis)): + talk(i+" found in the right but close to the center") + elif (position2[coord] >= (width - dis)): + talk(i+" found towards the right most end") + else: + talk(i+" found in the right middle") + + if TotalObject > 1: + talk("total "+str(TotalObject)+" "+i+" found") + w = 0 + objcountLeftCenter = 0 + objcountLeftMiddle = 0 + objcountLeftEnd = 0 + objcountRightCenter = 0 + objcountRightMiddle = 0 + objcountRightEnd = 0 + objcountCenter = 0 + + for q in items: + if q == i: + if((position[w] <= center) and (position2[w] >= center)): + objcountCenter = objcountCenter + 1 + if((position[w] < center) and (position2[w] <= center)): + if (position2[w] >= (center - dis)): + objcountLeftCenter = objcountLeftCenter + 1 + elif (position[w] <= dis): + objcountLeftEnd = objcountLeftEnd + 1 + else: + objcountLeftMiddle = objcountLeftMiddle + 1 + if((position[w] >= center) and (position2[w] > center)): + if (position[w] <= (center + dis)): + objcountRightCenter = objcountRightCenter + 1 + elif (position2[w] >= (width - dis)): + objcountRightEnd = objcountRightEnd + 1 + else: + objcountRightMiddle = objcountRightMiddle + 1 + w = w+1 + talk("out of which ") + if objcountLeftCenter != 0: + talk(str(objcountLeftCenter)+" "+i+" found in the left but close to the center") + if objcountLeftMiddle != 0: + talk(str(objcountLeftMiddle)+" "+i+" found in the left middle") + if objcountLeftEnd != 0: + talk(str(objcountLeftEnd)+" "+i+" found towards the left most end") + if objcountRightCenter != 0: + talk(str(objcountRightCenter)+" "+i+" found in the right but close to the center") + if objcountRightMiddle != 0: + talk(str(objcountRightMiddle)+" "+i+" found in the right middle") + if objcountRightEnd != 0: + talk(str(objcountRightEnd)+" "+i+" found towards the right most end") + if objcountCenter != 0: + talk(str(objcountCenter)+" "+i+" found in the center") + else: + f=1 + talk(i+" not found") + + else: + if ObjectToFindCount == ObjectToFindLen: + s=0 + for i in ObjectToFind: + s = s+1 + if s <= (len(ObjectToFind) - 1): + val = (i+" "+ObjectToFind[s]) + if val in classes: + if val in items: + f=1 + TotalObject = duplicate_dict[val] + if TotalObject == 1: + coord = items.index(val) + if((position[coord] <= center) and (position2[coord] >= center)): + talk(val+" found in the center") + if((position[coord] < center) and (position2[coord] <= center)): + if (position2[coord] >= (center - dis)): + talk(val+" found in the left but close to the center") + elif (position[coord] <= dis): + talk(val+" found towards the left most end") + else: + talk(val+" found in the left middle") + if((position[coord] >= center) and (position2[coord] > center)): + if (position[coord] <= (center + dis)): + talk(val+" found in the right but close to the center") + elif (position2[coord] >= (width - dis)): + talk(val+" found towards the right most end") + else: + talk(val+" found in the right middle") + + if TotalObject > 1: + talk("total "+str(TotalObject)+" "+val+" found") + w = 0 + objcountLeftCenter = 0 + objcountLeftMiddle = 0 + objcountLeftEnd = 0 + objcountRightCenter = 0 + objcountRightMiddle = 0 + objcountRightEnd = 0 + objcountCenter = 0 + for q in items: + + if q == val: + if((position[w] <= center) and (position2[w] >= center)): + objcountCenter = objcountCenter + 1 + if((position[w] < center) and (position2[w] <= center)): + if (position2[w] >= (center - dis)): + objcountLeftCenter = objcountLeftCenter + 1 + elif (position[w] <= dis): + objcountLeftEnd = objcountLeftEnd + 1 + else: + objcountLeftMiddle = objcountLeftMiddle + 1 + if((position[w] >= center) and (position2[w] > center)): + if (position[w] <= (center + dis)): + objcountRightCenter = objcountRightCenter + 1 + elif (position2[w] >= (width - dis)): + objcountRightEnd = objcountRightEnd + 1 + else: + objcountRightMiddle = objcountRightMiddle + 1 + w = w+1 + talk("out of which ") + if objcountLeftCenter != 0: + talk(str(objcountLeftCenter)+" "+val+" found in the left but close to the center") + if objcountLeftMiddle != 0: + talk(str(objcountLeftMiddle)+" "+val+" found in the left middle") + if objcountLeftEnd != 0: + talk(str(objcountLeftEnd)+" "+val+" found towards the left most end") + if objcountRightCenter != 0: + talk(str(objcountRightCenter)+" "+val+" found in the right but close to the center") + if objcountRightMiddle != 0: + talk(str(objcountRightMiddle)+" "+val+" found in the right middle") + if objcountRightEnd != 0: + talk(str(objcountRightEnd)+" "+val+" found towards the right most end") + if objcountCenter != 0: + talk(str(objcountCenter)+" "+val+" found in the center") + else: + f=1 + talk(val+" not found") + if s == (ObjectToFindLen -1) and f==0: + talk("Sorry no object found") +while True: + run_lg() diff --git a/poc-apps/voice-assistant/CMakeLists.txt b/poc-apps/voice-assistant/CMakeLists.txt new file mode 100644 index 0000000..ebdb3da --- /dev/null +++ b/poc-apps/voice-assistant/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.8.7) +project(voiceui NONE) +set(CMAKE_BUILD_TYPE Debug) + +include(webOS/webOS) + +webos_modules_init(1 6 3) +webos_component(1 0 0) + +set(INSTALL_DIR ${WEBOS_INSTALL_WEBOS_SERVICESDIR}/${CMAKE_PROJECT_NAME}) +#install necessary files to destination directory +install(DIRECTORY . DESTINATION ${INSTALL_DIR} + USE_SOURCE_PERMISSIONS + PATTERN "*~" EXCLUDE + PATTERN "CMake*" EXCLUDE + PATTERN "BUILD*" EXCLUDE + PATTERN "*~" EXCLUDE + PATTERN "*.in" EXCLUDE + PATTERN ".git" EXCLUDE + PATTERN ".gitignore" EXCLUDE + PATTERN ".project" EXCLUDE + PATTERN ".settings" EXCLUDE + PATTERN "files" EXCLUDE + PATTERN "test" EXCLUDE + PATTERN "tempdb" EXCLUDE + PATTERN "*.qmlproject" EXCLUDE + PATTERN "README.md" EXCLUDE) + +#webos_build_daemon() +webos_build_system_bus_files() \ No newline at end of file diff --git a/poc-apps/voice-assistant/README.md b/poc-apps/voice-assistant/README.md new file mode 100644 index 0000000..3f09197 --- /dev/null +++ b/poc-apps/voice-assistant/README.md @@ -0,0 +1,43 @@ +Summary +------- +voice assistant UI for webosose + +Description +----------- +voice assistant UI for webosose + +How to Build on Linux +--------------------- + +## Dependencies + +Below are the tools and libraries (and their minimum versions) required to build sample program: + +* cmake (version required by cmake-modules-webos) + +## Building + + $ cd build-webos + $ source oe-init-build-env + $ bitbake voiceUI + +Copyright and License Information +================================= +Unless otherwise specified, all content, including all source code files and +documentation files in this repository are: + +Copyright (c) 2021 LG Electronics, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 diff --git a/poc-apps/voice-assistant/files/launch/voiceui/voiceui.service b/poc-apps/voice-assistant/files/launch/voiceui/voiceui.service new file mode 100644 index 0000000..bcceacd --- /dev/null +++ b/poc-apps/voice-assistant/files/launch/voiceui/voiceui.service @@ -0,0 +1,29 @@ +# Copyright (c) 2020 LG Electronics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Author(s) : Piyush Asalmol +# Email ID. : piyush.asalmol@lgepartner.com + + [Unit] + Description=default - "%n" + Requires=ls-hubd.service + After=ls-hubd.service + + [Service] + Type=simple + OOMScoreAdjust=-1000 + ExecStart=/usr/bin/run-js-service -k -g -n /usr/palm/services/voiceui + Restart=on-failure \ No newline at end of file diff --git a/poc-apps/voice-assistant/files/systemd/services/voiceui.service b/poc-apps/voice-assistant/files/systemd/services/voiceui.service new file mode 100644 index 0000000..e9812ba --- /dev/null +++ b/poc-apps/voice-assistant/files/systemd/services/voiceui.service @@ -0,0 +1,20 @@ +# @@@LICENSE +# +# Copyright (c) 2021 LG Electronics, Inc. +# +# LICENSE@@@ +# A service which enables apps to execute flow based rules in node-red runtime + +[Unit] +Description=webos - "%n" +Requires=default-webos.target +After=default-webos.target + +[Service] +Type=simple +OOMScoreAdjust=1000 +ExecStart=/usr/bin/run-js-service -k -g -n /usr/palm/services/voiceui +Restart=on-failure + +[Install] +WantedBy=default-webos.target \ No newline at end of file diff --git a/poc-apps/voice-assistant/gstt.json b/poc-apps/voice-assistant/gstt.json new file mode 100644 index 0000000..cb6f696 --- /dev/null +++ b/poc-apps/voice-assistant/gstt.json @@ -0,0 +1,12 @@ +{ + "type": "service_account", + "project_id": "sharad-kumar-12", + "private_key_id": "700af149f3bf3b74975a81910709f5c058d815d2", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDMa51I9W3nFC8L\nVvpq3m6mnPSSLkVxeRnSXDVcIXuEY+vTZJNw0jph3ww2WbSO3NDarVKB0y2GNEen\ntTa+afO9pNk/OUQU7eqHi5d19IwyVDWqazsgmdxqO+VQFsM7tGFhpFM9CA5xlx5z\nQxgBrRGTaZzvdNeK9pG6BRF1JyGP2E35BnVFd3plN9EdSqrA0fFaefU6vUdahIwK\nAm1mpxiWLDEKVb1exlB8WoDO3YbPdfyUTt5xZ/IOBLUGNTrY5v2gaNVIEfYvz/Ym\nGCXOPgieXiGcQGSLhDhkEijTuFQ/cR8dHxib9Fldgp+x9p7zzK9AdpFTfFpKLI9Z\nYSQ2DHFNAgMBAAECggEAOXhz0sQ1bsbs5vpAEvWQFEWCOOrLWTibutlzowBC0pio\n/1iCXzCre0KTE2Cq85M/IIuj653saZOfJZY72tK81O9YqH64Fk5BJnRnU6E6HRgx\nskTNbb/0COXn9VlA8DMpQaNKR5lRPjwnMm6FfEsk6tXBakzmIEpr8+li3BXxSJgd\nreVLQA7rPyvqi8ivoVd/N+RY7BOaHW7pUNW2PorTfbty2sx07EavlvOlMeczg/lF\njj+ErjmMSO1jcZDNzUDzj7kINbFYFG6I9E1ZCKzrMr8KI39Eib87YLJBzL6wDUUY\new0vRWdX+YWeNKTxQG9ZT2eXkKe1D8hobM7lbGIKnQKBgQDuiI20IfOEfMbv0p1z\nCvU4dI1Yw1j9BPlSyKDoaTMUsXvc3rw//7wWJknLLxEzHxopTOIzVLB9nKYqFngW\nhjR71jhiYw3T/7j2A2Jk7bXJzHPW3rp3GMhnFpQ/1fs3CF2EQYU/BPWKa9R6EAoh\nI2HIb0Jjfe3SZxD2tsHhRauiGwKBgQDbY5eY87B8tTmLI13nama2GZpKdQXAdjyH\n1JbYHYUaxO10eIb5AVfXsVGihqh5YZ+X/EGKuntQVtYDdIIPDN5fmc0wmHANfRi+\n7rpKyk1dGZsVOfexRbhCSoJ5cuM2+btWuAI/CW7cDFIvcCqu5aApWIxaEd4XvZK4\nz6YNcUuwtwKBgQCURenfySL4fh+jQAaDVpJsD3c1b+jHDup0nSX4kjwgZtsZxpK6\nLZubIR3J4W4Nmw3GEnahdclnq5JWozTuu2mweM7/yZb06wbsVdo55tGTqTVwoW7D\nTXJ1MkHhFIBeCyMmbRwIumpvKuwfHA1P4BCOaSqZcdP9b/fJhH0sLQStdQKBgQCU\nOEvyjSk+h92RsFo//7XW76M7DKbPKdKpXipvZjpIJKXc25Us5Ahp9CWYLWvjXd5J\n+6Z4SJcJZzF98hfwfRw78Jgdbc++fTbbwEPiRZ1Q+fy5sYkznkvnlZli3gi+WqiD\n5Ru3ZPMd/cEExb5AnWjApk9khs/KsExRShEA4ftmLwKBgQDRzTxSikyfjaHvFh5E\ntW5cL5uKKLX5LAJkDdGyCpSQfWCPXiQ0BYJ8QpxftrTrjRMxwV+0V9Av6g60sMrj\nKP3ppfDGVFf2HFT+KHXvXhS0lvtJXbLVTTq8251fuxVxkUVGqMHwzNYJpEt/g0QX\ndjUbpnlefF/i++HOw5VC8Cy+/Q==\n-----END PRIVATE KEY-----\n", + "client_email": "gsttlg@sharad-kumar-12.iam.gserviceaccount.com", + "client_id": "105953768579189491195", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gsttlg%40sharad-kumar-12.iam.gserviceaccount.com" +} diff --git a/poc-apps/voice-assistant/index.js b/poc-apps/voice-assistant/index.js new file mode 100644 index 0000000..e1b7dcd --- /dev/null +++ b/poc-apps/voice-assistant/index.js @@ -0,0 +1,102 @@ +// Copyright (c) 2021-2022 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +const port = 5500; +_package = require('./package.json'); +const bodyParser = require('body-parser'); +const express = require('express'); +const app = express(); +flag = false; +const fs = require('fs'); +const exec = require('child_process').exec; + +app.use(express.static('public')); +app.use(express.urlencoded()); +app.use(bodyParser.urlencoded({ extended: false })); +app.get('/', function (req, res) { + res.sendFile(__dirname + '/views/index.html'); +}); + +function appRun() { + exec('cd /usr/palm/services/voiceui;export GOOGLE_APPLICATION_CREDENTIALS="/usr/palm/services/voiceui/gstt.json";python3 main.py;', (error, stdout, stderr) => { + if (error) { + console.log(`error: ${error.message}`); + return; + } + if (stderr) { + console.log(`stderr: ${stderr}`); + return; + } + console.log(`stdout: ${stdout}`); + }); + flag = true; +} + + +function appKill() { + exec("pidof python3 main.py", (error, stdout, stderr) => { + var killAppvalue = 'kill ' + stdout; + + exec(killAppvalue, (error, stdout, stderr) => { + if (error) { + console.log(`error: ${error.message}`); + return; + } + if (stderr) { + console.log(`stderr: ${stderr}`); + return; + } + console.log(`stdout: ${stdout}`); + }); + if (error) { + console.log(`error: ${error.message}`); + return; + } + if (stderr) { + console.log(`stderr: ${stderr}`); + return; + } + console.log(`stdout: ${stdout}`); + }); + flag = false; +} + +app.post('/clicked', (req, res) => { + if (flag == false) { + appRun(); + fs.truncate('./public/js/input.txt', 0, function () { + //console.log('clear input file done'); + }); + fs.truncate('./public/js/output.txt', 0, function () { + // console.log('clear output file done'); + }); + fs.truncate('./public/js/status.txt', 0, function () { + //console.log('clear status file done'); + }); + flag = true; + } + else { + appKill(); + fs.truncate('./public/js/status.txt', 0, function () { + //console.log('clear status file done'); + }); + + flag = false; + } + res.sendFile(__dirname + '/views/index.html'); +}); + +app.listen(port, () => console.log(`LG voice assistant listening at http://:${port}`)); diff --git a/poc-apps/voice-assistant/main.py b/poc-apps/voice-assistant/main.py new file mode 100644 index 0000000..6aba977 --- /dev/null +++ b/poc-apps/voice-assistant/main.py @@ -0,0 +1,670 @@ +# Copyright (c) 2021-2022 LG Electronics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import pyaudio +import speech_recognition as sr +import wave +import requests +import datetime +import wikipedia +import pickle +import os +import os.path +import time +from googleapiclient.discovery import build +from google_auth_oauthlib.flow import InstalledAppFlow +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +import httplib2 +import os +from oauth2client import file +from apiclient import discovery +import oauth2client +from oauth2client import client +from oauth2client import tools +from datetime import datetime + +# If modifying these scopes, delete the file token.pickle. +SCOPES = ['https://www.googleapis.com/auth/calendar'] + +def talk(text): + from gtts import gTTS + import os + mytext = text + text_file = open("/usr/palm/services/voiceui/public/js/output.txt", "w") + n = text_file.write(mytext) + text_file.close() + language = 'en' + myobj = gTTS(text=mytext, lang=language, slow=False) + myobj.save("welcome.mp3") + text_file = open("/usr/palm/services/voiceui/public/js/status.txt", "w") + n = text_file.write("Answering...") + text_file.close() + os.system("mpg123 " + "welcome.mp3") + +def take_command(): + """Transcribe the given audio file.""" + + def voicein(): + CHUNK = 515 + FORMAT = pyaudio.paInt16 + CHANNELS = 2 + RATE = 44100 + RECORD_SECONDS = 5 + WAVE_OUTPUT_FILENAME = "output.wav" + + p = pyaudio.PyAudio() + + stream = p.open(format=FORMAT, + channels=CHANNELS, + rate=RATE, + input=True, + frames_per_buffer=CHUNK) + print("* recording") + text_file = open("/usr/palm/services/voiceui/public/js/status.txt", "w") + n = text_file.write("Listening...") + text_file.close() + + frames = [] + + for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): + data = stream.read(CHUNK) + frames.append(data) + + print("* done recording") + text_file = open("/usr/palm/services/voiceui/public/js/status.txt", "w") + n = text_file.write("Processing...") + text_file.close() + + stream.stop_stream() + stream.close() + p.terminate() + + wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') + wf.setnchannels(CHANNELS) + wf.setsampwidth(p.get_sample_size(FORMAT)) + wf.setframerate(RATE) + wf.writeframes(b''.join(frames)) + wf.close() + + from google.cloud import speech + import io + + client = speech.SpeechClient() + speech_file = "/usr/palm/services/voiceui/output.wav" + + with io.open(speech_file, "rb") as audio_file: + content = audio_file.read() + + audio = speech.RecognitionAudio(content=content) + config = speech.RecognitionConfig( + encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16, + sample_rate_hertz=44100, + language_code="en-US", + audio_channel_count=2, + ) + + response = client.recognize(config=config, audio=audio) + + # Each result is for a consecutive portion of the audio. Iterate through + # them to get the transcripts for the entire audio file. + if len(response.results) == 0: + return voicein() + else: + for result in response.results: + # The first alternative is the most likely one for this portion. + print(u"Transcript: {}".format(result.alternatives[0].transcript)) + resultvoice = (format(result.alternatives[0].transcript)) + resultvoice = resultvoice.lower() + if 'lg' in resultvoice: + outputvoice = resultvoice.replace('lg', '') + + text_file = open("/usr/palm/services/voiceui/public/js/input.txt", "w") + n = text_file.write(outputvoice) + text_file.close() + + text_file = open("/usr/palm/services/voiceui/public/js/output.txt", "w") + n = text_file.write("") + text_file.close() + + return outputvoice + else: + return take_command() + + text = voicein() + return text; + + +def take_alter_command(): + """Transcribe the given audio file.""" + + def voicein(): + CHUNK = 515 + FORMAT = pyaudio.paInt16 + CHANNELS = 2 + RATE = 44100 + RECORD_SECONDS = 5 + WAVE_OUTPUT_FILENAME = "output.wav" + + p = pyaudio.PyAudio() + + stream = p.open(format=FORMAT, + channels=CHANNELS, + rate=RATE, + input=True, + frames_per_buffer=CHUNK) + print("* recording") + text_file = open("/usr/palm/services/voiceui/public/js/status.txt", "w") + n = text_file.write("Listening...") + text_file.close() + + frames = [] + + for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): + data = stream.read(CHUNK) + frames.append(data) + + print("* done recording") + text_file = open("/usr/palm/services/voiceui/public/js/status.txt", "w") + n = text_file.write("Processing...") + text_file.close() + + stream.stop_stream() + stream.close() + p.terminate() + + wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') + wf.setnchannels(CHANNELS) + wf.setsampwidth(p.get_sample_size(FORMAT)) + wf.setframerate(RATE) + wf.writeframes(b''.join(frames)) + wf.close() + + from google.cloud import speech + import io + + client = speech.SpeechClient() + speech_file = "/usr/palm/services/voiceui/output.wav" + with io.open(speech_file, "rb") as audio_file: + content = audio_file.read() + + audio = speech.RecognitionAudio(content=content) + config = speech.RecognitionConfig( + encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16, + sample_rate_hertz=44100, + language_code="en-US", + audio_channel_count=2, + ) + + response = client.recognize(config=config, audio=audio) + + # Each result is for a consecutive portion of the audio. Iterate through + # them to get the transcripts for the entire audio file. + + if len(response.results) == 0: + return voicein() + else: + for result in response.results: + # The first alternative is the most likely one for this portion. + + print(u"Transcript: {}".format(result.alternatives[0].transcript)) + + resultvoice = (format(result.alternatives[0].transcript)) + text_file = open("/usr/palm/services/voiceui/public/js/input.txt", "w") + n = text_file.write(resultvoice) + text_file.close() + + return resultvoice + text = voicein() + + return text; + +def run_lg(): + command = take_command() + print(command) + if 'play' in command: + try: + import urllib.request + import re + if 'play' in command: + command = command.replace('play ', '') + talk("playing "+command) + if ' ' in command: + command=command.replace(' ','+') + html = urllib.request.urlopen("https://www.youtube.com/results?search_query="+command) + video_ids = re.findall(r"watch\?v=(\S{11})", html.read().decode()) + searchresult = (video_ids[0]) + temp = ('luna-send -n 1 -f luna://com.webos.service.applicationmanager/launch '+"'"+'{"id":"com.webos.app.enactbrowser", "params": {"target":"https://www.youtube.com/watch?v='+searchresult+'"'+"}}'") + os.system(temp) + except: + talk("This youtube operation is having some issue please try again!") + + elif 'who is' in command: + try: + person = command.replace('who is', '') + temp = ('luna-send -n 1 -f luna://com.webos.service.applicationmanager/launch '+"'"+'{"id":"com.webos.app.enactbrowser", "params": {"target":"https://en.wikipedia.org/wiki/'+person+'"'+"}}'") + os.system(temp) + info = wikipedia.summary(person, 1); + talk(info) + except: + talk("This wiki operation is having some issue please try again!") + + elif 'temperature' in command or 'weather' in command: + try: + def cityValidate(name): + complete_url = base_url + "appid=" + api_key + "&q=" + name + response = requests.get(complete_url) + x = response.json() + return x, complete_url + if 'tell' in command: + command = command.replace('tell','') + if 'how' in command: + command = command.replace('how','') + import requests, json + api_key = "ed5459fa93e76130e7d895742ee345dd" + base_url = "http://api.openweathermap.org/data/2.5/weather?" + temp = 0 + if 'today' in command or 'outside' in command or 'place' in command or 'location' in command: + city_name = 'bengaluru' + complete_url = base_url + "appid=" + api_key + "&q=" + city_name + response = requests.get(complete_url) + x = response.json() + temp = 1 + else: + brk = command.split() + s=0 + temp=0 + t = 0 + for i in brk: + if (s+2) < len(brk): + h=(i+" "+brk[s+1]+" "+brk[s+2]) + x, complete_url = cityValidate(h) + if x["cod"] != "404": + temp = 1 + city_name = h + break + s=s+1 + if temp == 0: + for i in brk: + if (t+1) < len(brk): + h=(i+" "+brk[t+1]) + x, complete_url = cityValidate(h) + if x["cod"] != "404": + temp = 1 + city_name = h + break + t=t+1 + if temp == 0: + for i in brk: + x, complete_url = cityValidate(i) + if x["cod"] != "404": + temp = 1 + city_name = i + break + if temp == 1: + url=("https://www.timeanddate.com/weather/india/"+city_name) + temp = ('luna-send -n 1 -f luna://com.webos.service.applicationmanager/launch '+"'"+'{"id":"com.webos.app.enactbrowser", "params": {"target":"'+city_name+' weather"'+"}}'") + os.system(temp) + response = requests.get(complete_url) + y = x["main"] + current_temperature = y["temp"] + temperature = current_temperature - 273.15 + current_pressure = y["pressure"] + current_humidiy = y["humidity"] + z = x["weather"] + weather_description = z[0]["description"] + temperature = int(temperature) + temperature = str(temperature) + current_pressure = str(current_pressure) + current_humidiy = str(current_humidiy) + weather_description = str(weather_description) + if 'temperature' in command: + talk("Today in "+ city_name +" the temperature is "+temperature+" degree Celsius") + if 'weather' in command: + talk("Today in "+ city_name +" the temperature is "+temperature+" degree Celsius") + talk("with an atmospheric pressure of "+current_pressure+" hPa") + talk("and humidity of "+current_humidiy+" percent with "+weather_description) + + print(" Temperature (in kelvin unit) = " + + str(int(temperature)) + + "\n atmospheric pressure (in hPa unit) = " + + str(current_pressure) + + "\n humidity (in percentage) = " + + str(current_humidiy) + + "\n description = " + + str(weather_description)) + else: + talk("city not found") + except: + talk("This weather operation is having some issue please try again!") + + elif 'time' in command: + try: + time = datetime.now().strftime('%I:%M %p') + print(time) + talk('Current time is ' + time) + except: + talk("This operation is having some issue please try again!") + + elif 'date' in command: + try: + from datetime import date + today = date.today() + today = datetime.now().strftime("%d %B, %Y") + print("Today's date:", today) + talk("Today's date:" + today) + except: + talk("This operation is having some issue please try again!") + + elif 'create' in command and ('calendar' in command or 'event' in command): + def get_credentials(): + """Gets valid user credentials from storage. + + If nothing has been stored, or if the stored credentials are invalid, + the OAuth2 flow is completed to obtain the new credentials. + + Returns: + Credentials, the obtained credential. + """ + try: + import argparse + flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() + except ImportError: + flags = None + + # If modifying these scopes, delete your previously saved credentials + # at ~/.credentials/calendar-python-quickstart.json + SCOPES = 'https://www.googleapis.com/auth/calendar' + CLIENT_SECRET_FILE = 'credentials.json' + APPLICATION_NAME = 'Google Calendar API Python Quickstart' + + home_dir = os.path.expanduser('~') + credential_dir = os.path.join(home_dir, '.credentials') + if not os.path.exists(credential_dir): + os.makedirs(credential_dir) + credential_path = os.path.join(credential_dir, + 'calendar-python-quickstart.json') + + store = oauth2client.file.Storage(credential_path) + credentials = store.get() + if not credentials or credentials.invalid: + flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES) + flow.user_agent = APPLICATION_NAME + if flags: + credentials = tools.run_flow(flow, store, flags) + else: # Needed only for compatibility with Python 2.6 + credentials = tools.run(flow, store) + print('Storing credentials to ' + credential_path) + return credentials + + def create_event(): + try: + import datetime + import time + """Shows basic usage of the Google Calendar API. + + Creates a Google Calendar API service object and outputs a list of the next + 10 events on the user's calendar. + """ + credentials = get_credentials() + http = credentials.authorize(httplib2.Http()) + service = discovery.build('calendar', 'v3', http=http) + + # Refer to the Python quickstart on how to setup the environment: + # https://developers.google.com/google-apps/calendar/quickstart/python + # Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any + # stored credentials. + talk('Sure, what is the title of the event: ') + opt_var = take_alter_command() + title = opt_var + + def start_time(): + talk('start time of event: ') + str1 = take_alter_command() + if 'a.m.' in str1 : + str1 = str1.replace('a.m.', '') + if ' 'in str1: + str1 = str1.replace(' ','') + if len(str1) == 1 or len(str1) == 2: + str1 = (str1 +":00") + if str1[:2] == "12": + start=( "00" + str1[2:]) + else: + start =( str1) + elif 'p.m.' in str1 : + str1 = str1.replace('p.m.', '') + if ' 'in str1: + str1 = str1.replace(' ','') + if len(str1) == 1: + str1 = ("0"+str1 +":00") + if len(str1) == 2: + str1 = (str1 + ":00") + if len(str1) == 4: + str1 = ("0"+str1) + if str1[:2] == "12": + start = (str1) + else: + start = (str(int(str1[:2]) + 12) + str1[2:5]) + else: + talk("Please specify AM or PM in you Start time of Event") + start = start_time() + return start + start = start_time() + + + def end_time(): + talk('end time of event: ') + str1 = take_alter_command() + if 'a.m.' in str1 : + str1 = str1.replace('a.m.', '') + if ' 'in str1: + str1 = str1.replace(' ','') + if len(str1) == 1 or len(str1) == 2: + str1 = (str1 +":00") + if str1[:2] == "12": + end=( "00" + str1[2:]) + else: + end =(str1) + elif 'p.m.' in str1 : + str1 = str1.replace('p.m.', '') + if ' 'in str1: + str1 = str1.replace(' ','') + if len(str1) == 1: + str1 = ("0"+str1 +":00") + if len(str1) == 2: + str1 = (str1 + ":00") + if len(str1) == 4: + str1 = ("0"+str1) + if str1[:2] == "12": + end = ( str1) + else: + end = ( str(int(str1[:2]) + 12) + str1[2:5]) + + else: + talk("Please specify AM or PM in your End time of Event") + end = end_time() + + shr = int(start[0:2]) + smint = int(start[-2:]) + ehr = int(end[0:2]) + emint = int(end[-2:]) + if (ehr < shr or (ehr == shr and emint <= smint )): + talk("End time should always be greater than start time") + end = end_time() + return end + end = end_time() + today = datetime.datetime.today() + stoday = datetime.datetime.strftime(today, "%Y-%m-%d") + startdate = stoday+'T'+start+":00" + enddate = stoday+'T'+end+":00" + print(startdate) + print(enddate) + event = { + 'summary': title, + 'start': { + 'dateTime': startdate, + 'timeZone': 'Asia/Calcutta', + }, + 'end': { + 'dateTime': enddate, + 'timeZone': 'Asia/Calcutta', + }, + 'reminders': { + 'useDefault': False, + 'overrides': [ + {'method': 'email', 'minutes': 24 * 60}, + {'method': 'popup', 'minutes': 10}, + ], + }, + } + + event = service.events().insert(calendarId='primary', body=event).execute() + print('Event created: %s' % (event.get('htmlLink'))) + temp = ('luna-send -n 1 -f luna://com.webos.service.applicationmanager/launch '+"'"+'{"id":"com.webos.app.enactbrowser", "params": {"target":"https://calendar.google.com/'+'"'+"}}'") + os.system(temp) + talk("event created") + except: + talk("This operation is having some issue please try again!") + create_event() + + elif 'list' in command and ('calendar' in command or 'event' in command): + def main(): + try: + """Shows basic usage of the Google Calendar API. + Prints the start and name of the next 10 events on the user's calendar. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists('token.json'): + creds = Credentials.from_authorized_user_file('token.json', SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + 'credentials.json', SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open('token.json', 'w') as token: + token.write(creds.to_json()) + + service = build('calendar', 'v3', credentials=creds) + + # Call the Calendar API + now = datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time + print('Getting the upcoming 10 events') + #events_result = service.events().list(calendarId='primary', timeMin=now,maxResults=10, singleEvents=True,orderBy='startTime').execute() + events_result = service.events().list(calendarId='primary', timeMin=now, + maxResults=10, singleEvents=True, + orderBy='startTime').execute() + events = events_result.get('items', []) + + if not events: + print('No upcoming events found.') + talk("No upcoming events found.") + else: + talk("following are the calendar events") + for event in events: + start = event['start'].get('dateTime', event['start'].get('date')) + str2 = (str(start)) + str2 = str2[-14:-9] + d = datetime.strptime(str2, "%H:%M") + d2 = (d.strftime("%I:%M %p")) + talk(event['summary'] +" at " + str(d2)) + except: + talk("This operation is having some issue please try again!") + + if __name__ == '__main__': + main() + + elif 'delete' in command and ('calendar' in command or 'event' in command): + def main(): + try: + """Shows basic usage of the Google Calendar API. + Prints the start and name of the next 10 events on the user's calendar. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists('token.json'): + creds = Credentials.from_authorized_user_file('token.json', SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + 'credentials.json', SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open('token.json', 'w') as token: + token.write(creds.to_json()) + + service = build('calendar', 'v3', credentials=creds) + + # Call the Calendar API + now = datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time + print('Getting the upcoming 10 events') + #events_result = service.events().list(calendarId='primary', timeMin=now,maxResults=10, singleEvents=True,orderBy='startTime').execute() + events_result = service.events().list(calendarId='primary', timeMin=now, + maxResults=10, singleEvents=True, + orderBy='startTime').execute() + events = events_result.get('items', []) + + if not events: + print('No upcoming events found.') + talk("No upcoming events found.") + else: + talk("following are the calendar events") + for event in events: + start = event['start'].get('dateTime', event['start'].get('date')) + str2 = (str(start)) + str2 = str2[-14:-9] + d = datetime.strptime(str2, "%H:%M") + d2 = (d.strftime("%I:%M %p")) + talk(event['summary'] +" at " + str(d2)) + talk("which one you want to delete") + ename = take_alter_command() + s=0 + h=0 + dtime = d2 + for eventt in events: + s=s+1 + if eventt['summary'] == ename: + foxs = (eventt['id']) + temp = ('luna-send -n 1 -f luna://com.webos.service.applicationmanager/launch '+"'"+'{"id":"com.webos.app.enactbrowser", "params": {"target":"https://calendar.google.com/'+'"'+"}}'") + os.system(temp) + service.events().delete(calendarId='primary', eventId=foxs).execute() + talk(ename +' event at '+ d2 +' deleted') + else: + h=h+1 + if s == h: + talk(ename+ ' event not found') + except: + talk("This operation is having some issue please try again!") + + if __name__ == '__main__': + main() + + else: + talk('Please say again.') + +while True: + run_lg() \ No newline at end of file diff --git a/poc-apps/voice-assistant/package.json b/poc-apps/voice-assistant/package.json new file mode 100644 index 0000000..cf16d71 --- /dev/null +++ b/poc-apps/voice-assistant/package.json @@ -0,0 +1,15 @@ +{ + "name": "voiceui", + "version": "1.0.0", + "description": "voice assistant UI for webosose", + "main": "index.js", + "scripts": { + "test": "node index.js" + }, + "author": "piyush.asalmol", + "license": "Apache 2.0", + "dependencies": { + "express": "^4.17.1", + "fs":"0.0.1-security" + } +} diff --git a/poc-apps/voice-assistant/public/css/lgbtn.PNG b/poc-apps/voice-assistant/public/css/lgbtn.PNG new file mode 100644 index 0000000..6a53e14 Binary files /dev/null and b/poc-apps/voice-assistant/public/css/lgbtn.PNG differ diff --git a/poc-apps/voice-assistant/public/css/lglogo.jpg b/poc-apps/voice-assistant/public/css/lglogo.jpg new file mode 100644 index 0000000..64c5f80 Binary files /dev/null and b/poc-apps/voice-assistant/public/css/lglogo.jpg differ diff --git a/poc-apps/voice-assistant/public/css/style.css b/poc-apps/voice-assistant/public/css/style.css new file mode 100644 index 0000000..3bbd0f6 --- /dev/null +++ b/poc-apps/voice-assistant/public/css/style.css @@ -0,0 +1,125 @@ +* { + box-sizing: border-box; + } + body { + background-image: url("/css/lglogo.jpg"); + background-repeat: no-repeat; + background-position: top; + background-attachment: fixed; + } + input[type=text], + input[type=number], + select, + textarea { + width: 100%; + padding: 12px; + border: 1px solid #ccc; + border-radius: 4px; + resize: vertical; + } + + label { + padding: 12px 12px 12px 0; + display: inline-block; + color: #dc3545; + } + + input[type=button] { + background-color: #4CAF50; + color: white; + padding: 12px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + float: left; + margin-left: 25%; + } + + input[type=reset] { + background-color: #4CAF50; + color: white; + padding: 12px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + float: right; + } + + input[type=button]:hover { + background-color: #45a049; + } + + input[type=reset]:hover { + background-color: #45a049; + } + + .container { + border-radius: 5px; + background-color: transparent; + padding: 200px; + } + + .col-25 { + float: left; + width: 25%; + margin-top: 6px; + } + + .col-75 { + float: left; + width: 75%; + margin-top: 6px; + } + + .row:after { + content: ""; + display: table; + clear: both; + } + + @media screen and (max-width: 600px) { + .col-25, + .col-75, + input[type=reset] { + width: 100%; + margin-top: 0; + } + } + #lgbtn{ + background-image: url("/css/lgbtn.PNG"); + width: 85px; + height:85px; + border-color:transparent; + box-shadow: none; + background-repeat: no-repeat; + } + + #inputTextarea{ + scroll-behavior: none; + background-color: transparent; + border:ridge; + border-color: #dc3545; + text-decoration: white; + font-display: white; + overflow:hidden; + resize: vertical; + height: 150px; + } + #outputTextarea{ + background-color: transparent; + border:ridge; + border-color: #dc3545; + text-decoration: white; + font-display: white; + overflow:hidden; + resize: vertical; + height: 150px; + } + #readstatus{ + background-color: transparent; + border:none;text-align:center; + text-decoration: white; + font-display: white; + width:150px; + height: 45px; + } \ No newline at end of file diff --git a/poc-apps/voice-assistant/public/css/styles.css b/poc-apps/voice-assistant/public/css/styles.css new file mode 100644 index 0000000..436a56a --- /dev/null +++ b/poc-apps/voice-assistant/public/css/styles.css @@ -0,0 +1,232 @@ +body{ + background:#FFFFFF; + font-family: "Lucida Console", "Courier New", monospace; +} +.chat-list { + padding: 0; + font-size: .8rem; +} + +.chat-list li { + margin-bottom: 10px; + overflow: auto; + color: #ffffff; +} + +.chat-list .chat-img { + float: left; + width: 48px; +} + +.chat-list .chat-img img { + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 50px; + width: 100%; +} + +.chat-list .chat-message { + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 10px; + background: #5a99ee; + display: inline-block; + padding: 5px 10px; + position: relative; +} + +.chat-list .chat-message:before { + content: ""; + position: absolute; + top: 15px; + width: 0; + height: 0; +} + +.chat-list .chat-message h5 { + margin: 0 0 5px 0; + font-weight: 600; + line-height: 100%; + font-size: .9rem; +} + +.chat-list .chat-message p { + line-height: 18px; + margin: 0; + padding: 0; +} + +.chat-list .chat-body { + margin-left: 20px; + float: left; + width: 70%; +} + +.chat-list .in .chat-message:before { + left: -12px; + border-bottom: 20px solid transparent; + border-right: 20px solid #5a99ee; +} + +.chat-list .out .chat-img { + float: right; +} + +.chat-list .out .chat-body { + float: right; + margin-right: 20px; + text-align: right; +} + +.chat-list .out .chat-message { + background: #fc6d4c; +} + +.chat-list .out .chat-message:before { + right: -12px; + border-bottom: 20px solid transparent; + border-left: 20px solid #fc6d4c; +} + +.card .card-header:first-child { + -webkit-border-radius: 0.3rem 0.3rem 0 0; + -moz-border-radius: 0.3rem 0.3rem 0 0; + border-radius: 0.3rem 0.3rem 0 0; +} +.card .card-header { + background:grey; + border: 0; + font-size: 1rem; + padding: .06rem 1rem; + position: relative; + font-weight: 600; + color: #ffffff; +} + +.content{ + margin-top:40px; +} + +.lglogo{ + margin-top: -2%; + margin-bottom: -4%; +} +.lglogo img{ + width: 10%; + height: 100%; +} + + .switch { + position: relative; + display: inline-block; + width: 90px; + height: 34px; + } + + .switch input {display:none;} + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #2ab934; + -webkit-transition: .4s; + transition: .4s; + } + + .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + } + + input:checked + .slider { + background-color: #ca2222; + } + + input:focus + .slider { + box-shadow: 0 0 1px #2196F3; + } + + input:checked + .slider:before { + -webkit-transform: translateX(55px); + -ms-transform: translateX(55px); + transform: translateX(55px); + } + + /*------ ADDED CSS ---------*/ + .on + { + display: none; + } + + .on, .off + { + color: white; + position: absolute; + transform: translate(-50%,-50%); + top: 50%; + left: 50%; + font-size: 12px; + font-family: Verdana, sans-serif; + } + + input:checked+ .slider .on + {display: block;} + + input:checked + .slider .off + {display: none;} + + /*--------- END --------*/ + + /* Rounded sliders */ + .slider.round { + border-radius: 34px; + } + + .slider.round:before { + border-radius: 50%;} + +.toggle_button{ + padding-top: 1%; +} +.scrollView{ + width: 100%; + height: 410px; + overflow: scroll; + overflow-x: hidden; +} +.Ibox { + width: 250px; + height: 50px; + padding: 12px; + border: 1px solid #5a99ee; + border-radius: 4px; + resize: vertical; + background-color: #5a99ee; + color: white; + font-size: 15px; + outline: none; +} + +.Obox { + width: 350px; + height: 80px; + overflow:auto; + padding: 12px; + border: none; + border-radius: 4px; + outline: none; + resize: vertical; + background-color: #fc6d4c; + color: white; + font-size: 15px; +} \ No newline at end of file diff --git a/poc-apps/voice-assistant/public/js/data.js b/poc-apps/voice-assistant/public/js/data.js new file mode 100644 index 0000000..41fef13 --- /dev/null +++ b/poc-apps/voice-assistant/public/js/data.js @@ -0,0 +1,52 @@ +function readInput() { + fetch('/js/input.txt') + .then(response => response.text()) + .then(data => { + //console.log(data); + document.getElementById("inputTextarea").value = data; + delete data; + }) + setTimeout(readInput, 500) + return true; +} + +function readOutput() { + fetch('/js/output.txt') + .then(response => response.text()) + .then(data => { + //console.log(data); + document.getElementById("outputTextarea").value = data; + delete data; + }) + setTimeout(readOutput, 500); + return true; +} + +function iconchange(data) { + if (data == "Listening...") { + //document.getElementById("readstatus").value = data; + document.getElementById("statusicon").className = "fa fa-microphone"; + } + if (data == "Answering...") { + //document.getElementById("readstatus").value = data; + document.getElementById("statusicon").className = "fa fa-volume-up"; + } + if (data == "Processing...") { + // document.getElementById("readstatus").value = data; + document.getElementById("statusicon").className = "fa fa-spinner"; + } + + return; +} +function readStatus() { + fetch('/js/status.txt') + .then(response => response.text()) + .then(data => { + //console.log(data); + iconchange(data); + //document.getElementById("readstatus").value = data; + delete data; + }) + setTimeout(readStatus, 500); + return true; +} diff --git a/poc-apps/voice-assistant/public/js/input.txt b/poc-apps/voice-assistant/public/js/input.txt new file mode 100644 index 0000000..f68f26d --- /dev/null +++ b/poc-apps/voice-assistant/public/js/input.txt @@ -0,0 +1 @@ +7 p.m. \ No newline at end of file diff --git a/poc-apps/voice-assistant/public/js/output.txt b/poc-apps/voice-assistant/public/js/output.txt new file mode 100644 index 0000000..ab851b2 --- /dev/null +++ b/poc-apps/voice-assistant/public/js/output.txt @@ -0,0 +1 @@ +event created \ No newline at end of file diff --git a/poc-apps/voice-assistant/public/js/status.txt b/poc-apps/voice-assistant/public/js/status.txt new file mode 100644 index 0000000..570c9cc --- /dev/null +++ b/poc-apps/voice-assistant/public/js/status.txt @@ -0,0 +1 @@ +Listening... \ No newline at end of file diff --git a/poc-apps/voice-assistant/views/female_icon.png b/poc-apps/voice-assistant/views/female_icon.png new file mode 100644 index 0000000..efbfd56 Binary files /dev/null and b/poc-apps/voice-assistant/views/female_icon.png differ diff --git a/poc-apps/voice-assistant/views/index.html b/poc-apps/voice-assistant/views/index.html new file mode 100644 index 0000000..6d76f22 --- /dev/null +++ b/poc-apps/voice-assistant/views/index.html @@ -0,0 +1,96 @@ + + + + + + + + + + + + LG Assistant + + + + +
+
+
+
+
+
+
+
    +
  • +
    + Avtar +
    +
    +
    +
    User
    + + +
    +
    +
  • +
  • +
    + Avtar +
    +
    +
    +
    LG Assistant
    + + +
    +
    +
  • +
+
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+ + + + + + + diff --git a/poc-apps/voice-assistant/views/photo.jpg b/poc-apps/voice-assistant/views/photo.jpg new file mode 100644 index 0000000..8df273e Binary files /dev/null and b/poc-apps/voice-assistant/views/photo.jpg differ diff --git a/poc-apps/voice-assistant/voiceui.bb b/poc-apps/voice-assistant/voiceui.bb new file mode 100644 index 0000000..27edc39 --- /dev/null +++ b/poc-apps/voice-assistant/voiceui.bb @@ -0,0 +1,58 @@ +SUMMARY = "Demo UI for voice assistant" +AUTHOR = "piyush.asalmol" +LICENSE = "CLOSED" + +DEPENDS = "nodejs-native" +RDEPENDS_${PN} = "nodejs nodejs-module-webos-service" + +WEBOS_VERSION = "1.0.0" +PR = "r0" + +# The same restriction as nodejs as nodejs itself added to RDEPENDS_${PN} +COMPATIBLE_MACHINE_armv4 = "(!.*armv4).*" +COMPATIBLE_MACHINE_armv5 = "(!.*armv5).*" +COMPATIBLE_MACHINE_mips64 = "(!.*mips64).*" + +SRC_URI = "file://git \ + file://0001-voiceui-service-start-on-boot.patch" +S = "${WORKDIR}/git" + +inherit webos_component +inherit webos_cmake +inherit webos_system_bus +inherit webos_machine_impl_dep +inherit webos_submissions + +def get_nodejs_arch(d): + target_arch = d.getVar('TRANSLATED_TARGET_ARCH', True) + + if target_arch == "x86-64": + target_arch = "x64" + elif target_arch == "aarch64": + target_arch = "arm64" + elif target_arch == "powerpc": + target_arch = "ppc" + elif target_arch == "powerpc64": + target_arch = "ppc64" + elif (target_arch == "i486" or target_arch == "i586" or target_arch == "i686"): + target_arch = "ia32" + + return target_arch + +NPM_CACHE_DIR ?= "${WORKDIR}/npm_cache" +NPM_REGISTRY ?= "http://registry.npmjs.org/" +NPM_ARCH = "${@get_nodejs_arch(d)}" +NPM_INSTALL_FLAGS ?= "--production --without-ssl --insecure --no-optional --verbose --strip-debug" + +do_compile() { + cd ${S} + # configure cache to be in working directory + ${STAGING_BINDIR_NATIVE}/npm set cache ${NPM_CACHE_DIR} + + # clear local cache prior to each compile + ${STAGING_BINDIR_NATIVE}/npm cache clear --force + + ${STAGING_BINDIR_NATIVE}/npm --registry=${NPM_REGISTRY} --arch=${NPM_ARCH} --target_arch=${NPM_ARCH} ${NPM_INSTALL_FLAGS} install +} +FILES_${PN} += "${webos_servicesdir} ${webos_sysconfdir}" +INSANE_SKIP_${PN} = "already-stripped ldflags file-rdeps arch" \ No newline at end of file diff --git a/poc-apps/voiceui_1.0.0-r0_raspberrypi4.ipk b/poc-apps/voiceui_1.0.0-r0_raspberrypi4.ipk new file mode 100644 index 0000000..72b4619 Binary files /dev/null and b/poc-apps/voiceui_1.0.0-r0_raspberrypi4.ipk differ diff --git a/ref-apps/com.reference.app.calculator/LICENSE b/ref-apps/com.reference.app.calculator/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ref-apps/com.reference.app.calculator/LICENSES/Apache-2.0.txt b/ref-apps/com.reference.app.calculator/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000..22d4c20 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/LICENSES/Apache-2.0.txt @@ -0,0 +1,183 @@ +Apache License +Version 2.0, November 2021 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ref-apps/com.reference.app.calculator/LICENSES/BSD-3-Clause.txt b/ref-apps/com.reference.app.calculator/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..a7cf702 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,26 @@ +Copyright (c) 2021 LG Electronics, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ref-apps/com.reference.app.calculator/LICENSES/MIT.txt b/ref-apps/com.reference.app.calculator/LICENSES/MIT.txt new file mode 100644 index 0000000..61ff1b0 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/LICENSES/MIT.txt @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2021 LG Electronics, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ref-apps/com.reference.app.calculator/README.md b/ref-apps/com.reference.app.calculator/README.md new file mode 100644 index 0000000..e68b20c --- /dev/null +++ b/ref-apps/com.reference.app.calculator/README.md @@ -0,0 +1,237 @@ +## Overview +This tutorial demonstrates the usage of Enact components to create a typical calculator app for webOS OSE. + +The Enact UI theme used: + - Moonstone Theme + +The features offered by the calculator app: + - Basic arithmetic functions + - Basic maths functions + - Logarithmic functions + - Trigonometric functions + - Memory storage functions + +You can use this calculator reference app as follows: + + - Install the app as-is on a webOS OSE target device. + - Update the source code as required and then deploy on a webOS OSE target device. + - Analyze the source code to understand the usage of the different Enact components. + +## Folder Structure + +The Calculator App project should look like this: + +``` +com.reference.app.calculator/ + README.md + .gitignore + node_modules/ + package.json + resources/ + src/ + Actions/ + App/ + App.js + App.less + package.json + Assets/ + components/ + data/ + Reducers/ + Store/ + styles/ + Util/ + views/ + MainPanel.js + index.js + resources/ +``` + +For the project to build, **these files must exist with exact filenames**: + +* `package.json` is the core package manifest for the project +* `src/index.js` is the JavaScript entry point. + +You can delete or rename the other files. + +You can update the `license` entry in `package.json` to match the license of your choice. For more +information on licenses, please see the [npm documentation](https://docs.npmjs.com/files/package.json#license). + +## Available Scripts + +In the project directory, you can run: + +### `npm run serve` + +Builds and serves the app in the development mode.
+Open [http://localhost:8080](http://localhost:8080) to view it in the browser. + +The page will reload if you make edits.
+ +### `npm run pack` and `npm run pack-p` + +Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code included, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. Be sure to avoid shipping or performance testing on development mode builds. + +### `npm run watch` + +Builds the project in development mode and keeps watch over the project directory. Whenever files are changed, added, or deleted, the project will automatically get rebuilt using an active shared cache to speed up the process. This is similar to the `serve` task, but without the http server. + +### `npm run clean` + +Deletes previous build fragments from ./dist. + +### `npm run lint` + +Runs the Enact configuration of Eslint on the project for syntax analysis. + +### `npm run test` and `npm run test-watch` + +These tasks will execute all valid tests (files that end in `-specs.js`) that are within the project directory. The `test` is a standard single execution pass, while `test-watch` will set up a watcher to re-execute tests when files change. + + +## Source Code + +Analyze the source code to get an understanding of the functionalities implemented in the calculator app. +Refer to the snippets provided in this section. + +### Using Enact UI Components +The below code snippet uses the Moonstone theme on the Calculator. + +Panels provide a way to manage the different screens of an app. Here header and calculator components are managed by Panels. +Button and Label are mainly used for UI development. Since Calculator app is the reference app that uses only UI components. + + ```js + import { Panel } from "@enact/moonstone/Panels"; + ... + +
+ +
+ ... + import Label from "@enact/moonstone/LabeledItem"; + import Button from "@enact/moonstone/Button"; + ... + + + ... + + + + ... + ``` +###CSS Styles +The CSS-style grid list is used inside the calculator component for the KeyPad layout. + + ``` css + .keypad { + display: grid; + background-color: rgb(172, 172, 172); + font-family: "digital-clock-font"; + grid-template-columns: repeat(7, 6fr); + grid-template-rows: repeat(9, 6fr); + grid-column-gap: 5px; + grid-row-gap: 10px; + border: 5px solid #f93881; + border-radius: 12px; + padding: 20px; + zoom: 110%; + } + ``` +The result and history use the CSS style for the best appearance like the calculator. + ```css + ... + .history { + .common; + font-size: 2rem; + background-color: rgb(153, 153, 153); + color: whitesmoke; + font-weight: normal; + direction: rtl; + } + .result { + .common; + font-size: 2.5rem; + background-color: seashell; + color: black; + } + ``` +### Math JS library +Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types like numbers, big numbers, complex numbers, fractions, units, and matrices. + +The calculator app uses this library for parsing and evaluating the expressions. + + ```js + import * as mathjs from "mathjs"; + ... + const doEval = (expression) => { + let answer = DISPLAY.ERROR; + try { + answer = mathjs.evaluate(expression); + } catch (err) { + answer = DISPLAY.ERROR; + } + return answer; + }; + ``` +### Validate Syntax +A simple parsing engine checks the parenthesis on user input. The parser takes the user input and returns an error if the input is invalid. + + ```js + const validateSyntax = (history) => { + let leftParen = history.indexOf("("); + let rightParen = history.indexOf(")"); + if ( + (leftParen === -1 && rightParen >= 0) || + (rightParen === -1 && leftParen >= 0) + ) { + console.log("Parenthesss missing -1 !!!!"); + return -1; + } else { + let counter = 0; + let splitArr = history.split(""); + for (let i = 0; i < splitArr.length; i++) { + if (splitArr[i] === "(") { + counter++; + } else if (splitArr[i] === ")") { + counter--; + } + if (counter === -1) { + console.log("Parenthesss missing -2 !!!!"); + return -1; + } + } + if (counter < 0) { + console.log("Parenthesss missing -3 !!!!"); + return -1; + } + } + return 1; + }; + ... + const doEvalUtil = (history) => { + if (validateSyntax(history) === -1) { + return DISPLAY.INVALID; + } else if (history !== undefined && history.length > 0) { + if (history.includes("--")) { + history = history.replace("--", "+"); + } + if (history.includes("%")) { + history = history.replace("%", "/100"); + } + return doEval(history); + } + }; + ``` + +## Installing the App on the Target Device +Go to the app folder and execute the following commands: + +###Package the enact source code. +`enact pack` A dist folder will be created. + +###Package the app to create an IPK. +`ares-package dist` +An IPK named com.reference.app.calculator_1.0.2_all.ipk is created. + +###Install the IPK +`ares-install --device com.reference.app.calculator_1.0.2_all.ipk` \ No newline at end of file diff --git a/ref-apps/com.reference.app.calculator/oss-pkg-info.yaml b/ref-apps/com.reference.app.calculator/oss-pkg-info.yaml new file mode 100644 index 0000000..91ea690 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/oss-pkg-info.yaml @@ -0,0 +1,28 @@ +Open Source Software Package: + - name: Axios + source: git://github.com/axios/axios + license: MIT + - name: Cross Env + source: git://github.com/kentcdodds/cross-env + license: MIT + - name: Enact + source: https://github.com/enactjs/enact + license: Apache-2.0 + - name: Enact-cli + source: https://github.com/enactjs/cli + license: Apache-2.0 + - name: iLib + source: git://github.com/iLib-js/iLib + license: Apache-2.0 + - name: Immer + source: git://github.com/immerjs/immer + license: MIT + - name: Nodemon + source: git://github.com/remy/nodemon + license: MIT + - name: React + source: git://github.com/facebook/react + license: MIT + - name: Query String + source: git://github.com/sindresorhus/query-string + license: MIT diff --git a/ref-apps/com.reference.app.calculator/package-lock.json b/ref-apps/com.reference.app.calculator/package-lock.json new file mode 100644 index 0000000..f6a2f21 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/package-lock.json @@ -0,0 +1,582 @@ +{ + "name": "calculator", + "version": "1.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.17.tgz", + "integrity": "sha512-NCdgJEelPTSh+FEFylhnP1ylq848l1z9t9N0j1Lfbcw0+KXGjsTvUmkxy+voLLXB5SOKMbLLx4jxYliGrYQseA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@enact/core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/core/-/core-3.5.0.tgz", + "integrity": "sha512-k4CJ3KUdXEljgl/y/cj8uT0j//eKfsh1S6CnINjF2C4y/jchxzCSM2QEkwCTqIz86dGkx/N3JYr6cl09wgChFA==", + "requires": { + "classnames": "^2.2.5", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "react-is": "^16.8.3", + "warning": "^4.0.3" + } + }, + "@enact/i18n": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/i18n/-/i18n-3.5.0.tgz", + "integrity": "sha512-SNtMLqQQKUaXk9ZUMDqjhZsV/TL9IdR07nbYIjZ7mS6CFE8Qekf4jzUtC0bkV9uYjaoVP0bS4mj1VnyWm/uxvA==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "xhr": "^2.4.1" + } + }, + "@enact/moonstone": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@enact/moonstone/-/moonstone-3.3.1.tgz", + "integrity": "sha512-ccpFJR3pIJLtIPey/QB4OlNvIwHu98mkTTn7KE2VVL30L7mtiHIDbcjIIcVec31Z70VgzeTmyEyVnauuDVfd3g==", + "requires": { + "@enact/core": "^3.4.9", + "@enact/i18n": "^3.4.9", + "@enact/spotlight": "^3.4.9", + "@enact/ui": "^3.4.9", + "classnames": "^2.2.5", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/spotlight": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/spotlight/-/spotlight-3.5.0.tgz", + "integrity": "sha512-XqvAEOz0emHAQIQ5XL5nec0L9Poex4bfsp1YxoUa0zzNGybcERQg1ggxrTseQLzr+cUilN8weMRunOyDh7mQUQ==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.7.0", + "react-dom": "^16.7.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/ui": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/ui/-/ui-3.5.0.tgz", + "integrity": "sha512-Ln5SBvLKPTvuGarPnBra87lrfdi9wXVw8MMaQQOPmljx1a9tZmZRBHgI2bYZlObz5ltGEfXrpI3nerfqiAN0ww==", + "requires": { + "@enact/core": "^3.5.0", + "classnames": "^2.2.5", + "direction": "^1.0.1", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz", + "integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-redux": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz", + "integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "@types/scheduler": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", + "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, + "complex.js": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", + "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, + "deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" + }, + "direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==" + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==" + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ilib": { + "version": "14.7.0", + "resolved": "https://registry.npmjs.org/ilib/-/ilib-14.7.0.tgz", + "integrity": "sha512-f3AwWy04Pbb3rcIQD/CR40sgQHOpH4LQbB3FiU3mm4wTpYEOWjrxeT1WI6NMp9+dxQ5hnmd8zkuSjJoVlyAlzQ==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "mathjs": { + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.4.4.tgz", + "integrity": "sha512-5EEJXnWOzLDgMHSFyw623nH+MTBZxquWwXtrzTsingOouJJ6UZG2VNO1lwH31IMt9aMno1axO6TYleIP4YSDaQ==", + "requires": { + "@babel/runtime": "^7.14.6", + "complex.js": "^2.0.15", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.1.1", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-redux": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz", + "integrity": "sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/react-redux": "^7.1.16", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", + "requires": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "symbol-observable": "^1.0.4" + } + }, + "redux": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.0.tgz", + "integrity": "sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==" + }, + "redux-logger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", + "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", + "requires": { + "deep-diff": "^0.3.5" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "typed-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", + "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==" + }, + "ua-parser-js": { + "version": "0.7.27", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.27.tgz", + "integrity": "sha512-eXMaRYK2skomGocoX0x9sBXzx5A1ZVQgXfrW4mTc8dT0zS7olEcyfudAzRC5tIIRgLxQ69B6jut3DI+n5hslPA==" + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/ref-apps/com.reference.app.calculator/package.json b/ref-apps/com.reference.app.calculator/package.json new file mode 100644 index 0000000..9f09b9f --- /dev/null +++ b/ref-apps/com.reference.app.calculator/package.json @@ -0,0 +1,48 @@ +{ + "name": "calculator", + "version": "1.0.2", + "description": "A general template for an Enact Moonstone application.", + "author": "", + "main": "src/index.js", + "scripts": { + "serve": "enact serve", + "pack": "enact pack", + "pack-p": "enact pack -p", + "watch": "enact pack --watch", + "clean": "enact clean", + "lint": "enact lint .", + "license": "enact license", + "test": "enact test", + "test-watch": "enact test --watch" + }, + "license": "Apache-2.0", + "private": true, + "repository": "", + "enact": { + "theme": "moonstone" + }, + "eslintConfig": { + "extends": "enact" + }, + "eslintIgnore": [ + "node_modules/*", + "build/*", + "dist/*" + ], + "dependencies": { + "@enact/core": "^3.2.0", + "@enact/i18n": "^3.2.0", + "@enact/moonstone": "^3.2.0", + "@enact/spotlight": "^3.2.0", + "ilib": "^14.4.0", + "mathjs": "^9.4.4", + "prop-types": "^15.7.2", + "react": "^16.7.0", + "react-dom": "^16.7.0", + "react-redux": "^7.2.4", + "redux": "^4.1.0", + "redux-devtools-extension": "^2.13.9", + "redux-logger": "^3.0.6", + "redux-thunk": "^2.3.0" + } +} diff --git a/ref-apps/com.reference.app.calculator/resources/ilibmanifest.json b/ref-apps/com.reference.app.calculator/resources/ilibmanifest.json new file mode 100644 index 0000000..5916671 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/resources/ilibmanifest.json @@ -0,0 +1,3 @@ +{ + "files": [] +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.calculator/src/Actions/CalcActions.js b/ref-apps/com.reference.app.calculator/src/Actions/CalcActions.js new file mode 100644 index 0000000..2ae0646 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/Actions/CalcActions.js @@ -0,0 +1,79 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Types } from "./Types"; +import { doEvalUtil, isValidResult } from "../Util/Utils"; +import { Memory } from "../data/OperFunc"; + +const doReset = () => { + return { + type: Types.RESET, + }; +}; +const doBackSpace = (history) => { + return { + type: Types.BACKSPACE, + history: history, + }; +}; +const setResult = (result) => { + return { + type: Types.RESULT, + result: result, + }; +}; +const setHistory = (history, isResult) => { + return { + type: Types.HISTORY, + history: history, + isResult: isResult, + }; +}; +const setMemory = (memory) => { + return { + type: Types.MEMORY, + memory: memory, + }; +}; + +const evaluate = (result) => { + return { + type: Types.EVALUATE, + result: result, + }; +}; + +const doMemoryOp = (memory, value) => (disptach) => { + if (value === Memory.MR) { + disptach(setResult(memory)); + } else if (value === Memory.MC) { + disptach(setMemory(memory)); + } else { + let result = doEvalUtil(memory) + ""; + if (isValidResult(result) === true) { + disptach(setMemory(result)); + disptach(setResult(result)); + } + } +}; + +const doEvaluate = (history) => (disptach) => { + let result = doEvalUtil(history) + ""; + disptach(evaluate(result)); + return result; +}; + +export { doReset, doBackSpace, setHistory, doEvaluate, doMemoryOp }; diff --git a/ref-apps/com.reference.app.calculator/src/Actions/Types.js b/ref-apps/com.reference.app.calculator/src/Actions/Types.js new file mode 100644 index 0000000..3868e6a --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/Actions/Types.js @@ -0,0 +1,24 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +export const Types = { + RESULT: "RESULT", + HISTORY: "HISTORY", + EVALUATE: "EVALUATE", + RESET: "RESET", + BACKSPACE: "BACKSPACE", + MEMORY: "MEMORY", +}; diff --git a/ref-apps/com.reference.app.calculator/src/App/App.js b/ref-apps/com.reference.app.calculator/src/App/App.js new file mode 100644 index 0000000..f3748d1 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/App/App.js @@ -0,0 +1,34 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import kind from "@enact/core/kind"; +import MoonstoneDecorator from "@enact/moonstone/MoonstoneDecorator"; +import React from "react"; +import MainPanel from "../views/MainPanel"; +import css from "./App.module.less"; + +const App = kind({ + name: "App", + + styles: { + css, + className: "app", + }, + + render: (props) => , +}); + +export default MoonstoneDecorator(App); diff --git a/ref-apps/com.reference.app.calculator/src/App/App.module.less b/ref-apps/com.reference.app.calculator/src/App/App.module.less new file mode 100644 index 0000000..c989bbf --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/App/App.module.less @@ -0,0 +1,19 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.app { + background-color: black; +} diff --git a/ref-apps/com.reference.app.calculator/src/App/package.json b/ref-apps/com.reference.app.calculator/src/App/package.json new file mode 100644 index 0000000..add0ba2 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/App/package.json @@ -0,0 +1,3 @@ +{ + "main": "App.js" +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.calculator/src/Reducers/calcReducer.js b/ref-apps/com.reference.app.calculator/src/Reducers/calcReducer.js new file mode 100644 index 0000000..4f7a789 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/Reducers/calcReducer.js @@ -0,0 +1,101 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Types } from "../Actions/Types"; + +const initialState = { + expression: [], + history: "", + result: "", + memory: "", + isMemory: false, + isHistory: false, + isCalculated: true, +}; + +const calcReducer = (state = initialState, action) => { + switch (action.type) { + case Types.RESULT: { + return { + ...state, + result: action.result, + }; + } + case Types.EVALUATE: { + return { + ...state, + result: action.result, + isHistory: false, + isCalculated: true, + }; + } + case Types.RESET: + return { + ...state, + expression: [], + history: "", + result: "", + isHistory: false, + isCalculated: true, + }; + case Types.BACKSPACE: { + let expression = state.expression; + if (expression && expression.length > 0) { + expression.pop(); + } + return { + ...state, + expression: expression, + history: expression.join(""), + isHistory: true, + }; + } + case Types.HISTORY: { + let hist = []; + if ( + state.history && + state.history.length > 0 && + state.isCalculated === false + ) { + hist = state.expression; + } + if (action.isResult === true) { + hist.push(state.result); + } + hist.push(action.history); + return { + ...state, + expression: hist, + history: hist.join(""), + isHistory: true, + isCalculated: state.isCalculated === true ? false : state.isCalculated, + }; + } + case Types.MEMORY: { + let mem = action.memory; + let isMem = mem === "" ? false : mem && mem.length > 0 ? true : false; + return { + ...state, + memory: mem && mem.length > 0 ? mem : "", + isMemory: isMem, + }; + } + default: + return state; + } +}; + +export default calcReducer; diff --git a/ref-apps/com.reference.app.calculator/src/Reducers/index.js b/ref-apps/com.reference.app.calculator/src/Reducers/index.js new file mode 100644 index 0000000..67aca53 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/Reducers/index.js @@ -0,0 +1,22 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { combineReducers } from "redux"; +import calcReducer from "./calcReducer"; +const rootReducer = combineReducers({ + calc: calcReducer, +}); +export default rootReducer; diff --git a/ref-apps/com.reference.app.calculator/src/Store/ConfigureStore.js b/ref-apps/com.reference.app.calculator/src/Store/ConfigureStore.js new file mode 100644 index 0000000..2c43280 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/Store/ConfigureStore.js @@ -0,0 +1,35 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { applyMiddleware, createStore } from "redux"; +import thunk from "redux-thunk"; +import logger from "redux-logger"; +import { composeWithDevTools } from "redux-devtools-extension"; +import rootReducer from "../Reducers"; + +const ConfigureStore = () => { + const middleware = [thunk]; + if (process.env.NODE_ENV !== "production") { + middleware.push(logger); + } + const middlewareEnhancer = applyMiddleware(...middleware); + const store = createStore( + rootReducer, + composeWithDevTools(middlewareEnhancer) + ); + return store; +}; +export default ConfigureStore; diff --git a/ref-apps/com.reference.app.calculator/src/Util/Utils.js b/ref-apps/com.reference.app.calculator/src/Util/Utils.js new file mode 100644 index 0000000..5f9aa88 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/Util/Utils.js @@ -0,0 +1,122 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { OperFunc, Memory } from "../data/OperFunc"; +import { DISPLAY } from "../data/Display"; +import * as mathjs from "mathjs"; + +const isArthimetic = (value) => { + let operators = OperFunc.PLUS + OperFunc.MINUS + OperFunc.MUL + OperFunc.DIV; + console.log(operators); + if (operators.indexOf(value) !== -1) { + return true; + } + return false; +}; + +const isNumbers = (value) => { + let special = "0123456789"; + if (special.indexOf(value) !== -1) { + return true; + } + return false; +}; + +const isMemoryOp = (value) => { + let memory = Memory.MC + Memory.MP + Memory.MM + Memory.MR + Memory.MS; + if (memory.indexOf(value) !== -1) { + return true; + } + return false; +}; + +const isValidResult = (result) => { + let ret = true; + if ( + result !== undefined && + result !== null && + result.indexOf("undefined") === -1 + ) { + for (const status in DISPLAY) { + if (result.indexOf(DISPLAY[status]) !== -1) { + ret = false; + } + } + } + return ret; +}; +const validateSyntax = (history) => { + let leftParen = history.indexOf("("); + let rightParen = history.indexOf(")"); + if ( + (leftParen === -1 && rightParen >= 0) || + (rightParen === -1 && leftParen >= 0) + ) { + console.log("Parenthesss missing -1 !!!!"); + return -1; + } else { + let counter = 0; + let splitArr = history.split(""); + for (let i = 0; i < splitArr.length; i++) { + if (splitArr[i] === "(") { + counter++; + } else if (splitArr[i] === ")") { + counter--; + } + + if (counter === -1) { + console.log("Parenthesss missing -2 !!!!"); + return -1; + } + } + if (counter < 0) { + console.log("Parenthesss missing -3 !!!!"); + return -1; + } + } + return 1; +}; +const doEval = (expression) => { + let answer = DISPLAY.ERROR; + try { + answer = mathjs.evaluate(expression); + } catch (err) { + answer = DISPLAY.ERROR; + } + return answer; +}; + +const doEvalUtil = (history) => { + if (validateSyntax(history) === -1) { + return DISPLAY.INVALID; + } else if (history !== undefined && history.length > 0) { + if (history.includes("--")) { + history = history.replace("--", "+"); + } + if (history.includes("%")) { + history = history.replace("%", "/100"); + } + return doEval(history); + } +}; +export { + validateSyntax, + isValidResult, + isArthimetic, + isMemoryOp, + isNumbers, + doEvalUtil, +}; diff --git a/ref-apps/com.reference.app.calculator/src/components/Calculator.js b/ref-apps/com.reference.app.calculator/src/components/Calculator.js new file mode 100644 index 0000000..2f91158 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/components/Calculator.js @@ -0,0 +1,155 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import KeyPad from "./KeyPad"; +import { OperFunc, Memory } from "../data/OperFunc"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import css from "./Calculator.module.less"; + +import { + isValidResult, + isArthimetic, + isMemoryOp, + isNumbers, +} from "../Util/Utils"; + +import { + doReset, + doBackSpace, + setHistory, + doEvaluate, + doMemoryOp, +} from "../Actions/CalcActions"; + +function Calculator({ + history, + memory, + isHistory, + isCalculated, + isMemory, + result, + updateHistory, + doMemory, + reset, + backSpace, + evaluate, +}) { + const handleMemory = (value) => { + let memOp = result; + if (value === Memory.MS) { + if (result.length > 0) { + doMemory(result, memOp, value); + } + } else if (isMemory === true) { + if (value === Memory.MP) { + memOp = memory + "+" + result; + } else if (value === Memory.MM) { + memOp = memory + "-" + result; + } else if (value === Memory.MR) { + memOp = memory; + } else if (value === Memory.MC) { + memOp = ""; + } + doMemory(memOp, value); + } + }; + const onClickHandler = (value) => { + if (value === OperFunc.EQUAL) { + if (history.length > 0) { + evaluate(history); + } + } else if (value === OperFunc.RESET) { + reset(); + } else if (value === OperFunc.BACK) { + if (isHistory === true) { + backSpace(history); + } else { + reset(); + } + } else if (isMemoryOp(value)) { + handleMemory(value); + } else { + let isResult = false; + if ( + isHistory === false && + isCalculated === true && + isValidResult(result) === true && + isArthimetic(value) === true && + isNumbers(value) === false + ) { + isResult = true; + } + if (isContainDot(value) !== true) { + updateHistory(value, isResult); + } + } + }; + + function isContainDot(dot) { + if (history.includes(dot) && dot === OperFunc.DOT) { + return true; + } + return false; + } + + return ( +
+ +
+ ); +} + +Calculator.propTypes = { + history: PropTypes.string, + memory: PropTypes.string, + isHistory: PropTypes.bool, + isCalculated: PropTypes.bool, + isMemory: PropTypes.bool, + result: PropTypes.string, + updateHistory: PropTypes.func, + doMemory: PropTypes.func, + reset: PropTypes.func, + backSpace: PropTypes.func, + evaluate: PropTypes.func, +}; + +const mapStateToProps = ({ calc }) => { + return { + memory: calc.memory, + history: calc.history, + result: calc.result, + isCalculated: calc.isCalculated, + isHistory: calc.isHistory, + isMemory: calc.isMemory, + }; +}; +const mapDispatchToState = (dispatch) => { + return { + reset: () => dispatch(doReset()), + updateHistory: (value, isResult) => dispatch(setHistory(value, isResult)), + backSpace: (history) => dispatch(doBackSpace(history)), + evaluate: (history) => dispatch(doEvaluate(history)), + doMemory: (memory, value) => dispatch(doMemoryOp(memory, value)), + }; +}; + +export default connect(mapStateToProps, mapDispatchToState)(Calculator); diff --git a/ref-apps/com.reference.app.calculator/src/components/Calculator.module.less b/ref-apps/com.reference.app.calculator/src/components/Calculator.module.less new file mode 100644 index 0000000..1dcd9c4 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/components/Calculator.module.less @@ -0,0 +1,71 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.keypad { + display: grid; + background-color: rgb(172, 172, 172); + font-family: "digital-clock-font"; + grid-template-columns: repeat(7, 6fr); + grid-template-rows: repeat(9, 6fr); + grid-column-gap: 5px; + grid-row-gap: 10px; + border: 5px solid #f93881; + border-radius: 12px; + padding: 20px; + zoom: 110%; +} +.calculator { + margin: 30px auto; + width: 90%; + height: 90%; +} + +.common { + border: 3px solid #f93881; + text-align: right; + border-style: solid; + border-radius: 12px; + grid-column: span 7; + padding-top: 12px; + font-family: "digital-clock-font"; +} + +.hrule { + grid-column: span 7; + grid-row: 1fr; + background-color: rgb(172, 172, 172); + border: solid rgb(172, 172, 172); + border-style: solid; + height: 1px; + width: 100%; + justify-content: center; +} + +.history { + .common; + font-size: 2rem; + background-color: rgb(153, 153, 153); + color: whitesmoke; + font-weight: normal; + direction: rtl; +} + +.result { + .common; + font-size: 2.5rem; + background-color: seashell; + color: black; +} diff --git a/ref-apps/com.reference.app.calculator/src/components/Header.js b/ref-apps/com.reference.app.calculator/src/components/Header.js new file mode 100644 index 0000000..39e8cb8 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/components/Header.js @@ -0,0 +1,39 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import IconButton from "@enact/moonstone/IconButton"; + +export default function Header({ onClose }) { + return ( +
+ onClose()}> + closex + +
+ ); +} diff --git a/ref-apps/com.reference.app.calculator/src/components/KeyPad.js b/ref-apps/com.reference.app.calculator/src/components/KeyPad.js new file mode 100644 index 0000000..3659e55 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/components/KeyPad.js @@ -0,0 +1,180 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import Button from "@enact/moonstone/Button"; +import React from "react"; +import { OperFunc, Numbers, Memory } from "../data/OperFunc"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import Label from "@enact/moonstone/LabeledItem"; +import css from "./Calculator.module.less"; + +function KeyPad({ result, history, onClickHandler, isMemory, isHistory }) { + return ( +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +} +KeyPad.propTypes = { + history: PropTypes.string, + result: PropTypes.string, +}; + +const mapStateToProps = ({ calc }) => { + return { + history: calc.history, + result: calc.result, + }; +}; +export default connect(mapStateToProps, null)(React.memo(KeyPad)); diff --git a/ref-apps/com.reference.app.calculator/src/components/README.md b/ref-apps/com.reference.app.calculator/src/components/README.md new file mode 100644 index 0000000..b1a7853 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/components/README.md @@ -0,0 +1 @@ +Reusable components for your application go here \ No newline at end of file diff --git a/ref-apps/com.reference.app.calculator/src/data/Display.js b/ref-apps/com.reference.app.calculator/src/data/Display.js new file mode 100644 index 0000000..a3f65f3 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/data/Display.js @@ -0,0 +1,25 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +const DISPLAY = { + INVALID: "INVALID", + ERROR: "ERROR", + INFINITY: "INFINITY", + NAN: "NaN", + MATHERROR: "MATHERROR", +}; + +export { DISPLAY }; diff --git a/ref-apps/com.reference.app.calculator/src/data/OperFunc.js b/ref-apps/com.reference.app.calculator/src/data/OperFunc.js new file mode 100644 index 0000000..442206c --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/data/OperFunc.js @@ -0,0 +1,66 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +const OperFunc = { + ONEBYX: "1/", + XSQ: "^2", + TENX: "10^", + FACT: "!", + PERC: "%", + LOG: "log", + LOG10: "log10", + PI: "PI", + EXP: "exp", + POW: "^", + SQRT: "sqrt", + SIN: "sin", + ASIN: "asin", + COS: "cos", + ACOS: "acos", + TAN: "tan", + ATAN: "atan", + MINUS: "-", + DIV: "/", + MUL: "*", + DOT: ".", + EQUAL: "=", + PLUS: "+", + RESET: "AC", + BACK: "CE", + LEFTBRACK: "(", + RIGHTBRACK: ")", +}; +const Numbers = { + ZERO: "0", + ONE: "1", + TWO: "2", + THREE: "3", + FOUR: "4", + FIVE: "5", + SIX: "6", + SEVEN: "7", + EIGHT: "8", + NINE: "9", +}; +const Memory = { + MC: "MC", + MR: "MR", + MS: "MS", + MP: "MP", + MM: "MM", +}; + +export { OperFunc, Numbers, Memory }; diff --git a/ref-apps/com.reference.app.calculator/src/index.js b/ref-apps/com.reference.app.calculator/src/index.js new file mode 100644 index 0000000..4c750cf --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/index.js @@ -0,0 +1,36 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import { render } from "react-dom"; +import App from "./App"; +import configureStore from "./Store/ConfigureStore"; +import { Provider } from "react-redux"; + +const store = configureStore(); + +const appElement = ( + + + +); + +// In a browser environment, render instead of exporting +if (typeof window !== "undefined") { + render(appElement, document.getElementById("root")); +} + +export default appElement; diff --git a/ref-apps/com.reference.app.calculator/src/styles/App.css b/ref-apps/com.reference.app.calculator/src/styles/App.css new file mode 100644 index 0000000..32e4fba --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/styles/App.css @@ -0,0 +1,22 @@ +/* Copyright (c) 2021 LG Electronics, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + SPDX-License-Identifier: Apache-2.0 */ + + +.panel { + background: lightgray; + display: block; + overflow: auto; +} diff --git a/ref-apps/com.reference.app.calculator/src/views/MainPanel.js b/ref-apps/com.reference.app.calculator/src/views/MainPanel.js new file mode 100644 index 0000000..ed2d4ac --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/views/MainPanel.js @@ -0,0 +1,34 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import Calculator from "../components/Calculator"; +import { Panel } from "@enact/moonstone/Panels"; +import Header from "../components/Header"; +const handleClose = () => { + if (typeof window !== "undefined") { + window.close(); + } +}; +const MainPanel = (props) => { + return ( + +
+ +
+ ); +}; + +export default MainPanel; diff --git a/ref-apps/com.reference.app.calculator/src/views/README.md b/ref-apps/com.reference.app.calculator/src/views/README.md new file mode 100644 index 0000000..e18ab3d --- /dev/null +++ b/ref-apps/com.reference.app.calculator/src/views/README.md @@ -0,0 +1 @@ +Composite components that make up a distinct view go here \ No newline at end of file diff --git a/ref-apps/com.reference.app.calculator/webos-meta/appinfo.json b/ref-apps/com.reference.app.calculator/webos-meta/appinfo.json new file mode 100644 index 0000000..cb529c3 --- /dev/null +++ b/ref-apps/com.reference.app.calculator/webos-meta/appinfo.json @@ -0,0 +1,16 @@ +{ + "id": "com.reference.app.calculator", + "version": "1.0.2", + "vendor": "LGE", + "type": "web", + "main": "index.html", + "title": "Calculator", + "icon": "icon.png", + "transparent": true, + "requiredPermissions": [ + "mediapipeline.resourcemanagement", + "mediapipeline.operation", + "session.management", + "sleep.management" + ] +} diff --git a/ref-apps/com.reference.app.calculator/webos-meta/icon-large.png b/ref-apps/com.reference.app.calculator/webos-meta/icon-large.png new file mode 100644 index 0000000..0b5fc45 Binary files /dev/null and b/ref-apps/com.reference.app.calculator/webos-meta/icon-large.png differ diff --git a/ref-apps/com.reference.app.calculator/webos-meta/icon-mini.png b/ref-apps/com.reference.app.calculator/webos-meta/icon-mini.png new file mode 100644 index 0000000..c3afff2 Binary files /dev/null and b/ref-apps/com.reference.app.calculator/webos-meta/icon-mini.png differ diff --git a/ref-apps/com.reference.app.calculator/webos-meta/icon.png b/ref-apps/com.reference.app.calculator/webos-meta/icon.png new file mode 100644 index 0000000..6ade4c0 Binary files /dev/null and b/ref-apps/com.reference.app.calculator/webos-meta/icon.png differ diff --git a/ref-apps/com.reference.app.calculator/webos-meta/index.html b/ref-apps/com.reference.app.calculator/webos-meta/index.html new file mode 100644 index 0000000..482d83a --- /dev/null +++ b/ref-apps/com.reference.app.calculator/webos-meta/index.html @@ -0,0 +1,17 @@ + + + + + + + Calculator + + + +
+ + + diff --git a/ref-apps/com.reference.app.familyeventplanner/LICENSE b/ref-apps/com.reference.app.familyeventplanner/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ref-apps/com.reference.app.familyeventplanner/LICENSES/Apache-2.0.txt b/ref-apps/com.reference.app.familyeventplanner/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000..22d4c20 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/LICENSES/Apache-2.0.txt @@ -0,0 +1,183 @@ +Apache License +Version 2.0, November 2021 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ref-apps/com.reference.app.familyeventplanner/LICENSES/BSD-3-Clause.txt b/ref-apps/com.reference.app.familyeventplanner/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..a7cf702 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,26 @@ +Copyright (c) 2021 LG Electronics, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ref-apps/com.reference.app.familyeventplanner/LICENSES/MIT.txt b/ref-apps/com.reference.app.familyeventplanner/LICENSES/MIT.txt new file mode 100644 index 0000000..61ff1b0 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/LICENSES/MIT.txt @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2021 LG Electronics, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ref-apps/com.reference.app.familyeventplanner/README.md b/ref-apps/com.reference.app.familyeventplanner/README.md new file mode 100644 index 0000000..bff2686 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/README.md @@ -0,0 +1,356 @@ +##Overview +This tutorial demonstrates the usage of Enact components to create a typical Calendar app for webOS OSE. +The enact UI theme used: + - Moonstone Theme +The features offered by the calendar app: + - Monthly calendar display + - Create a new event with a specific Date + - Highlighting the current date to the user + - Display the list of events when the user selected on the same date + - Multiple event creation on the same date + +You can use this calculator reference app as follows: + - Install the app as-is on a webOS OSE target device. + - Update the source code as required and then deploy on a webOS OSE target device. + - Analyze the source code to understand the usage of the different Enact components. + +## Folder Structure + +After creation, your project should look like this: + +``` +com.reference.app.familyeventplanner/ + assets/ + Licences/ + node_modules/ + resources/ + src/ + actions/ + App/ + App.js + App.module.less + package.json + components/ + reducers/ + services/ + store/ + styles/ + views/ + index.js + webos-meta/ + appinfo.json + icon-large.png + icon-mini.png + icon.png + index.html + LICENSE + npm-shrinkwrap.json + oss-pkg-info.yaml + package-lock.json + package.json + README.md +``` + +For the project to build, **these files must exist with exact filenames**: + +* `package.json` is the core package manifest for the project +* `src/index.js` is the JavaScript entry point. + +You can delete or rename the other files. + +You can update the `license` entry in `package.json` to match the license of your choice. For more +information on licenses, please see the [npm documentation](https://docs.npmjs.com/files/package.json#license). + +## Available Scripts + +In the project directory, you can run: + +### `npm run serve` + +Builds and serves the app in the development mode.
+Open [http://localhost:8080](http://localhost:8080) to view it in the browser. + +The page will reload if you make edits.
+ +### `npm run pack` and `npm run pack-p` + +Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code included, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. Be sure to avoid shipping or performance testing on development mode builds. + +### `npm run watch` + +Builds the project in development mode and keeps watch over the project directory. Whenever files are changed, added, or deleted, the project will automatically get rebuilt using an active shared cache to speed up the process. This is similar to the `serve` task, but without the http server. + +### `npm run clean` + +Deletes previous build fragments from ./dist. + +### `npm run lint` + +Runs the Enact configuration of Eslint on the project for syntax analysis. + +### `npm run test` and `npm run test-watch` + +These tasks will execute all valid tests (files that end in `-specs.js`) that are within the project directory. The `test` is a standard single execution pass, while `test-watch` will set up a watcher to re-execute tests when files change. + +## Source Code +The source code is available at XXXXXXXXXXXXXXXX. + +Analyze the source code to get an understanding of the functionalities implemented in the calendar app. Refer to the snippets provided in this section. + +Note: Clone/download this code on your local development system. + +###Using Enact UI Components + Dialog: A modal dialog component, ready to use in Moonstone applications. Here we use it for creating events and displaying event lists to the user. + Scroller: This scroll bar is visible when many events are present in the list. + Notification: This triggers the user to set the event name and description if its missed. +```js +import Dialog from '@enact/moonstone/Dialog'; +import Scroller from '@enact/moonstone/Scroller'; +import Notification from '@enact/moonstone/Notification'; +... + { + this.onDailogClose(); + }} + showCloseButton + title={formatedDate} + titleBelow={this.state.dailogSubTitle} + > + +
+ { + this.state.dailogType === 'form' ? + : + + } +
+
+
+ + {this.state.notificationMsg} + +``` +###Luna Service Usage +The application uses the "com.webos.service.db" luna service for finding, creating, and deleting events on devices. The API calls are as follows: + + putKind - Creates the DB for the application if it does not exist on the system. + Registers a kind with the database. + Kinds define the owner, and the indexes for a JSON data object. + Indexes can be composed of single or multiple properties. + When you create your index, be aware that queries can only return results that are indexed, and are contiguously ordered. + + put - Stores JSON data objects of a particular Kind into the database. + The method will: + Assign an ID field to each object, if it was not set. + Return the ID and rev for each stored object. + find - Returns a set of objects that match the query specified in the query parameter. + del - Deletes JSON data objects from the database. + +```js +import LS2Request from '@enact/webos/LS2Request'; + +export const dbServices = { + createKind : (cb) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'putKind', + parameters: { + 'id':'com.reference.app.service.familyeventplanner:4', + 'owner':'com.reference.app.familyeventplanner', + 'indexes':[ + {'name':'year', 'props':[{'name':'year'}]}, + {'name':'month', 'props':[{'name':'month'}]}, + {'name':'date', 'props':[{'name':'date'}]}, + {'name':'fullDate', 'props':[{'name':'year'}, {'name':'month'}, {'name':'date'}]} + ] + }, +... + }); + }, + putData : (obj, cb) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'put', + parameters: { + 'objects':[ + obj + ] + }, +... + }); + }, + findData : (queryObj, cb) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'find', + parameters: { + 'query': queryObj + }, +... + }); + }, + deleteEvent: (id) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'del', + parameters: { + 'ids' : [id] + }, +... + }); + } +}; +``` +###Custom Components Used in the App +####Calendar Header +This component populates the UI with the year, month selection by using dropdown and arrows. +````js +import Icon from '@enact/moonstone/Icon'; +import Dropdown from '@enact/moonstone/Dropdown'; +import EditableIntegerPicker from '@enact/moonstone/EditableIntegerPicker'; + +const CalendarHeader = (props) => { + return ( + + + { + props.prevMonth(); + }} + >arrowsmallleft + + + { + props.onSelectChange(d); + }} + > + {props.months} + + + {' '} + { + props.onYearChange(d); + }} + width="small" + /> + + + { + props.nextMonth(); + }} + >arrowsmallright + + + ); +}; + +export default CalendarHeader; +``` +####Days +Displays the days of the selected month along with an event list that was created for the specific date in order. +if more events are present on the day, The "Click to view more" option will be displayed to the user +Highlights the current day to the user. +```js +import LabeledIcon from '@enact/moonstone/LabeledIcon'; + +const Days = (props) => { + let blanks = []; + for (let i = 0; i < props.firstDayOfMonth(); i++) { + blanks.push( + {''} + + ); + } + let daysInMonth = []; + const getEvents = (d) => { + let events = null; + if (props.monthsData[props.month][d.toString()]) { + let daysData = props.monthsData[props.month][d.toString()]; + if (daysData.length > 3) { + events = []; + events[0] =
{daysData[0].title}
; + events[1] =
{daysData[1].title}
; + events[2] = Click to view more; + } else { + events = daysData.map((ev, i) => { + return ( +
{ev.title}
+ ); + } ); + } + } + + return events; + }; + ... +}; +``` +###Utility - Find Query & Filter Data + findQuery - This is a utility method that is helpful during event list preparation and deletion of events. + filterData - Prepares the data for the current year/month and date, and filters the data to display for the specific month. +```js +export const generateQuery = { + findQuery : (obj) => { + let query = { + 'from':_kind, + 'where':[] + }; + for (let key in obj) { + query['where'].push({ + 'prop' : key, + 'op': '=', + 'val': obj[key] + }); + } + return query; + }, + filterData : (arr) => { + let filteredData = {}; + + for (let i = 0; i < arr.length; i++) { +... + let temp = {}; + temp['title'] = arr[i]['event']['title']; + temp['description'] = arr[i]['event']['description']; + temp['image'] = arr[i]['image']; + + filteredData[arr[i].year][arr[i].month][arr[i].date].push(temp); + } + + return filteredData; + } +``` +## Installing the App on the Target Device +Go to the app folder and execute the following commands: + +###Package the enact source code. +`enact pack` A dist folder will be created. + +###Package the app to create an IPK. +`ares-package dist` +An IPK named com.reference.app.familyeventplanner_1.0.2_all.ipk is created. + +###Install the IPK +`ares-install --device com.reference.app.familyeventplanner_1.0.2_all.ipk` \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/assets/mock/deviceList.json b/ref-apps/com.reference.app.familyeventplanner/assets/mock/deviceList.json new file mode 100644 index 0000000..f488fc8 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/assets/mock/deviceList.json @@ -0,0 +1,47 @@ +{ + "pluginList": [ + { + "deviceList": [ + { + "name": "Ultra", + "imageCount": 0, + "uri": "msc://B5E0-BBB2", + "videoCount": 1, + "available": true, + "icon": "", + "audioCount": 34, + "description": "Ultra" + }, + { + "name": "Cruzer Blade", + "imageCount": 33, + "uri": "msc://D94A-BE4D", + "videoCount": 7, + "available": true, + "icon": "", + "audioCount": 35, + "description": "Cruzer Blade" + } + ], + "active": true, + "uri": "msc" + }, + { + "deviceList": [ + { + "name": "Media", + "imageCount": 0, + "uri": "msc://Media/Multimedia", + "videoCount": 1, + "available": true, + "icon": "", + "audioCount": 34, + "description": "Media" + } + ], + "active": true, + "uri": "storage" + } + ], + "returnValue": true + } \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/oss-pkg-info.yaml b/ref-apps/com.reference.app.familyeventplanner/oss-pkg-info.yaml new file mode 100644 index 0000000..91ea690 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/oss-pkg-info.yaml @@ -0,0 +1,28 @@ +Open Source Software Package: + - name: Axios + source: git://github.com/axios/axios + license: MIT + - name: Cross Env + source: git://github.com/kentcdodds/cross-env + license: MIT + - name: Enact + source: https://github.com/enactjs/enact + license: Apache-2.0 + - name: Enact-cli + source: https://github.com/enactjs/cli + license: Apache-2.0 + - name: iLib + source: git://github.com/iLib-js/iLib + license: Apache-2.0 + - name: Immer + source: git://github.com/immerjs/immer + license: MIT + - name: Nodemon + source: git://github.com/remy/nodemon + license: MIT + - name: React + source: git://github.com/facebook/react + license: MIT + - name: Query String + source: git://github.com/sindresorhus/query-string + license: MIT diff --git a/ref-apps/com.reference.app.familyeventplanner/package-lock.json b/ref-apps/com.reference.app.familyeventplanner/package-lock.json new file mode 100644 index 0000000..74c1069 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/package-lock.json @@ -0,0 +1,533 @@ +{ + "name": "familyeventplanner", + "version": "1.0.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.17.tgz", + "integrity": "sha512-NCdgJEelPTSh+FEFylhnP1ylq848l1z9t9N0j1Lfbcw0+KXGjsTvUmkxy+voLLXB5SOKMbLLx4jxYliGrYQseA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@enact/core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/core/-/core-3.5.0.tgz", + "integrity": "sha512-k4CJ3KUdXEljgl/y/cj8uT0j//eKfsh1S6CnINjF2C4y/jchxzCSM2QEkwCTqIz86dGkx/N3JYr6cl09wgChFA==", + "requires": { + "classnames": "^2.2.5", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "react-is": "^16.8.3", + "warning": "^4.0.3" + } + }, + "@enact/i18n": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/i18n/-/i18n-3.5.0.tgz", + "integrity": "sha512-SNtMLqQQKUaXk9ZUMDqjhZsV/TL9IdR07nbYIjZ7mS6CFE8Qekf4jzUtC0bkV9uYjaoVP0bS4mj1VnyWm/uxvA==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "xhr": "^2.4.1" + } + }, + "@enact/moonstone": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@enact/moonstone/-/moonstone-3.3.1.tgz", + "integrity": "sha512-ccpFJR3pIJLtIPey/QB4OlNvIwHu98mkTTn7KE2VVL30L7mtiHIDbcjIIcVec31Z70VgzeTmyEyVnauuDVfd3g==", + "requires": { + "@enact/core": "^3.4.9", + "@enact/i18n": "^3.4.9", + "@enact/spotlight": "^3.4.9", + "@enact/ui": "^3.4.9", + "classnames": "^2.2.5", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/spotlight": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/spotlight/-/spotlight-3.5.0.tgz", + "integrity": "sha512-XqvAEOz0emHAQIQ5XL5nec0L9Poex4bfsp1YxoUa0zzNGybcERQg1ggxrTseQLzr+cUilN8weMRunOyDh7mQUQ==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.7.0", + "react-dom": "^16.7.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/ui": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/ui/-/ui-3.5.0.tgz", + "integrity": "sha512-Ln5SBvLKPTvuGarPnBra87lrfdi9wXVw8MMaQQOPmljx1a9tZmZRBHgI2bYZlObz5ltGEfXrpI3nerfqiAN0ww==", + "requires": { + "@enact/core": "^3.5.0", + "classnames": "^2.2.5", + "direction": "^1.0.1", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/webos": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/webos/-/webos-3.5.0.tgz", + "integrity": "sha512-E/XXyMOnjSsDvvk7r0VRNXUO1gkyPwWOCo5TUWDTZ8fYImJHfBO5okTwHw9rVK2v32sc8wSMEr8VcIf9BCwHTQ==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "react": "^16.8.0", + "react-dom": "^16.8.0" + } + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz", + "integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-redux": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz", + "integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "@types/scheduler": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", + "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" + }, + "deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" + }, + "direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==" + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ilib": { + "version": "14.7.0", + "resolved": "https://registry.npmjs.org/ilib/-/ilib-14.7.0.tgz", + "integrity": "sha512-f3AwWy04Pbb3rcIQD/CR40sgQHOpH4LQbB3FiU3mm4wTpYEOWjrxeT1WI6NMp9+dxQ5hnmd8zkuSjJoVlyAlzQ==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-redux": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.3.tgz", + "integrity": "sha512-ZhAmQ1lrK+Pyi0ZXNMUZuYxYAZd59wFuVDGUt536kSGdD0ya9Q7BfsE95E3TsFLE3kOSFp5m6G5qbatE+Ic1+w==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/react-redux": "^7.1.16", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", + "requires": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "symbol-observable": "^1.0.4" + } + }, + "redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==" + }, + "redux-logger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", + "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", + "requires": { + "deep-diff": "^0.3.5" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "ua-parser-js": { + "version": "0.7.24", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz", + "integrity": "sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==" + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/ref-apps/com.reference.app.familyeventplanner/package.json b/ref-apps/com.reference.app.familyeventplanner/package.json new file mode 100644 index 0000000..1728594 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/package.json @@ -0,0 +1,49 @@ +{ + "name": "familyeventplanner", + "version": "1.0.2", + "description": "A general template for an Enact Moonstone application.", + "author": "", + "main": "src/index.js", + "scripts": { + "serve": "enact serve", + "pack": "enact pack", + "pack-p": "enact pack -p", + "watch": "enact pack --watch", + "clean": "enact clean", + "lint": "enact lint .", + "license": "enact license", + "test": "enact test", + "test-watch": "enact test --watch" + }, + "license": "Apache-2.0", + "private": true, + "repository": "", + "enact": { + "theme": "moonstone" + }, + "eslintConfig": { + "extends": "enact" + }, + "eslintIgnore": [ + "node_modules/*", + "build/*", + "dist/*" + ], + "dependencies": { + "@enact/core": "^3.2.5", + "@enact/i18n": "^3.2.5", + "@enact/moonstone": "^3.2.6", + "@enact/spotlight": "^3.2.5", + "@enact/ui": "^3.2.5", + "@enact/webos": "^3.5.0", + "ilib": "^14.2.0", + "moment": "^2.29.1", + "prop-types": "^15.6.2", + "react": "^16.7.0", + "react-dom": "^16.14.0", + "react-redux": "^7.2.3", + "redux-devtools-extension": "^2.13.8", + "redux-logger": "^3.0.6", + "redux-thunk": "^2.3.0" + } +} diff --git a/ref-apps/com.reference.app.familyeventplanner/resources/ilibmanifest.json b/ref-apps/com.reference.app.familyeventplanner/resources/ilibmanifest.json new file mode 100644 index 0000000..5916671 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/resources/ilibmanifest.json @@ -0,0 +1,3 @@ +{ + "files": [] +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/src/App/App.js b/ref-apps/com.reference.app.familyeventplanner/src/App/App.js new file mode 100644 index 0000000..9b6e32c --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/App/App.js @@ -0,0 +1,39 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import kind from '@enact/core/kind'; +import MoonstoneDecorator from '@enact/moonstone/MoonstoneDecorator'; + +import MainPanel from '../views/MainPanel'; + +import css from './App.module.less'; + +const App = kind({ + name: 'App', + + styles: { + css, + className: 'app' + }, + + render: (props) => ( +
+ +
+ ) +}); + +export default MoonstoneDecorator(App); diff --git a/ref-apps/com.reference.app.familyeventplanner/src/App/App.module.less b/ref-apps/com.reference.app.familyeventplanner/src/App/App.module.less new file mode 100644 index 0000000..b308f87 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/App/App.module.less @@ -0,0 +1,19 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.app { + // styles can be put here +} diff --git a/ref-apps/com.reference.app.familyeventplanner/src/App/package.json b/ref-apps/com.reference.app.familyeventplanner/src/App/package.json new file mode 100644 index 0000000..add0ba2 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/App/package.json @@ -0,0 +1,3 @@ +{ + "main": "App.js" +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/src/actions/deviceActions.js b/ref-apps/com.reference.app.familyeventplanner/src/actions/deviceActions.js new file mode 100644 index 0000000..db848f0 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/actions/deviceActions.js @@ -0,0 +1,68 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {types} from './types'; +import LS2Request from '@enact/webos/LS2Request'; +// import mockDeviceList from '../../assets/mock/deviceList.json'; + +const getDeviceListRequest = () => { + return { + type: types.FETCH_DEVICE_LIST_REQUEST + }; +}; + +const setDeviceListSuccess = (deviceList) => { + return { + type: types.FETCH_DEVICE_LIST_SUCCESS, + payload: deviceList + }; +}; + +const setDeviceListError = (errMessage) => { + return { + type: types.FETCH_DEVICE_LIST_ERROR, + payload: errMessage + }; +}; + +const getDeviceList = ({subscribe}) => (dispatch) => { + // if (typeof window === 'object' && !window.PalmSystem) { + // dispatch(setDeviceListSuccess(mockDeviceList.pluginList)); + // return{}; + // } + dispatch(getDeviceListRequest()); + return new LS2Request().send({ + service: 'luna://com.webos.service.mediaindexer/', + method: 'getDeviceList', + parameters: { + subscribe: subscribe + }, + onSuccess: (res) => { + dispatch(setDeviceListSuccess(res.pluginList)); + }, + onFailure: (err) => { + dispatch(setDeviceListError(err.errorText)); + } + }); +}; + + +export { + getDeviceList, + getDeviceListRequest, + setDeviceListSuccess, + setDeviceListError +}; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/actions/types.js b/ref-apps/com.reference.app.familyeventplanner/src/actions/types.js new file mode 100644 index 0000000..f3ff6e4 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/actions/types.js @@ -0,0 +1,24 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +export const types = { + FETCH_DEVICE_LIST_REQUEST: 'FETCH_DEVICE_LIST_REQUEST', + FETCH_DEVICE_LIST_SUCCESS: 'FETCH_DEVICE_LIST_SUCCESS', + FETCH_DEVICE_LIST_ERROR: 'FETCH_DEVICE_LIST_ERROR', + FETCH_IMAGE_LIST_REQUEST: 'FETCH_IMAGE_LIST_REQUEST', + FETCH_IMAGE_LIST_SUCCESS: 'FETCH_IMAGE_LIST_SUCCESS', + FETCH_IMAGE_LIST_ERROR: 'FETCH_IMAGE_LIST_ERROR' +}; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Calendar.js b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Calendar.js new file mode 100644 index 0000000..5d923dd --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Calendar.js @@ -0,0 +1,354 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {Component} from 'react'; +import moment from 'moment'; +import Dialog from '@enact/moonstone/Dialog'; +import Scroller from '@enact/moonstone/Scroller'; +import Notification from '@enact/moonstone/Notification'; + +import './calendar.css'; +import {dbServices} from '../../services/dbServices'; +import {generateQuery} from '../../services/generateQuery'; +import CreateForm from '../CreateForm/CreateForm'; +import Days from './Days'; +import CalendarHeader from './CalendarHeader'; +import EventsList from '../EventsList/EventsList'; +import Button from '@enact/moonstone/Button'; +import Header from "./Header"; +const _kind = 'com.reference.app.service.familyeventplanner:4'; + +export default class Calendar extends Component { + state = { + dateContext: moment(), + today: moment(), + selectedDay: null, + dailog : false, + dailogSubTitle: null, + monthsData : [], + notification: false, + notificationMsg: '', + dailogType: 'form' + }; + + constructor (props) { + super(props); + this.width = props.width || '100%'; + this.style = props.style || {}; + this.style.width = '100%'; // add this + } + + componentDidMount () { + dbServices.createKind((resp) => { + console.log('Response in callback :: ', resp); + if (resp.returnValue) { + this.getDataFromDb(); + } + }); + } + + getDataFromDb = () => { + let month = this.months.indexOf(this.month()) + 1; + let query = generateQuery.findQuery({ + 'year': this.year(), + 'month': month.toString() + }); + console.log('Query here :: ', query); + dbServices.findData(query, (res) => { + console.log('Find data response :: ', res); + if (res.returnValue) { + let monthsData = generateQuery.filterData(res.results); + console.log('This month data here :: ', monthsData); + this.setState({monthsData}); + } + }); + }; + + + weekdays = moment.weekdays(); // ["Sunday", "Monday", "Tuesday", "Wednessday", "Thursday", "Friday", "Saturday"] + weekdaysShort = moment.weekdaysShort(); // ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + months = moment.months(); + + year = () => { + return this.state.dateContext.format('Y'); + }; + month = () => { + return this.state.dateContext.format('MMMM'); + }; + daysInMonth = () => { + return this.state.dateContext.daysInMonth(); + }; + currentDate = () => { + return this.state.dateContext.get('date'); + }; + currentDay = () => { + return this.state.dateContext.format('D'); + }; + + firstDayOfMonth = () => { + let dateContext = this.state.dateContext; + let firstDay = moment(dateContext).startOf('month').format('d'); // Day of week 0...1..5...6 + return firstDay; + }; + + setDateContext = (dateContext) => { + this.setState({ + dateContext: dateContext + }, () => { + this.getDataFromDb(); + }); + }; + + setMonth = (month) => { + let monthNo = this.months.indexOf(month); + let dateContext = Object.assign({}, this.state.dateContext); + dateContext = moment(dateContext).set('month', monthNo); + this.setDateContext(dateContext); + }; + + nextMonth = () => { + let dateContext = Object.assign({}, this.state.dateContext); + dateContext = moment(dateContext).add(1, 'month'); + this.setDateContext(dateContext); + this.props.onNextMonth && this.props.onNextMonth(); + }; + + prevMonth = () => { + let dateContext = Object.assign({}, this.state.dateContext); + dateContext = moment(dateContext).subtract(1, 'month'); + this.setDateContext(dateContext); + this.props.onPrevMonth && this.props.onPrevMonth(); + }; + + onSelectChange = (data) => { + this.setMonth(data.data); + this.props.onMonthChange && this.props.onMonthChange(); + }; + + SelectList = (props) => { + let popup = props.data.map((data) => { + return ( + + ); + }); + + return ( +
+ {popup} +
+ ); + }; + + setYear = (year) => { + let dateContext = Object.assign({}, this.state.dateContext); + dateContext = moment(dateContext).set('year', year); + this.setDateContext(dateContext); + }; + + onYearChange = (e) => { + this.setYear(e.value); + }; + + onDayClick = (e, day) => { + let currentMonth = this.months.indexOf(this.month()) + 1; + let currentYear = this.year().toString(); + let currentDate = day.toString(); + // console.log("Selected date data :: ", this.state.monthsData[currentYear][currentMonth.toString()][currentDate]); + // let selectedDayData = this.state.monthsData[currentYear][currentMonth.toString()][currentDate] + // let dailogSubTitle = selectedDayData ? "Events of the Day" : "Add new event" + let dailogSubTitle = 'Add new event'; + if (this.state.monthsData[currentYear] && this.state.monthsData[currentYear][currentMonth.toString()] && this.state.monthsData[currentYear][currentMonth.toString()][currentDate]) { + console.log('Data exists'); + this.setState({ + dailogType: 'list' + }); + dailogSubTitle = 'Events of the Day'; + } + this.setState({ + selectedDay: day, + dailog: true, + dailogSubTitle: dailogSubTitle + }); + this.props.onDayClick && this.props.onDayClick(e, day); + }; + + onDailogClose = () => { + this.setState({ + dailog: false, + dailogType: 'form' + }); + }; + + postObj = (obj) => { + obj['_kind'] = _kind; + dbServices.putData(obj, (res) => { + if (res.returnValue) { + this.getDataFromDb(); + } + }); + this.setState({ + dailog: false + }); + }; + + triggerNotification = (msg) => { + let self = this; + this.setState({ + notification: true, + notificationMsg: msg.message + }, () => { + setTimeout(() => { + self.setState({ + notification: false, + notificationMsg: '' + }); + }, 3000); + }); + }; + + deleteEvent = (event, date, month, year) => { + this.onDailogClose(); + let query = generateQuery.findQuery({ + 'year': year, + 'month': month.toString(), + 'date': date + }); + console.log('Delete Event Query here :: ', query); + dbServices.findData(query, (res) => { + console.log('Find data response :: ', res); + let ev = ''; + for (let i in res.results) { + console.log(res.results[i]); + if (res.results[i].event.title === event.title && res.results[i].event.description === event.description) { + ev = res.results[i]._id; + } + } + console.log(ev); + dbServices.deleteEvent(ev); + this.getDataFromDb(); + }); + }; + + createNewEvent = () => { + this.setState({ + dailogType: 'form', + dailog: true + }); + }; + + + render () { + // Map the weekdays i.e Sun, Mon, Tue etc as + let weekdays = this.weekdaysShort.map((day) => { + return ( + {day} + ); + }); + + let formatedDate = this.state.selectedDay ? this.state.selectedDay + '-' + this.month() + '-' + this.year() : this.currentDate() + '-' + this.month() + '-' + this.year(); + let currentSelectedDate = new Date(formatedDate); + let currentMonth = this.months.indexOf(this.month()) + 1; + let currentYear = this.year().toString(); + let currentDate = this.state.selectedDay && this.state.selectedDay.toString(); + return ( + <> + +
+
+ + + + + + + {weekdays} + + + +
+ + { + this.onDailogClose(); + }} + showCloseButton + title={formatedDate} + titleBelow={this.state.dailogSubTitle} + > + +
+ { + this.state.dailogType === 'form' ? + : + + } +
+
+
+ + {this.state.notificationMsg} + +
+ + + ); + } +} diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/CalendarHeader.js b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/CalendarHeader.js new file mode 100644 index 0000000..3cbe4a5 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/CalendarHeader.js @@ -0,0 +1,65 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import Icon from '@enact/moonstone/Icon'; +import Dropdown from '@enact/moonstone/Dropdown'; +import EditableIntegerPicker from '@enact/moonstone/EditableIntegerPicker'; + +const CalendarHeader = (props) => { + return ( + + + { + props.prevMonth(); + }} + >arrowsmallleft + + + { + props.onSelectChange(d); + }} + > + {props.months} + + + {' '} + { + props.onYearChange(d); + }} + width="small" + /> + + + { + props.nextMonth(); + }} + >arrowsmallright + + + ); +}; + +export default CalendarHeader; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Days.js b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Days.js new file mode 100644 index 0000000..d1261b0 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Days.js @@ -0,0 +1,107 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import LabeledIcon from '@enact/moonstone/LabeledIcon'; + +const Days = (props) => { + let blanks = []; + for (let i = 0; i < props.firstDayOfMonth(); i++) { + blanks.push( + {''} + + ); + } + let daysInMonth = []; + const getEvents = (d) => { + let events = null; + if (props.monthsData[props.month][d.toString()]) { + let daysData = props.monthsData[props.month][d.toString()]; + if (daysData.length > 3) { + events = []; + events[0] =
{daysData[0].title}
; + events[1] =
{daysData[1].title}
; + events[2] = Click to view more; + } else { + events = daysData.map((ev, i) => { + return ( +
{ev.title}
+ ); + } ); + } + } + + return events; + }; + let currentDate = props.currentDay(); + for (let d = 1; d <= props.daysInMonth(); d++) { + let className = (d === parseInt(currentDate)) ? ' day current-day' : 'day'; + if (props.monthsData && props.monthsData[props.month]) { + daysInMonth.push( + { + props.onDayClick(e, d); + }} className={className} + > + {d} + {getEvents(d)} + + ); + } else { + daysInMonth.push( + { + props.onDayClick(e, d); + }} className={className} + > + {d} + + ); + } + } + let totalSlots = [...blanks, ...daysInMonth]; + let rows = []; + let cells = []; + + totalSlots.forEach((row, i) => { + if ((i % 7) !== 0 || i === 0) { + cells.push(row); + } else { + let insertRow = cells.slice(); + rows.push(insertRow); + cells = []; + cells.push(row); + } + if (i === totalSlots.length - 1) { + let insertRow = cells.slice(); + rows.push(insertRow); + } + }); + + let trElems = rows.map((d, i) => { + return ( + + {d} + + ); + }); + return ( + <> + {trElems} + + ); +}; + +export default Days; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Header.js b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Header.js new file mode 100644 index 0000000..ceb1d37 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/Header.js @@ -0,0 +1,44 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import IconButton from "@enact/moonstone/IconButton"; + +export default function Header({ onClose }) { + onClose = () => { + if (typeof window !== "undefined") { + window.close(); + } + }; + return ( +
+ onClose()}> + closex + +
+ ); +} diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/calendar.css b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/calendar.css new file mode 100644 index 0000000..75b1d2a --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/Calendar/calendar.css @@ -0,0 +1,215 @@ +/* Copyright (c) 2021 LG Electronics, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + SPDX-License-Identifier: Apache-2.0 */ + +.calendar-container, +.calendar, +.calendar-header, +td, +tr { + padding: 0; + margin: 0; +} + +td { + width: 14% !important; +} + +.calendar-container { + border: 0.08333rem solid skyblue; + margin: 2%; +} + +.calendar { + background-color: white; + color: #00000091; + border-spacing: 0; + border-collapse: collapse; + width: 100%; +} + +.calendar-header { + border-bottom: 1px dashed #f93881; + font-size: 1.6em; + background: #383838; +} + +.calendar-header td { + border-spacing: 0; + padding-left: 5px; +} + +.day { + border: 1px solid #f93881; +} + +.day, +.empty-slot, +.week-day { + padding: 5px; + position: relative; + text-align: center; + height: 40px; +} + +.day-text { + top: 5px; + right: 5px; + /* position: absolute; */ +} + +.week-day { + font-size: 0.8em; +} + +.day { + font-size: 0.8em; + cursor: pointer; + width: 24px; + height: 24px; + line-height: 24px; + width: 14%; +} + +.current-day { + background-color: #f93881; + border-radius: 2%; + color: white; +} +.selected-day { + background-color: yellow; + border-radius: 2%; + color: black; +} + +.month-popup { + position: absolute; + padding: 5px; + background: white; + border: 2px solid #f93881; +} + +.month-popup div { + padding: 0.3em; +} + +.nav-content { + text-align: center; +} + +.nav-month { + text-align: right; +} + +.week-day-nav { + height: 70px; + background: #f93881; + color: #ffffff; +} + +.dates-row { + height: 130px; + width: 100%; +} + +.editor-year { + max-width: 3.6em; +} + +.label-month, +.label-year { + font-size: 0.6em; +} + +.label-month { + cursor: pointer; +} + +.event-element { + background: #f93881; + font-size: 18px; + height: 20px; + width: 250px; + color: #ffffff; + padding: 0px !important; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 2px; +} + +.current-day .event-element { + background: #ffffff; + color: #f93881; +} + +.more-event-element { + background: #383838 !important; + font-size: 18px; + height: 20px; + width: 100%; + color: #ffffff; + margin: 0px !important; + padding: 0px !important; +} + +.more-event-element > label { + font-size: 18px; +} + +.more-event-element > div > div { + padding: 0px !important; + font-size: 30px; +} + +.more-event-icon { + margin: 0px; + font-size: 1rem; + width: 0px; + height: 0px; +} + +.event-input { + margin: 15px 0px; +} +.event-input Input { + font-size: 30px !important; +} + +.event-input-internal { + width: 50%; + padding: 20px; + margin: 15px; + font-size: 10px !important; +} + +.photo-list { + height: 170px; + padding: 20px; +} + +.create-new-btn { + right: 50px; + position: absolute; + margin-top: 5px; + bottom: 15px; +} +.dialog-popup { + height: 80%; + width: 80%; + position: relative; + margin: 0 auto; + background-color: rgba(228, 231, 233, 6.95) !important; +} diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/CreateForm/CreateForm.js b/ref-apps/com.reference.app.familyeventplanner/src/components/CreateForm/CreateForm.js new file mode 100644 index 0000000..b040309 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/CreateForm/CreateForm.js @@ -0,0 +1,149 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {useEffect, useState} from 'react'; +import {connect} from 'react-redux'; +import moment from 'moment'; +import Button from '@enact/moonstone/Button'; +import DatePicker from '@enact/moonstone/DatePicker'; +import Input from '@enact/moonstone/Input'; +import ImageItem from '@enact/ui/ImageItem'; +import {getDeviceList} from '../../actions/deviceActions'; +import './createForm.css'; +const CreateForm = ({getListDevice, imageList, deviceList, currentSelectedDate, postObj, triggerNotification}) => { + + useEffect(() => { + getListDevice(); + }, []); + + const [datePickerVal, setdatePickerVal] = useState(currentSelectedDate); + const [eventTitle, seteventTitle] = useState(null); + const [eventDesc, seteventDesc] = useState(null); + const [pickedImage, setpickedImage] = useState(null); + let deviceInfo = []; + + const getDevices = () => { + let list = []; + list.push('Default Images'); + deviceInfo.push( { + 'name': 'Default Images', + 'uri': 'DefaultImages' + }); + deviceList.map((device) => { + if (device.deviceList.length > 0) { + device.deviceList.map((deviceList) => { + deviceInfo.push(deviceList); + list.push(deviceList.name); + }); + } + }); + return list; + }; + let devices = getDevices(); + + const onDatePickerChange = (date) => { + setdatePickerVal(date.value); + }; + const onEventTitleChange = (title) => { + if(title !== undefined && title.value !== undefined) { + if(title.value.length <=50) { + seteventTitle(title.value); + } else { + triggerNotification({message: 'Title - Maximum characters reached '}); + } + } + }; + const onEventDescChange = (desc) => { + seteventDesc(desc.value); + }; + const onSelectImage = (index) => { + if (pickedImage !== null) { + imageList.results[pickedImage].selected = false; + if (pickedImage === index) { + setpickedImage(null); + } + else { + setpickedImage(index); + imageList.results[index].selected = true; + } + } else { + setpickedImage(index); + imageList.results[index].selected = true; + } + }; + + const onButtonSubmit = () => { + if (eventTitle && eventTitle !== '') { + if (eventDesc && eventDesc !== '') { + let month = moment(datePickerVal).month() + 1; + let obj = {}; + obj['year'] = moment(datePickerVal).year().toString(); + obj['month'] = month.toString(); + obj['date'] = moment(datePickerVal).date().toString(); + obj['event'] = { + 'title' : eventTitle, + 'description' : eventDesc + }; + if (pickedImage !== null) { + obj['image'] = imageList.results[pickedImage]; + } else { + obj['image'] = {}; + } + console.log(obj); + postObj(obj); + } else { + triggerNotification({message: 'Event Description is Mandatory'}); + } + } else { + triggerNotification({message: 'Event Title is Mandatory'}); + } + }; + + return ( + <> + +
+ +
+
+ +
+ + + ); +}; + +const mapStateToProps = ({device, image}) => { + return { + imageList: image.imageList, + deviceList: device.deviceList + }; +}; + +const mapDispatchToState = dispatch => { + return { + getListDevice: () => dispatch(getDeviceList({ + subscribe: true + })), + }; +}; + +export default connect(mapStateToProps, mapDispatchToState)(CreateForm); + diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/CreateForm/createForm.css b/ref-apps/com.reference.app.familyeventplanner/src/components/CreateForm/createForm.css new file mode 100644 index 0000000..c394455 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/CreateForm/createForm.css @@ -0,0 +1,45 @@ +/* Copyright (c) 2021 LG Electronics, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + SPDX-License-Identifier: Apache-2.0 */ + +.no-border { + border: solid 2px transparent; + padding: 4px 4px 4px 4px; + margin: 0px 0px 10px 0; +} +.with-border { + border: solid 7px #f93881; + margin: 0px 0px 10px 0; +} +.image-heading{ + font-size: 30px; + font-weight: normal; + padding: 20px 10px 20px 10px; + margin: 30px 0px; +} +.pick-image{ + margin-top: 30px; +} +.source-label{ + padding: 10px; + font-size: 25px; +} +.choose-source{ + margin: 10px 0; +} +.dropdown-label{ + padding: 30px; + font-size: 25px !important; +} diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/EventsList/EventsList.js b/ref-apps/com.reference.app.familyeventplanner/src/components/EventsList/EventsList.js new file mode 100644 index 0000000..bd14ba1 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/EventsList/EventsList.js @@ -0,0 +1,47 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import ExpandableItem from '@enact/moonstone/ExpandableItem'; +import Button from '@enact/moonstone/Button'; +import IconButton from '@enact/moonstone/IconButton'; +import './eventsList.css'; + +const EventsList = (props) => { + return ( + <> + {props.events.map((ev, i) => { + return ( + +
+ {ev.image.file_path != null ? {ev.image.title} : ''} +
{ev.description}
+ props.deleteEvent(ev, props.date, props.month, props.year)} + className="delete-button" + size="small" + > + trash + +
+
+ ); + })} + + + ); +}; + +export default EventsList; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/EventsList/eventsList.css b/ref-apps/com.reference.app.familyeventplanner/src/components/EventsList/eventsList.css new file mode 100644 index 0000000..67fe190 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/EventsList/eventsList.css @@ -0,0 +1,37 @@ +/* Copyright (c) 2021 LG Electronics, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + SPDX-License-Identifier: Apache-2.0 */ + +.event { + padding: 10px 0px 0px 0px; +} +.event-body { + padding: 10px 10px 10px 30px; +} +img { + float: left; + border: 5px solid #f93881; + border-radius: 4px; + width: 100px; + height: 100px; +} +.event-desc { + padding-left: 20px; + display: inline-block; +} + +.delete-button { + float: right; +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/src/components/README.md b/ref-apps/com.reference.app.familyeventplanner/src/components/README.md new file mode 100644 index 0000000..b1a7853 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/components/README.md @@ -0,0 +1 @@ +Reusable components for your application go here \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/src/index.js b/ref-apps/com.reference.app.familyeventplanner/src/index.js new file mode 100644 index 0000000..8bae880 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/index.js @@ -0,0 +1,35 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {render} from 'react-dom'; +import App from './App'; +import {Provider} from 'react-redux'; +import configureStore from './store/configureStore'; + +const store = configureStore(); + +const appElement = ( + + + +); + +// In a browser environment, render instead of exporting +if (typeof window !== 'undefined') { + render(appElement, document.getElementById('root')); +} + +export default appElement; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/reducers/deviceReducer.js b/ref-apps/com.reference.app.familyeventplanner/src/reducers/deviceReducer.js new file mode 100644 index 0000000..a03943c --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/reducers/deviceReducer.js @@ -0,0 +1,61 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {types} from '../actions/types'; + +const initialState = { + isLoading: false, + deviceList: [], + error: '', + currentDevice:{} +}; + +const deviceListReducer = (state = initialState, action) => { + switch (action.type) { + case types.FETCH_DEVICE_LIST_REQUEST : { + return { + ...state, + isLoading: true, + deviceList: [], + error: '' + }; + } + case types.FETCH_DEVICE_LIST_SUCCESS: { + return { + ...state, + isLoading: false, + deviceList: action.payload, + error: '' + }; + } + case types.FETCH_DEVICE_LIST_ERROR: { + return { + ...state, + isLoading: true, + deviceList: [], + error: action.payload + }; + } + case types.SET_CURRENT_DEVICE: { + return {...state, + currentDevice: action.payload + }; + } + default: return state; + } +}; + +export default deviceListReducer; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/reducers/imageReducer.js b/ref-apps/com.reference.app.familyeventplanner/src/reducers/imageReducer.js new file mode 100644 index 0000000..a726482 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/reducers/imageReducer.js @@ -0,0 +1,57 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {types} from '../actions/types'; + + +const initialState = { + isImageListLoading: false, + imageList: [], + imageIndex: -1, + imageListError: '' +}; + +const imageListReducer = (state = initialState, action) => { + switch (action.type) { + case types.FETCH_IMAGE_LIST_REQUEST: { + return { + ...state, + isImageListLoading: true, + imageList: [], + imageListError: '' + }; + } + case types.FETCH_IMAGE_LIST_SUCCESS: { + return { + ...state, + isImageListLoading: false, + imageList: action.payload, + imageListError: '' + }; + } + case types.FETCH_IMAGE_LIST_ERROR: { + return { + ...state, + isImageListLoading: false, + imageList: [], + imageListError: action.payload + }; + } + default: return state; + } +}; + +export default imageListReducer; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/reducers/index.js b/ref-apps/com.reference.app.familyeventplanner/src/reducers/index.js new file mode 100644 index 0000000..cc31500 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/reducers/index.js @@ -0,0 +1,25 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import imageListReducer from './imageReducer'; +import deviceListReducer from './deviceReducer'; +import {combineReducers} from 'redux'; + +const rootReducer = combineReducers({ + device: deviceListReducer, + image: imageListReducer +}); +export default rootReducer; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/services/dbServices.js b/ref-apps/com.reference.app.familyeventplanner/src/services/dbServices.js new file mode 100644 index 0000000..7a09cef --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/services/dbServices.js @@ -0,0 +1,95 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import LS2Request from '@enact/webos/LS2Request'; + +export const dbServices = { + createKind : (cb) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'putKind', + parameters: { + 'id':'com.reference.app.service.familyeventplanner:4', + 'owner':'com.reference.app.familyeventplanner', + 'indexes':[ + {'name':'year', 'props':[{'name':'year'}]}, + {'name':'month', 'props':[{'name':'month'}]}, + {'name':'date', 'props':[{'name':'date'}]}, + {'name':'fullDate', 'props':[{'name':'year'}, {'name':'month'}, {'name':'date'}]} + ] + }, + onSuccess: (res) => { + console.log('Success response :: ', res); + cb(res); + }, + onFailure: (res) => { + console.log('Failed response :: ', res); + cb(res); + } + }); + }, + putData : (obj, cb) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'put', + parameters: { + 'objects':[ + obj + ] + }, + onSuccess: (res) => { + console.log('Success response :: ', res); + cb(res); + }, + onFailure: (res) => { + console.log('Failed response :: ', res); + cb(res); + } + }); + }, + findData : (queryObj, cb) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'find', + parameters: { + 'query': queryObj + }, + onSuccess: (res) => { + console.log('Success response :: ', res); + cb(res); + }, + onFailure: (res) => { + console.log('Failed response :: ', res); + cb(res); + } + }); + }, + deleteEvent: (id) => { + return new LS2Request().send({ + service: 'luna://com.webos.service.db', + method: 'del', + parameters: { + 'ids' : [id] + }, + onSuccess: (res) => { + console.log('Success response :: ', res); + }, + onFailure: (res) => { + console.log('Failed response :: ', res); + } + }); + } +}; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/services/generateQuery.js b/ref-apps/com.reference.app.familyeventplanner/src/services/generateQuery.js new file mode 100644 index 0000000..e00b105 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/services/generateQuery.js @@ -0,0 +1,59 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +const _kind = 'com.reference.app.service.familyeventplanner:4'; +export const generateQuery = { + findQuery : (obj) => { + let query = { + 'from':_kind, + 'where':[] + }; + for (let key in obj) { + query['where'].push({ + 'prop' : key, + 'op': '=', + 'val': obj[key] + }); + } + return query; + }, + filterData : (arr) => { + let filteredData = {}; + + for (let i = 0; i < arr.length; i++) { + if (!filteredData[arr[i].year]) { + filteredData[arr[i].year] = {}; + } + + if (!filteredData[arr[i].year][arr[i].month]) { + filteredData[arr[i].year][arr[i].month] = {}; + } + + if (!filteredData[arr[i].year][arr[i].month][arr[i].date]) { + filteredData[arr[i].year][arr[i].month][arr[i].date] = []; + } + + let temp = {}; + temp['title'] = arr[i]['event']['title']; + temp['description'] = arr[i]['event']['description']; + temp['image'] = arr[i]['image']; + + filteredData[arr[i].year][arr[i].month][arr[i].date].push(temp); + } + + return filteredData; + } +}; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/store/configureStore.js b/ref-apps/com.reference.app.familyeventplanner/src/store/configureStore.js new file mode 100644 index 0000000..e129b75 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/store/configureStore.js @@ -0,0 +1,34 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {applyMiddleware, createStore} from 'redux'; +import thunk from 'redux-thunk'; +import logger from 'redux-logger'; +import {composeWithDevTools} from 'redux-devtools-extension'; +import rootReducer from '../reducers'; + +const configureStore = () => { + const middleware = [thunk]; + if (process.env.NODE_ENV !== 'production') { + middleware.push(logger); + } + const middlewareEnhancer = applyMiddleware(...middleware); + const store = createStore(rootReducer, composeWithDevTools(middlewareEnhancer)); + return store; +}; + + +export default configureStore; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/views/MainPanel.js b/ref-apps/com.reference.app.familyeventplanner/src/views/MainPanel.js new file mode 100644 index 0000000..22998fb --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/views/MainPanel.js @@ -0,0 +1,40 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {Component} from 'react'; + +import css from './MainPanel.view.less'; +import Calendar from '../components/Calendar/Calendar'; + +export default class MainPanel extends Component { + + onDayClick = () => { + // window.alert(day); + }; + + render () { + return ( +
+ this.onDayClick(e, day)} + /> +
+ ); + } +} + +// export default MainPanel; diff --git a/ref-apps/com.reference.app.familyeventplanner/src/views/MainPanel.view.less b/ref-apps/com.reference.app.familyeventplanner/src/views/MainPanel.view.less new file mode 100644 index 0000000..18a1ae3 --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/views/MainPanel.view.less @@ -0,0 +1,20 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.mainDiv { + position: "relative"; + margin: "50px auto"; +} diff --git a/ref-apps/com.reference.app.familyeventplanner/src/views/README.md b/ref-apps/com.reference.app.familyeventplanner/src/views/README.md new file mode 100644 index 0000000..e18ab3d --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/src/views/README.md @@ -0,0 +1 @@ +Composite components that make up a distinct view go here \ No newline at end of file diff --git a/ref-apps/com.reference.app.familyeventplanner/webos-meta/appinfo.json b/ref-apps/com.reference.app.familyeventplanner/webos-meta/appinfo.json new file mode 100644 index 0000000..c3e6eae --- /dev/null +++ b/ref-apps/com.reference.app.familyeventplanner/webos-meta/appinfo.json @@ -0,0 +1,23 @@ +{ + "id": "com.reference.app.familyeventplanner", + "version": "1.0.2", + "vendor": "LGE", + "type": "web", + "main": "index.html", + "title": "Calendar", + "icon": "icon.png", + "transparent": true, + "requiredPermissions": [ + "audio.operation", + "media.operation", + "mediacontroller.operation", + "mediaindexer.operation", + "mediapipeline.resourcemanagement", + "mediapipeline.operation", + "session.management", + "sleep.management", + "physicaldevice.management", + "physicaldevice.query", + "database.operation" + ] +} diff --git a/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon-large.png b/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon-large.png new file mode 100644 index 0000000..6709fe7 Binary files /dev/null and b/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon-large.png differ diff --git a/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon-mini.png b/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon-mini.png new file mode 100644 index 0000000..e7e7879 Binary files /dev/null and b/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon-mini.png differ diff --git a/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon.png b/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon.png new file mode 100644 index 0000000..4703014 Binary files /dev/null and b/ref-apps/com.reference.app.familyeventplanner/webos-meta/icon.png differ diff --git a/ref-apps/com.reference.app.musicplayer/LICENSE b/ref-apps/com.reference.app.musicplayer/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ref-apps/com.reference.app.musicplayer/LICENSES/Apache-2.0.txt b/ref-apps/com.reference.app.musicplayer/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000..22d4c20 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/LICENSES/Apache-2.0.txt @@ -0,0 +1,183 @@ +Apache License +Version 2.0, November 2021 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ref-apps/com.reference.app.musicplayer/LICENSES/BSD-3-Clause.txt b/ref-apps/com.reference.app.musicplayer/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..a7cf702 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,26 @@ +Copyright (c) 2021 LG Electronics, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ref-apps/com.reference.app.musicplayer/LICENSES/MIT.txt b/ref-apps/com.reference.app.musicplayer/LICENSES/MIT.txt new file mode 100644 index 0000000..61ff1b0 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/LICENSES/MIT.txt @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2021 LG Electronics, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ref-apps/com.reference.app.musicplayer/README.md b/ref-apps/com.reference.app.musicplayer/README.md new file mode 100644 index 0000000..98cf36c --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/README.md @@ -0,0 +1,345 @@ +## Overview +This tutorial demonstrates the usage of Enact components to create a typical MusicPlayer for webOS OSE. + +The enact UI theme used: + - Sandstone Theme + +The features offered by the MusicPlayer app: + + - Displays the music files in grid Layouts + - Support for the External Storage + - Support for mp3 and Ogg files formats +MusicPlayer features + - Display Time duration on screen + - Album details with Thumbnail information + - Play/Pause/Previous/Next video controls + - Fast forward and Backward Operations + - Seek support on the slider +You can use this reference app as follows: + + - Install the app as-is on a webOS OSE target device. + - Update the source code as required and then deploy on a webOS OSE target device. + - Analyze the source code to understand the usage of the different Enact components. + +## Prerequisites + Node JS v10 or later - Link to download: https://nodejs.org/en/download/ + npm install -g @enact/cli - Install the Enact CLI globally. + npm install -g @webosose/ares-cli - Install the webOS OSE CLI. + +## Folder Structure +The MusicPlayer App project should look like this: + +``` +com.reference.app.musicplayer/ + assets/ + Licences/ + node_modules/ + resources/ + src/ + actions/ + App/ + App.js + App.module.less + package.json + components/ + AudioList/ + AudioPlayer/ + reducers/ + services/ + store/ + styles/ + util/ + views/ + index.js + webos-meta/ + appinfo.json + icon-large.png + icon-mini.png + icon.png + index.html + LICENSE + npm-shrinkwrap.json + oss-pkg-info.yaml + package-lock.json + package.json + README.md +``` + +For the project to build, **these files must exist with exact filenames**: + +* `package.json` is the core package manifest for the project +* `src/index.js` is the JavaScript entry point. + +You can delete or rename the other files. + +You can update the `license` entry in `package.json` to match the license of your choice. For more +information on licenses, please see the [npm documentation](https://docs.npmjs.com/files/package.json#license). + +## Available Scripts + +In the project directory, you can run: + +### `npm run serve` + +Builds and serves the app in the development mode.
+Open [http://localhost:8080](http://localhost:8080) to view it in the browser. + +The page will reload if you make edits.
+ +### `npm run pack` and `npm run pack-p` + +Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code included, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. Be sure to avoid shipping or performance testing on development mode builds. + +### `npm run watch` + +Builds the project in development mode and keeps watch over the project directory. Whenever files are changed, added, or deleted, the project will automatically get rebuilt using an active shared cache to speed up the process. This is similar to the `serve` task, but without the http server. + +### `npm run clean` + +Deletes previous build fragments from ./dist. + +### `npm run lint` + +Runs the Enact configuration of Eslint on the project for syntax analysis. + +### `npm run test` and `npm run test-watch` + +These tasks will execute all valid tests (files that end in `-specs.js`) that are within the project directory. The `test` is a standard single execution pass, while `test-watch` will set up a watcher to re-execute tests when files change. + +##Source Code +Analyze the source code to get an understanding of the functionalities implemented in the music player app. Refer to the snippets provided in this section. + +Note: Clone/download this code on your local development system. + +###Using Enact Components + - Panels - Provides a way to manage the different screens of an app. + - TabLayout - Provides the space for displaying connected devices on the Tab in the webOS OSE device. + - AudioList - Component lists the music files that are available on the device. +```js +import { TabLayout, Tab } from "@enact/sandstone/TabLayout"; +import { Panel, Header } from "@enact/sandstone/Panels"; +... + +
+ + {devices.map((device) => { + return ( + device.deviceList.length > 0 && + device.deviceList.map((deviceList, index) => { + return ( + getListAudio(deviceList.uri)} + title={deviceList.name} + > + + + ); + }) + ); + })} + + +``` + + - VirutalGridList - Populates grid list display by using music files. + - ImageItem - Component used to render the music files with name and duration. +```js +import ImageItem from "@enact/sandstone/ImageItem"; +import { VirtualGridList } from "@enact/sandstone/VirtualList"; +... + handleNavigate("/audioplayer", audiolist[index], index)} + > + {audiolist[index].title} + +... + +``` +###Luna Service usage +The application uses the "com.webos.service.mediaindexer" luna service for listing devices, music files, and getting metadata information. + +The methods are as follows + +- getDeviceList + Gets the list of all the attached storage devices. + The list can contain devices that are currently attached or the devices attached in the past. +- getAudioList + Gets the available audio file list included in attached devices. + If the uri is specified, the audio file list for the specified URI is provided. Else the audio file list for all attached devices is provided. +- getAudioMetadata + Gets the detailed metadata information of the specified URI for the given audio file. + For Ex: "file_size", "thumbnail", "duration", "file_path" ,"album", "title" etc + +```js + getDeviceList: ({subscribe, ...rest}) => { + let params = { + subscribe: subscribe + }; + return luna('com.webos.service.mediaindexer', 'getDeviceList', params)(rest); + } + getAudioList: ({uri, ...rest}) => { + let params = { + uri: uri + }; + return luna('com.webos.service.mediaindexer', 'getAudioList', params)(rest); + }, + + getAudioMetaData: ({uri, ...rest}) => { + let params = { + uri: uri + }; + return luna('com.webos.service.mediaindexer', 'getAudioMetadata', params)(rest); + } +... +... + const luna = ( + service, + method, + {subscribe = false, timeout = 0, ...params} = {}, + map + ) => ( + ({onSuccess, onFailure, onTimeout, onComplete, ...additionalParams} = {}) => { + const req = new LS2Request(); + req.send({ + service: 'luna://' + service, + method, + parameters: Object.assign({}, params, additionalParams), + onSuccess: handler(onSuccess, map), + onFailure: handler(onFailure), + onTimeout: handler(onTimeout), + onComplete: handler(onComplete, map), + subscribe, + timeout + }); + return req; + } + ); +``` +###Custom Components used in App + +####Audio Player +This is the main component derived from the "audio" HTML base which helps to play/pause audio files on the device. +Displays the current playing information on the device. +Handles to play previous or next audio files on the list. + +####Album Info +Displays the album information such as "Title", "Artist", "Album name" and "Thumbnail" image for each music file. + +####Media Slider +Seek the audio file at a particular position. So the audio starts playing from the current position. +Hides the media knob when seeking disabled. + ```js + handleBack("home")} + handleNext={handleNextAudio} + handlePrevious={handlePreviousAudio} + ... + playlist={audioMetaData} + seekDisabled={false} + spotlightDisabled={false} + thumbnailSrc={audioMetaData.thumbnail} + title={"Music Player"} + titleHideDelay={4000} + /> + ... +... + +... + +``` +####Utility Method + - secondsToTime + Takes the seconds value as input and represents the values in user understandable format. + + - getEncodedPath + Encodes the image URI path to avoid reading issues from the system. + It adds the "file:///" when the path directly starts with "/". + Replaces the "/" with "%20" format to avoid file read issues. +```js + const secondsToTime = (seconds, durfmt, config) => { + const includeHour = config && config.includeHour; + + if (durfmt) { + const parsedTime = parseTime(seconds); + const timeString = durfmt.format(parsedTime).toString(); + + if (includeHour && !parsedTime.hour) { + return "00:" + timeString; + } else { + return timeString; + } + } + + return includeHour ? "00:00:00" : "00:00"; + }; + ... + const getEncodedPath = (path) => { + let encodedPath = ""; + if (path && path.length > 0) { + encodedPath = encodeURIComponent(path); + if (path && path.substring(0, 1) === "/") { + encodedPath = "file:///" + encodedPath; + } + encodedPath = encodedPath.replace(/ /g, "%20"); + } + return encodedPath; + }; +``` +## Installing the App on the Target Device +Go to the app folder and execute the following commands: + +###Package the enact source code. +`enact pack` A dist folder will be created. + +###Package the app to create an IPK. +`ares-package dist` +An IPK named com.reference.app.musicplayer_1.0.2_all.ipk is created. + +###Install the IPK +`ares-install --device com.reference.app.musicplayer_1.0.2_all.ipk` \ No newline at end of file diff --git a/ref-apps/com.reference.app.musicplayer/assets/images/defaultalbum.png b/ref-apps/com.reference.app.musicplayer/assets/images/defaultalbum.png new file mode 100644 index 0000000..7c5c069 Binary files /dev/null and b/ref-apps/com.reference.app.musicplayer/assets/images/defaultalbum.png differ diff --git a/ref-apps/com.reference.app.musicplayer/assets/mock/audioList.json b/ref-apps/com.reference.app.musicplayer/assets/mock/audioList.json new file mode 100644 index 0000000..1eb58ae --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/assets/mock/audioList.json @@ -0,0 +1,106 @@ +{ + "errorCode": 0, + "returnValue": true, + "errorText": "No Error", + "audioList": { + "results": [ + { + "last_modified_date": "Wed Jul 22 09:09:38 2020 GMT", + "duration": 348, + "file_path": "file:///tmp/usb/sdh/sdh1/Sample-OGG-File.ogg", + "album": "Ninja Tuna", + "genre": "Electronic", + "artist": "Mr. Scruff", + "title": "Kalimba", + "uri": "msc://B5E0-BBB2/tmp/usb/sdg/sdg1/Sample-OGG-File.ogg", + "file_size": 6984260, + "thumbnail": "" + }, + { + "last_modified_date": "Thu Jul 30 06:48:04 2020 GMT", + "duration": 10, + "file_path": "file:///tmp/usb/sdh/sdh1/tagtest.ID3v2.4.mp3", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist", + "title": "Test Name", + "uri": "msc://D94A-BE4D/tmp/usb/sdh/sdh1/tagtest.ID3v2.4.mp3", + "file_size": 184861, + "thumbnail": "/media/.thumbnail/D94A-BE4D/9513454098549223.jpg" + }, + { + "duration": 205, + "file_path": "file:///tmp/usb/sdg/sdg1/audio/SampleAudiofiles1.mp3", + "last_modified_date": "Fri Aug 14 03:55:22 2020 GMT", + "thumbnail": "/media/.thumbnail/B5E0-BBB2/1716759322466815.jpg", + "file_size": 264881271, + "uri": "msc://B5E0-BBB2/tmp/usb/sdg/sdg1/audio/SampleAudiofiles1.mp3", + "title": "Test Song1", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist" + }, + { + "duration": 205, + "file_path": "file:///tmp/usb/sdg/sdg1/audio/SampleAudiofiles2.mp3", + "last_modified_date": "Fri Aug 14 03:55:22 2020 GMT", + "thumbnail": "/media/.thumbnail/B5E0-BBB2/1716759322466815.jpg", + "file_size": 264881271, + "uri": "msc://B5E0-BBB2/tmp/usb/sdg/sdg1/audio/SampleAudiofiles2.mp4", + "title": "Test Song2", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist" + }, + { + "duration": 205, + "file_path": "file:///tmp/usb/sdg/sdg1/audio/SampleAudiofiles3.mp3", + "last_modified_date": "Fri Aug 14 03:55:22 2020 GMT", + "thumbnail": "/media/.thumbnail/B5E0-BBB2/1716759322466815.jpg", + "file_size": 264881271, + "uri": "msc://B5E0-BBB2/tmp/usb/sdg/sdg1/audio/SampleAudiofiles3.mp4", + "title": "Test Song3", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist" + }, + { + "duration": 209, + "file_path": "file:///tmp/usb/sdh/sdh1/SampleAudiofiles4.mp3", + "last_modified_date": "Wed Jul 22 09:07:16 2020 GMT", + "thumbnail": "/media/.thumbnail/D94A-BE4D/3810749630515133.jpg", + "file_size": 268264916, + "uri": "msc://D94A-BE4D/tmp/usb/sdh/sdh1/SampleAudiofiles4.mp3", + "title": "Test Song4", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist" + }, + { + "duration": 209, + "file_path": "file:///tmp/usb/sdh/sdh1/SampleAudiofiles5.mp3", + "last_modified_date": "Wed Jul 22 09:07:16 2020 GMT", + "thumbnail": "/media/.thumbnail/D94A-BE4D/3810749630515133.jpg", + "file_size": 268264916, + "uri": "msc://D94A-BE4D/tmp/usb/sdh/sdh1/SampleAudiofiles5.mp3", + "title": "Test Song5", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist" + }, + { + "duration": 209, + "file_path": "file:///tmp/usb/sdh/sdh1/SampleAudiofiles6.mp3", + "last_modified_date": "Wed Jul 22 09:07:16 2020 GMT", + "thumbnail": "/media/.thumbnail/D94A-BE4D/3810749630515133.jpg", + "file_size": 268264916, + "uri": "msc://D94A-BE4D/tmp/usb/sdh/sdh1/SampleAudiofiles6.mp3", + "title": "Test Song6", + "album": "Test Album", + "genre": "Classical", + "artist": "Test Artist" + } + ], + "count": 8 + } +} diff --git a/ref-apps/com.reference.app.musicplayer/assets/mock/deviceList.json b/ref-apps/com.reference.app.musicplayer/assets/mock/deviceList.json new file mode 100644 index 0000000..d7635d4 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/assets/mock/deviceList.json @@ -0,0 +1,36 @@ +{ + "pluginList": [ + { + "deviceList": [ + { + "name": "Ultra", + "imageCount": 0, + "uri": "msc://B5E0-BBB2", + "videoCount": 1, + "available": true, + "icon": "", + "audioCount": 34, + "description": "Ultra" + }, + { + "name": "Cruzer Blade", + "imageCount": 33, + "uri": "msc://D94A-BE4D", + "videoCount": 7, + "available": true, + "icon": "", + "audioCount": 35, + "description": "Cruzer Blade" + } + ], + "active": true, + "uri": "msc" + }, + { + "deviceList": [], + "active": false, + "uri": "storage" + } + ], + "returnValue": true + } \ No newline at end of file diff --git a/ref-apps/com.reference.app.musicplayer/npm-shrinkwrap.json b/ref-apps/com.reference.app.musicplayer/npm-shrinkwrap.json new file mode 100644 index 0000000..56bd0c2 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/npm-shrinkwrap.json @@ -0,0 +1,655 @@ +{ + "name": "com.reference.app.photovideo", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz", + "integrity": "sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@enact/core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/core/-/core-3.5.0.tgz", + "integrity": "sha512-k4CJ3KUdXEljgl/y/cj8uT0j//eKfsh1S6CnINjF2C4y/jchxzCSM2QEkwCTqIz86dGkx/N3JYr6cl09wgChFA==", + "requires": { + "classnames": "^2.2.5", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "react-is": "^16.8.3", + "warning": "^4.0.3" + }, + "dependencies": { + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/i18n": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/i18n/-/i18n-3.5.0.tgz", + "integrity": "sha512-SNtMLqQQKUaXk9ZUMDqjhZsV/TL9IdR07nbYIjZ7mS6CFE8Qekf4jzUtC0bkV9uYjaoVP0bS4mj1VnyWm/uxvA==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "xhr": "^2.4.1" + }, + "dependencies": { + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + } + } + }, + "@enact/moonstone": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@enact/moonstone/-/moonstone-4.0.0.tgz", + "integrity": "sha512-KIiimX3gyNQAEQJ5eAk04lwL+35VI5TcsWEhLgUadnMjrPgBUTUDTL4SUw+pi7dsy2ktHMzm9a0DiJRd2g/n5A==", + "requires": { + "@enact/core": "^4.0.0", + "@enact/i18n": "^4.0.0", + "@enact/spotlight": "^4.0.0", + "@enact/ui": "^4.0.0", + "classnames": "^2.2.5", + "ilib": "^14.2.0", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "@enact/core": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@enact/core/-/core-4.0.0.tgz", + "integrity": "sha512-xvfJ8efsThX/YmMbvMIqpTlyEF5V1PmU1jQrpTW2leUUVVrBYKw9Vgr9i2qfcMT1Zb5zslLwIeD6lQqVCm+0IQ==", + "requires": { + "classnames": "^2.2.5", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "react-is": "^17.0.1", + "recompose": "^0.26.0", + "warning": "^4.0.3" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "@enact/i18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@enact/i18n/-/i18n-4.0.0.tgz", + "integrity": "sha512-u5JNd2b16MXmI8j3XPwt2aPoJEEVqKwAFD+81LR5LmzoyS3U+imyHbMHjDoRgWOLpFMZ+LTNAUTpahYIPy0x/Q==", + "requires": { + "@enact/core": "^4.0.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "xhr": "^2.4.1" + } + }, + "@enact/spotlight": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@enact/spotlight/-/spotlight-4.0.0.tgz", + "integrity": "sha512-1AA3o1Uzzut56BWHvo+aeiOaE3pfIvKSrFTGPntfn9K71WOVRz47onRll2sRFuoG+aWPWpIE+D29OMQxDQANKQ==", + "requires": { + "@enact/core": "^4.0.0", + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "warning": "^3.0.0" + } + }, + "@enact/ui": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@enact/ui/-/ui-4.0.0.tgz", + "integrity": "sha512-hPs6xuaiLb4RPFzLZHmVarE1LRUjGqb3gn0WcCCRe6466Gi2P1UnnQaPq2yd7qZrlrgxLfifafaroaLyVfAQ6w==", + "requires": { + "@enact/core": "^4.0.0", + "classnames": "^2.2.5", + "direction": "^1.0.1", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "react-is": "^17.0.1", + "recompose": "^0.26.0", + "warning": "^3.0.0" + } + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", + "requires": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "symbol-observable": "^1.0.4" + } + }, + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + } + }, + "@enact/sandstone": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@enact/sandstone/-/sandstone-1.5.1.tgz", + "integrity": "sha512-ujuA2k99fSm2X5ZqhT4HDGnnNMGqkjn37QD2U6+jJPwL9E/68K1Bpyrq4CtmlxGE+Tc7QBkoQmPZVXEURYJOyw==", + "requires": { + "@enact/core": "^3.5.0", + "@enact/i18n": "^3.5.0", + "@enact/spotlight": "^3.5.0", + "@enact/ui": "^3.5.0", + "classnames": "^2.2.5", + "ilib": "^14.2.0", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", + "requires": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "symbol-observable": "^1.0.4" + } + } + } + }, + "@enact/spotlight": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/spotlight/-/spotlight-3.5.0.tgz", + "integrity": "sha512-XqvAEOz0emHAQIQ5XL5nec0L9Poex4bfsp1YxoUa0zzNGybcERQg1ggxrTseQLzr+cUilN8weMRunOyDh7mQUQ==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.7.0", + "react-dom": "^16.7.0", + "warning": "^3.0.0" + }, + "dependencies": { + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + } + } + }, + "@enact/ui": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/ui/-/ui-3.5.0.tgz", + "integrity": "sha512-Ln5SBvLKPTvuGarPnBra87lrfdi9wXVw8MMaQQOPmljx1a9tZmZRBHgI2bYZlObz5ltGEfXrpI3nerfqiAN0ww==", + "requires": { + "@enact/core": "^3.5.0", + "classnames": "^2.2.5", + "direction": "^1.0.1", + "invariant": "^2.2.2", + "prop-types": "^15.7.2", + "ramda": "^0.24.1", + "react": "^16.8.0", + "react-dom": "^16.8.0", + "recompose": "^0.26.0", + "warning": "^3.0.0" + }, + "dependencies": { + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" + }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", + "requires": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "symbol-observable": "^1.0.4" + } + } + } + }, + "@enact/webos": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@enact/webos/-/webos-3.5.0.tgz", + "integrity": "sha512-E/XXyMOnjSsDvvk7r0VRNXUO1gkyPwWOCo5TUWDTZ8fYImJHfBO5okTwHw9rVK2v32sc8wSMEr8VcIf9BCwHTQ==", + "requires": { + "@enact/core": "^3.5.0", + "prop-types": "^15.7.2", + "react": "^16.8.0", + "react-dom": "^16.8.0" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" + }, + "direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==" + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ilib": { + "version": "14.7.0", + "resolved": "https://registry.npmjs.org/ilib/-/ilib-14.7.0.tgz", + "integrity": "sha512-f3AwWy04Pbb3rcIQD/CR40sgQHOpH4LQbB3FiU3mm4wTpYEOWjrxeT1WI6NMp9+dxQ5hnmd8zkuSjJoVlyAlzQ==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-addons-update": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/react-addons-update/-/react-addons-update-15.6.3.tgz", + "integrity": "sha512-wBkjgx5cR0XTjZEz5jl2kScChrjI9T7rWVdaM0dLiIdHSgeHycLRdHPPiTgKk7vK18Od4rXmLJv91qofBXlE0A==", + "requires": { + "object-assign": "^4.1.0" + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-redux": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", + "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", + "requires": { + "@babel/runtime": "^7.12.1", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + } + } + }, + "recompose": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", + "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", + "requires": { + "@babel/runtime": "^7.0.0", + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "react-lifecycles-compat": "^3.0.2", + "symbol-observable": "^1.0.4" + } + }, + "redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-devtools-extension": { + "version": "2.13.8", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz", + "integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==" + }, + "redux-logger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", + "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", + "requires": { + "deep-diff": "^0.3.5" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "ua-parser-js": { + "version": "0.7.24", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz", + "integrity": "sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==" + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "whatwg-fetch": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz", + "integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==" + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/ref-apps/com.reference.app.musicplayer/oss-pkg-info.yaml b/ref-apps/com.reference.app.musicplayer/oss-pkg-info.yaml new file mode 100644 index 0000000..91ea690 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/oss-pkg-info.yaml @@ -0,0 +1,28 @@ +Open Source Software Package: + - name: Axios + source: git://github.com/axios/axios + license: MIT + - name: Cross Env + source: git://github.com/kentcdodds/cross-env + license: MIT + - name: Enact + source: https://github.com/enactjs/enact + license: Apache-2.0 + - name: Enact-cli + source: https://github.com/enactjs/cli + license: Apache-2.0 + - name: iLib + source: git://github.com/iLib-js/iLib + license: Apache-2.0 + - name: Immer + source: git://github.com/immerjs/immer + license: MIT + - name: Nodemon + source: git://github.com/remy/nodemon + license: MIT + - name: React + source: git://github.com/facebook/react + license: MIT + - name: Query String + source: git://github.com/sindresorhus/query-string + license: MIT diff --git a/ref-apps/com.reference.app.musicplayer/package.json b/ref-apps/com.reference.app.musicplayer/package.json new file mode 100644 index 0000000..32438b4 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/package.json @@ -0,0 +1,57 @@ +{ + "name": "com.reference.app.photovideo", + "version": "1.0.0", + "description": "A general template for an Enact Agate application.", + "author": "", + "main": "src/index.js", + "scripts": { + "serve": "enact serve", + "pack": "enact pack", + "pack-p": "enact pack -p", + "watch": "enact pack --watch", + "clean": "enact clean", + "lint": "enact lint .", + "license": "enact license", + "test": "enact test", + "test-watch": "enact test --watch" + }, + "license": "Apache-2.0", + "private": true, + "repository": "", + "enact": { + "theme": "goldstone", + "forceCSSModules": true, + "ri": { + "baseSize": 72 + } + }, + "eslintConfig": { + "extends": "enact" + }, + "eslintIgnore": [ + "node_modules/*", + "build/*", + "dist/*" + ], + "dependencies": { + "@enact/core": "^3.2.0", + "@enact/i18n": "^3.2.0", + "@enact/moonstone": "^4.0.0", + "@enact/sandstone": "^1.5.1", + "@enact/spotlight": "^3.2.0", + "@enact/ui": "^3.2.0", + "@enact/webos": "^3.5.0", + "ilib": "^14.4.0", + "prop-types": "^15.6.2", + "ramda": "^0.27.1", + "react": "^16.7.0", + "react-addons-update": "^15.6.3", + "react-dom": "^16.7.0", + "react-redux": "^7.2.2", + "recompose": "^0.30.0", + "redux": "^4.0.5", + "redux-devtools-extension": "^2.13.8", + "redux-logger": "^3.0.6", + "redux-thunk": "^2.3.0" + } +} diff --git a/ref-apps/com.reference.app.musicplayer/resources/ilibmanifest.json b/ref-apps/com.reference.app.musicplayer/resources/ilibmanifest.json new file mode 100644 index 0000000..5916671 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/resources/ilibmanifest.json @@ -0,0 +1,3 @@ +{ + "files": [] +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.musicplayer/src/App/App.js b/ref-apps/com.reference.app.musicplayer/src/App/App.js new file mode 100644 index 0000000..4ed7c8f --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/App/App.js @@ -0,0 +1,47 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { Panels, Routable, Route } from "@enact/sandstone/Panels"; +import ThemeDecorator from "@enact/sandstone/ThemeDecorator"; + +import MainPanel from "../views/MainPanel"; +import AudioPanel from "../views/AudioPanel/AudioPanel"; + +const RoutablePanels = Routable({ navigate: "onBack" }, Panels); + +const App = ({ path, ...rest }) => { + return ( + + + + + ); +}; + +App.propTypes = { + path: PropTypes.string, +}; + +const mapStateToProps = ({ path }) => { + return { + path: path.path, + }; +}; + +export default connect(mapStateToProps, {})(ThemeDecorator(App)); diff --git a/ref-apps/com.reference.app.musicplayer/src/App/App.module.less b/ref-apps/com.reference.app.musicplayer/src/App/App.module.less new file mode 100644 index 0000000..57bc3df --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/App/App.module.less @@ -0,0 +1,19 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.app { + // styles can be put here +} diff --git a/ref-apps/com.reference.app.musicplayer/src/App/package.json b/ref-apps/com.reference.app.musicplayer/src/App/package.json new file mode 100644 index 0000000..add0ba2 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/App/package.json @@ -0,0 +1,3 @@ +{ + "main": "App.js" +} \ No newline at end of file diff --git a/ref-apps/com.reference.app.musicplayer/src/actions/audioActions.js b/ref-apps/com.reference.app.musicplayer/src/actions/audioActions.js new file mode 100644 index 0000000..f424a11 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/actions/audioActions.js @@ -0,0 +1,138 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { types } from "./types"; +import { Audio } from "../services"; +import LS2Request from "@enact/webos/LS2Request"; +import mockAudioList from "../../assets/mock/audioList.json"; + +const getCurrentAudioRequest = () => { + return { + type: types.FETCH_CURRENT_AUDIO_REQUEST, + }; +}; + +const setCurrentAudioSuccess = (audioMetaData, index) => { + return { + type: types.FETCH_CURRENT_AUDIO_SUCCESS, + payload: audioMetaData, + index: index, + }; +}; + +const setCurrentAudioError = (message) => { + return { + type: types.FETCH_CURRENT_AUDIO_ERROR, + payload: message, + }; +}; + +const getAudioListRequest = () => { + return { + type: types.FETCH_AUDIO_LIST_REQUEST, + }; +}; + +const setAudioListSuccess = (audioList) => { + return { + type: types.FETCH_AUDIO_LIST_SUCCESS, + payload: audioList, + }; +}; + +const setAudioListError = (message) => { + return { + type: types.FETCH_AUDIO_LIST_ERROR, + payload: message, + }; +}; + +const updateAudioMetaData = (metaData) => { + if (metaData !== undefined && metaData !== null) { + let title = metaData.title; + if (title.length === 0) { + let pbArray = String(metaData.file_path).split("/"); + metaData.title = pbArray[pbArray.length - 1]; + } + if (metaData.thumbnail === 0) { + metaData.thumbnail = ""; + } + } + return metaData; +}; + +const updateAudioList = (audiolist) => { + if (audiolist && audiolist.length > 0) { + for (let i in audiolist) { + audiolist[i] = updateAudioMetaData(audiolist[i]); + } + } + return audiolist; +}; + +const getAudioList = + ({ uri }) => + (dispatch) => { + dispatch(getAudioListRequest()); + /* + if (!window.palm) { + dispatch(setAudioListSuccess(mockAudioList.audioList.results)); + console.log(mockAudioList.audioList.results); + } else */ { + Audio.getAudioList({ + uri: uri, + onSuccess: (res) => { + const { returnValue, audioList } = res; + if (returnValue) { + dispatch(setAudioListSuccess(updateAudioList(audioList.results))); + } + }, + onFailure: (err) => { + dispatch(setAudioListError(err.errorText)); + }, + }); + } + }; + +const getCurrentAudioMetaData = + ({ uri, audioIndex }) => + (dispatch) => { + dispatch(getCurrentAudioRequest()); + return new LS2Request().send({ + service: "luna://com.webos.service.mediaindexer", + method: "getAudioMetadata", + parameters: { uri: uri }, + onSuccess: ({ metadata }) => { + dispatch( + setCurrentAudioSuccess(updateAudioMetaData(metadata), audioIndex) + ); + }, + onFailure: (err) => { + dispatch(setAudioListError(err.errorText)); + }, + }); + }; + +export { + getCurrentAudioMetaData, + getCurrentAudioRequest, + getAudioList, + getAudioListRequest, + setCurrentAudioError, + setCurrentAudioSuccess, + setAudioListError, + setAudioListSuccess, +}; diff --git a/ref-apps/com.reference.app.musicplayer/src/actions/deviceActions.js b/ref-apps/com.reference.app.musicplayer/src/actions/deviceActions.js new file mode 100644 index 0000000..84edda9 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/actions/deviceActions.js @@ -0,0 +1,76 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { types } from "./types"; +import { Device } from "../services"; +import mockDeviceList from "../../assets/mock/deviceList.json"; + +const getDeviceListRequest = () => { + return { + type: types.FETCH_DEVICE_LIST_REQUEST, + }; +}; + +const setDeviceListSuccess = (deviceList) => { + return { + type: types.FETCH_DEVICE_LIST_SUCCESS, + payload: deviceList, + }; +}; + +const setDeviceListError = (errMessage) => { + return { + type: types.FETCH_DEVICE_LIST_ERROR, + payload: errMessage, + }; +}; + +const getDeviceList = + ({ subscribe }) => + (dispatch) => { + dispatch(getDeviceListRequest()); + /* + if(!window.plam){ + dispatch(setDeviceListSuccess(mockDeviceList.pluginList)); + } + else*/ + { + Device.getDeviceList({ + subscribe: subscribe, + onSuccess: (res) => { + dispatch(setDeviceListSuccess(res.pluginList)); + }, + onFailure: (err) => { + dispatch(setDeviceListSuccess(err.errorText)); + }, + }); + } + }; + +const setCurrentDevice = (device) => { + return { + type: types.SET_CURRENT_DEVICE, + payload: device, + }; +}; + +export { + setCurrentDevice, + getDeviceList, + getDeviceListRequest, + setDeviceListSuccess, + setDeviceListError, +}; diff --git a/ref-apps/com.reference.app.musicplayer/src/actions/navigationActions.js b/ref-apps/com.reference.app.musicplayer/src/actions/navigationActions.js new file mode 100644 index 0000000..4332559 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/actions/navigationActions.js @@ -0,0 +1,28 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import {types} from './types'; + +const changePath = (path) => { + return { + type: types.CHANGE_PATH, + payload: path + }; +}; + +export { + changePath +}; diff --git a/ref-apps/com.reference.app.musicplayer/src/actions/types.js b/ref-apps/com.reference.app.musicplayer/src/actions/types.js new file mode 100644 index 0000000..82941c1 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/actions/types.js @@ -0,0 +1,29 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +export const types = { + CHANGE_PATH: "CHANGE_PATH", + FETCH_CURRENT_AUDIO_REQUEST: "FETCH_CURRENT_AUDIO_REQUEST", + FETCH_CURRENT_AUDIO_SUCCESS: "FETCH_CURRENT_AUDIO_SUCCESS", + FETCH_CURRENT_AUDIO_ERROR: "FETCH_CURRENT_AUDIO_ERROR", + FETCH_AUDIO_LIST_REQUEST: "FETCH_AUDIO_LIST_REQUEST", + FETCH_AUDIO_LIST_SUCCESS: "FETCH_AUDIO_LIST_SUCCESS", + FETCH_AUDIO_LIST_ERROR: "FETCH_AUDIO_LIST_ERROR", + FETCH_DEVICE_LIST_REQUEST: "FETCH_DEVICE_LIST_REQUEST", + FETCH_DEVICE_LIST_SUCCESS: "FETCH_DEVICE_LIST_SUCCESS", + FETCH_DEVICE_LIST_ERROR: "FETCH_DEVICE_LIST_ERROR", + SET_CURRENT_DEVICE: "SET_CURRENT_DEVICE", +}; diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioList/AudioList.js b/ref-apps/com.reference.app.musicplayer/src/components/AudioList/AudioList.js new file mode 100644 index 0000000..2e4201f --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioList/AudioList.js @@ -0,0 +1,87 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import PropTypes from "prop-types"; +import ImageItem from "@enact/sandstone/ImageItem"; +import { VirtualGridList } from "@enact/sandstone/VirtualList"; +import ri from "@enact/ui/resolution"; +import placeHolderImg from "../../../assets/images/defaultalbum.png"; +import { getEncodedPath } from "../../components/AudioPlayer/AudioPlayerBase/util"; +import { memoize } from "@enact/core/util"; +import DurationFmt from "ilib/lib/DurationFmt"; +import { secondsToTime } from "../AudioPlayer/AudioPlayerBase/util"; + +const memoGetDurFmt = memoize( + () => + new DurationFmt({ + length: "medium", + style: "clock", + useNative: false, + }) +); + +const getDurFmt = () => { + if (typeof window === "undefined") return null; + return memoGetDurFmt(); +}; + +const audioList = ({ audiolist, handleNavigate }) => { + const renderItem = ({ index, ...rest }) => { + let encodedPath = getEncodedPath(audiolist[index].thumbnail); + const durFmt = getDurFmt(); + const duration = secondsToTime(audiolist[index].duration, durFmt); + + return ( + handleNavigate("/audioplayer", audiolist[index], index)} + > + {audiolist[index].title} + + ); + }; + audiolist = audiolist || []; + return audiolist.length === 0 ? ( +

Audio folders does not exist in storage device

+ ) : ( + + ); +}; + +audioList.propTypes = { + handleNavigate: PropTypes.func.isRequired, + audioList: PropTypes.array, +}; + +audioList.default = { + audioList: [], +}; + +export default audioList; diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayer.js b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayer.js new file mode 100644 index 0000000..0b0bf62 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayer.js @@ -0,0 +1,102 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Provides Goldstone-themed Audio player component with out of the box features. + * + * @exports AudioPlayer + */ +import PropTypes from "prop-types"; +import React, { useReducer } from "react"; +import AudioPlayerBase from "./AudioPlayerBase/AudioPlayerBase"; +import AudioPlayerReducer from "./Reducers/AudioPlayerReducer"; + +const initialState = { + current: 0, + repeat: { + type: 0, + loop: false, + }, +}; +const AudioPlayer = ({ + handleBack, + handleNext, + handlePrevious, + playlist, + ...rest +}) => { + const [state] = useReducer(AudioPlayerReducer, initialState); + const getAudioType = (file_path) => { + let mimeType = "audio/mp3"; + if (file_path && file_path.length > 0) { + let extension = file_path.substring(file_path.lastIndexOf(".") + 1); + if (extension && extension.length > 0) { + mimeType = "audio/" + extension; + } + } + return mimeType; + }; + let extType = getAudioType(playlist.file_path); + return ( + + + + ); +}; + +AudioPlayer.propTypes = { + /** + * Function to handle navigation + * + * @type {Function} + */ + handleBack: PropTypes.func, + + /** + * Function to handle Next audio + * + * @type {Function} + */ + handleNext: PropTypes.func, + + /** + * Function to handle Previous audio + * + * @type {Function} + */ + handlePrevious: PropTypes.func, + + /** + * Contains the list of audios to be played + * + * @type {Array} + */ + playlist: PropTypes.object, +}; + +export default AudioPlayer; diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayer.module.less b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayer.module.less new file mode 100644 index 0000000..88e1491 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayer.module.less @@ -0,0 +1,60 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.white::cue { + color: #ffffff; +} + +.gray::cue { + color: #afa5a3; +} + +.yellow::cue { + color: #eeda10; +} + +.green::cue { + color: #46ee10; +} + +.blue::cue { + color: #1046ee; +} + +.red::cue { + color: #ee1027; +} + +.vsmall::cue { + font-size: 0.6rem; +} +.small::cue { + font-size: 0.8rem; +} +.normal::cue { + font-size: 1rem; +} +.big::cue { + font-size: 1.7rem; +} +.vbig::cue { + font-size: 2rem; +} + +video::-webkit-media-text-track-display { + // position: absolute; + margin-bottom: 1000px; +} diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AlbumInfo.js b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AlbumInfo.js new file mode 100644 index 0000000..2c28c70 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AlbumInfo.js @@ -0,0 +1,53 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import React from "react"; +import placeHolderImg from "../../../../assets/images/defaultalbum.png"; +import css from "./AlbumInfo.module.less"; +import { getEncodedPath } from "./util"; + +function AlbumInfo({ title, artist, album, thumbnail, isPlaying }) { + let imgSrc = + thumbnail && thumbnail.length > 0 + ? getEncodedPath(thumbnail) + : placeHolderImg; + const getTitle = () => { + if (isPlaying === true) { + return ( + +

{title}

+
+ ); + } else { + return

{title}

; + } + }; + let artists = artist === 0 ? "" : artist; + let albums = album === 0 ? "" : album; + + return ( +
+ +
+ {getTitle()} +

{artists}

+

{albums}

+
+
+ ); +} + +export default React.memo(AlbumInfo); diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AlbumInfo.module.less b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AlbumInfo.module.less new file mode 100644 index 0000000..c0a2320 --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AlbumInfo.module.less @@ -0,0 +1,64 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +.title { + width: 30vw; + font-size: 1.6rem; + color: white; + text-transform: uppercase; +} + +.subtitle { + width: 25vw; + font-size: 1.3rem; + color: lightgrey; + text-transform: uppercase; +} + +.albumname { + width: 25vw; + font-size: 1.3rem; + color: lightgrey; + text-transform: uppercase; +} + +.albumimage { + position: relative; + width: 30%; + height: 50%; + top: 5%; + object-fit: contain; + display: block; +} +.info { + position: relative; + justify-content: left; + align-items: left; + margin-left: 3%; + top: 5%; +} +.album { + position: absolute; + top: -10%; + display: inline-flex; + justify-content: left; + align-items: center; + margin-left: 10%; + height: 100vh; + width: 100vw; + max-height: 100%; + max-width: 100%; +} diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/Audio.js b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/Audio.js new file mode 100644 index 0000000..92c857d --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/Audio.js @@ -0,0 +1,300 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { forward } from "@enact/core/handle"; +import ForwardRef from "@enact/ui/ForwardRef"; +import { Media, getKeyFromSource } from "@enact/ui/Media"; +import EnactPropTypes from "@enact/core/internal/prop-types"; +import Slottable from "@enact/ui/Slottable"; +import compose from "ramda/src/compose"; +import React from "react"; +import PropTypes from "prop-types"; +import css from "./AudioPlayer.module.less"; + +/** + * Adds support for preloading a video source for `VideoPlayer`. + * + * @class VideoBase + * @memberof sandstone/VideoPlayer + * @ui + * @private + */ +const AudioBase = class extends React.Component { + static displayName = "Audio"; + + static propTypes = /** @lends sandstone/VideoPlayer.Video.prototype */ { + /** + * Video plays automatically. + * + * @type {Boolean} + * @default false + * @public + */ + autoPlay: PropTypes.bool, + + /** + * Video component to use. + * + * The default (`'video'`) renders an `HTMLVideoElement`. Custom video components must have + * a similar API structure, exposing the following APIs: + * + * Properties: + * * `currentTime` {Number} - Playback index of the media in seconds + * * `duration` {Number} - Media's entire duration in seconds + * * `error` {Boolean} - `true` if video playback has errored. + * * `loading` {Boolean} - `true` if video playback is loading. + * * `paused` {Boolean} - Playing vs paused state. `true` means the media is paused + * * `playbackRate` {Number} - Current playback rate, as a number + * * `proportionLoaded` {Number} - A value between `0` and `1` + * representing the proportion of the media that has loaded + * * `proportionPlayed` {Number} - A value between `0` and `1` representing the + * proportion of the media that has already been shown + * + * Events: + * * `onLoadStart` - Called when the video starts to load + * * `onPlay` - Sent when playback of the media starts after having been paused + * * `onUpdate` - Sent when any of the properties were updated + * + * Methods: + * * `play()` - play video + * * `pause()` - pause video + * * `load()` - load video + * + * The [`source`]{@link sandstone/VideoPlayer.Video.source} property is passed to + * the video component as a child node. + * + * @type {String|Component|Element} + * @default 'video' + * @public + */ + mediaComponent: EnactPropTypes.renderableOverride, + + /** + * The video source to be preloaded. Expects a `` node. + * + * @type {Node} + * @public + */ + preloadSource: PropTypes.node, + + /** + * Called with a reference to the active [Media]{@link ui/Media.Media} component. + * + * @type {Function} + * @private + */ + setMedia: PropTypes.func, + + /** + * The video source to be played. + * + * Any children `` elements will be sent directly to the `mediaComponent` as video + * sources. + * + * See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source + * + * @type {Node} + * @public + */ + source: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + }; + + static defaultProps = { + mediaComponent: "audio", + }; + + componentDidUpdate(prevProps) { + const { source, preloadSource } = this.props; + const { source: prevSource, preloadSource: prevPreloadSource } = prevProps; + + const key = getKeyFromSource(source); + const prevKey = getKeyFromSource(prevSource); + const preloadKey = getKeyFromSource(preloadSource); + const prevPreloadKey = getKeyFromSource(prevPreloadSource); + + if (this.props.setMedia !== prevProps.setMedia) { + this.clearMedia(prevProps); + this.setMedia(); + } + + if (source) { + if (key === prevPreloadKey && preloadKey !== prevPreloadKey) { + // if there's source and it was the preload source + + // if the preloaded video didn't error, notify VideoPlayer it is ready to reset + if (this.preloadLoadStart) { + forward("onLoadStart", this.preloadLoadStart, this.props); + } + + // emit onUpdate to give VideoPlayer an opportunity to updates its internal state + // since it won't receive the onLoadStart or onError event + forward("onUpdate", { type: "onUpdate" }, this.props); + + this.autoPlay(); + } else if (key !== prevKey) { + // if there's source and it has changed. + this.autoPlay(); + } + } + + if (preloadSource && preloadKey !== prevPreloadKey) { + this.preloadLoadStart = null; + + // In the case that the previous source equalled the previous preload (causing the + // preload video node to not be created) and then the preload source was changed, we + // need to guard against accessing the preloadVideo node. + if (this.preloadAudio) { + this.preloadAudio.load(); + } + } + } + + componentWillUnmount() { + this.clearMedia(); + } + + handlePreloadLoadStart = (ev) => { + // persist the event so we can cache it to re-emit when the preload becomes active + ev.persist(); + this.preloadLoadStart = ev; + + // prevent the from bubbling to upstream handlers + ev.stopPropagation(); + }; + + clearMedia({ setMedia } = this.props) { + if (setMedia) { + setMedia(null); + } + } + + setMedia({ setMedia } = this.props) { + if (setMedia) { + setMedia(this.audio); + } + } + + autoPlay() { + if (!this.props.autoPlay) return; + + this.audio.play(); + } + + setAudioRef = (node) => { + this.audio = node; + this.setMedia(); + }; + + setPreloadRef = (node) => { + if (node) { + node.load(); + } + this.preloadAudio = node; + }; + + render() { + const { preloadSource, source, mediaComponent, ...rest } = this.props; + + delete rest.setMedia; + + const sourceKey = getKeyFromSource(source); + let preloadKey = getKeyFromSource(preloadSource); + + // prevent duplicate components by suppressing preload when sources are the same + if (sourceKey === preloadKey) { + preloadKey = null; + } + + return ( + + {sourceKey ? ( + + } + /> + ) : null} + {preloadKey ? ( + + ) + } + /> + ) : null} + + ); + } +}; + +const AudioDecorator = compose( + ForwardRef({ prop: "setMedia" }), + Slottable({ slots: ["source", "preloadSource"] }) +); + +/** + * Provides support for more advanced video configurations for `VideoPlayer`. + * + * Custom Video Tag + * + * ``` + * + * + * + * ``` + * + * Preload Video Source + * + * ``` + * + * + * + * ``` + * + * @class Video + * @mixes ui/Slottable.Slottable + * @memberof sandstone/VideoPlayer + * @ui + * @public + */ +const Audio = AudioDecorator(AudioBase); +Audio.defaultSlot = "audioComponent"; + +export default Audio; +export { Audio }; diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AudioPlayer.module.less b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AudioPlayer.module.less new file mode 100644 index 0000000..ee1975d --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AudioPlayer.module.less @@ -0,0 +1,351 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +// VideoPlayer.module.less +// +@import "../../../styles/variables.less"; +@import "../../../styles/mixins.less"; +@import "../../../styles/skin.less"; + +.audioPlayer { + // Set by counting the IconButtons inside the side components. + --liftDistance: 0px; + + overflow: hidden; + + .audio { + height: 100%; + width: 100%; + } + + .preloadVideo { + display: none; + } + + .overlay { + position: absolute; + .position(0); + + &::after { + content: ""; + position: absolute; + .position(auto, 0, 0, 0); + height: 720px; + transform-origin: bottom; + // Fancier gradient for future reference. Keeping linear-gradient as specified from Enyo. + // background-image: radial-gradient(rgba(0, 0, 0, 0) 50%, #000000 100%); + // background-size: 170% 200%; + // background-position: bottom center; + } + } + + .back { + position: absolute; + top: 43px; + right: 67px; + } + + .fullscreen { + .miniFeedback { + position: absolute; + z-index: 50; // Value assigned as part of the VideoPlayer API so layers may be inserted in-between + top: @sand-video-feedback-mini-position-top; + left: @sand-video-feedback-mini-position-left; + font-family: @sand-video-feedback-mini-font-family; + font-weight: @sand-video-feedback-mini-font-weight; + font-style: @sand-video-feedback-mini-font-style; + font-size: @sand-video-feedback-mini-font-size; + padding: 0 @sand-video-feedback-mini-padding-side; + line-height: @sand-video-feedback-mini-line-height; + border-radius: (@sand-video-feedback-mini-line-height / 2); + bottom: auto; + pointer-events: none; + } + + .bottom { + position: absolute; + z-index: 100; // Value assigned as part of the VideoPlayer API so layers may be inserted in-between + bottom: @sand-video-player-padding-bottom; + left: @sand-video-player-padding-side; + right: @sand-video-player-padding-side; + + &.lift { + transform: translateY(~"calc(var(--liftDistance) * -1)"); + transition: transform 0.3s linear; + } + + &.hidden { + pointer-events: none; + + .sliderContainer { + position: absolute; + width: 100%; + } + } + + .infoFrame { + display: flex; + margin-bottom: @sand-spotlight-outset; + + // Title Container + .titleFrame { + --infoComponentsOffset: 0; + + @badges-present-transition: transform 500ms ease-in-out; + + position: relative; + flex-grow: 1; + opacity: 1; + .margin-start-end(0, @sand-spotlight-outset); + + &.hidden { + transition: opacity 1000ms ease-in-out; + opacity: 0; + } + + // Title + .title { + position: absolute; + bottom: -10px; + width: 100%; + .sand-text-base(@sand-video-player-title-size); + line-height: @sand-heading-title-line-height; + transition: @badges-present-transition; + .font-kerning; + + &.infoVisible { + transform: translateY(~"calc(var(--infoComponentsOffset) * -1)") + translateZ(0); + } + + .sand-locale-non-latin({ + // The font-size .times is the same as .title in non-latin locales so no + // vertical adjustment is required to align their baselines + bottom: 0; + }); + } + + // Badges and title components + .infoComponents { + vertical-align: super; + font-size: 54px; + + &.hidden { + opacity: 0; + } + &.visible { + transition: opacity 500ms ease-in-out; + } + + > * { + display: inline-block; + margin: 0 12px; + } + + .badgeTextIcon { + font-family: @sand-font-family-bold; + font-size: @sand-video-player-badge-text-size; + text-align: center; + white-space: nowrap; + display: inline-block; + } + + .fontLgIcons { + font-family: "LG Icons"; + } + } + } + } + + .sliderContainer { + display: flex; + align-items: center; + + :global(.enact-locale-right-to-left) & { + direction: ltr; + } + + .times { + .sand-text-base(@sand-video-player-indicator-text-size); + white-space: nowrap; + flex: 0 1 auto; + + > * { + display: inline-block; + } + + .separator { + padding: 0 1ex; + } + + :global(.enact-locale-right-to-left) & { + direction: ltr; + } + } + + .sliderFrame { + @slider-padding-v: ( + ( + @sand-video-player-slider-tap-area - + @sand-video-player-slider-height + ) / 2 + ); + @slider-padding-h: @sand-video-player-slider-knob-size; + flex: 1 0 auto; + + &.hidden { + will-change: opacity; + opacity: 0; + } + + .mediaSlider { + margin: 0 @sand-video-player-slider-knob-size; + padding: @slider-padding-v 0; + height: (@sand-video-player-slider-height * 2); + + // Add a tap area that extends to the edges of the screen, to make the slider more accessible + &::before { + content: ""; + position: absolute; + .position(0, -(@sand-video-player-padding-side)); + } + + // Grow the knob when the Slider gets spotted + .focus({ + .knob::before { + transform: @sand-translate-center scale(0.75); + opacity: 1; + } + }); + + .spottable({ + &.pressed { + .knob::before { + transform: @sand-translate-center scale(0.75); + opacity: 1; + } + } + }); + } + + // Knob + .knob { + @activate-transition-function: cubic-bezier(0.15, 0.85, 0.6, 1.65); + //@slide-transition-function: cubic-bezier(0.15, 0.85, 0.53, 1.09); + + //-webkit-transition: -webkit-transform @slide-transition-function 0.2s; + //transition: transform @slide-transition-function 0.2s; + + &::before { + width: (@sand-video-player-slider-knob-size * 2); + height: (@sand-video-player-slider-knob-size * 2); + border-width: 0; + border-radius: @sand-video-player-slider-knob-size; + -webkit-transform: @sand-translate-center scale(0.5); + transform: @sand-translate-center scale(0.5); + opacity: 0; + will-change: transform, opacity; + -webkit-transition: -webkit-transform + @activate-transition-function 0.2s, + opacity ease 0.2s; + transition: transform @activate-transition-function 0.2s, + opacity ease 0.2s; + } + } + + &.scrubbing { + .knob { + display: block; + } + } + + // Status Indicator + .status-indicator { + .sand-slider-popup-center { + font-family: @sand-font-family; + font-size: @sand-video-player-indicator-text-size; + line-height: 72px; + position: absolute; // Allows flip-h to work + margin: 0 60px; + } + } + } + } + } + } + + .controlsHandleAbove { + pointer-events: none; + position: absolute; + .position(0, 0, auto, 0); + } + + // Skin colors + .applySkins({ + .fullscreen { + .miniFeedback { + background-color: @sand-video-feedback-mini-bg-color; + color: @sand-video-feedback-mini-text-color; + } + + .bottom { + background-color: @sand-video-player-bottom-bg-color; + + .infoFrame { + text-shadow: @sand-video-player-title-text-shadow; + + // Title Container + .titleFrame { + color: @sand-video-player-title-color; + + .redIcon { + background-color: @sand-video-player-redicon-bg-color; + color: @sand-video-player-redicon-text-color; + } + } + } + + .sliderFrame { + .slider { + .knob { + &::before { + background-color: @sand-video-player-slider-knob-color; + } + } + } + + // Status Indicator + .status-indicator { + .sand-slider-popup-center { + color: @sand-video-player-indicator-text-color; + text-shadow: @sand-video-player-title-text-shadow; + + .sand-icon { + color: inherit; + } + } + } + } + } + } + + .overlay { + &.high-contrast-scrim::after { + background: @sand-video-player-high-contrast-scrim-gradient-color + } + } + }); +} diff --git a/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AudioPlayerBase.js b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AudioPlayerBase.js new file mode 100644 index 0000000..3b398ff --- /dev/null +++ b/ref-apps/com.reference.app.musicplayer/src/components/AudioPlayer/AudioPlayerBase/AudioPlayerBase.js @@ -0,0 +1,1740 @@ +// Copyright (c) 2021 LG Electronics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Provides Goldstone-themed video player components. + * + * @module goldstone/VideoPlayer + * @exports Video + * @exports VideoPlayer + * @exports VideoPlayerBase + * @exports MediaControls + */ +import ApiDecorator from "@enact/core/internal/ApiDecorator"; +import { on, off } from "@enact/core/dispatcher"; +import { memoize } from "@enact/core/util"; +import { + adaptEvent, + call, + forKey, + forward, + forwardWithPrevent, + handle, + preventDefault, + stopImmediate, + returnsTrue, +} from "@enact/core/handle"; +import { is } from "@enact/core/keymap"; +import { platform } from "@enact/core/platform"; +import EnactPropTypes from "@enact/core/internal/prop-types"; +import { Job } from "@enact/core/util"; +import { I18nContextDecorator } from "@enact/i18n/I18nDecorator"; +import { toUpperCase } from "@enact/i18n/util"; +import Spotlight from "@enact/spotlight"; +import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator"; +import { Spottable } from "@enact/spotlight/Spottable"; +import ComponentOverride from "@enact/ui/ComponentOverride"; +import { FloatingLayerDecorator } from "@enact/ui/FloatingLayer"; +import { FloatingLayerContext } from "@enact/ui/FloatingLayer/FloatingLayerDecorator"; +import Slottable from "@enact/ui/Slottable"; +import Touchable from "@enact/ui/Touchable"; +import DurationFmt from "ilib/lib/DurationFmt"; +import equals from "ramda/src/equals"; +import classNames from "classnames"; +import PropTypes from "prop-types"; +import React, { + useState, + useEffect, + useCallback, + useContext, + useRef, + memo, +} from "react"; +import ReactDOM from "react-dom"; +import Skinnable from "@enact/sandstone/Skinnable"; +import Spinner from "@enact/sandstone/Spinner"; +import Button from "@enact/sandstone/Button"; +import { calcNumberValueOfPlaybackRate } from "./util"; +import Overlay from "./Overlay"; +import Media from "./Media"; +import MediaControls from "./MediaControls"; +import MediaTitle from "./MediaTitle"; +import MediaSlider from "./MediaSlider"; +import Times from "./Times"; +import Audio from "./Audio"; +import css from "./AudioPlayer.module.less"; +import AlbumInfo from "./AlbumInfo"; + +const SpottableDiv = Touchable(Spottable("div")); +const RootContainer = memo( + SpotlightContainerDecorator( + { + enterTo: "default-element", + defaultElement: [`.${css.controlsHandleAbove}`, `.${css.controlsFrame}`], + }, + "div" + ) +); + +const ControlsContainer = memo( + SpotlightContainerDecorator( + { + enterTo: "", + straightOnly: true, + }, + "div" + ) +); + +const memoGetDurFmt = memoize( + (/* locale */) => + new DurationFmt({ + length: "medium", + style: "clock", + useNative: false, + }) +); + +const getDurFmt = (locale) => { + if (typeof window === "undefined") return null; + + return memoGetDurFmt(locale); +}; + +const forwardWithState = (type) => + adaptEvent(call("addStateToEvent"), forwardWithPrevent(type)); + +// provide forwarding of events on media controls +const forwardControlsAvailable = forward("onControlsAvailable"); +const forwardPlay = forward("onPlay"); +const forwardPause = forwardWithState("onPause"); + +// Function to get previous value from the state +const usePrevious = (value) => { + const ref = useRef(); + useEffect(() => { + ref.current = value; + }); + return ref.current; +}; + +/** + * A player for audio + * + * @class AudioPlayerBase + * @ui + * @public + */ +function AudioPlayerBase({ + autoCloseTimeout, + className, + feedbackHideDelay, + disabled, + infoComponents, + jumpBy, + loading, + locale, + mediaControlsComponent, + miniFeedbackHideDelay, + noAutoPlay, + noAutoShowMediaControls, + noMediaSliderFeedback, + noSlider, + noSpinner, + onBack, + onJumpBackward, + onJumpForward, + seekDisabled, + selection, + spotlightDisabled, + spotlightId, + style, + thumbnailSrc, + title, + titleHideDelay, + videoComponent: VideoComponent, + ...mediaProps +}) { + /* eslint no-use-before-define: ["error", { "variables": false }]*/ + let handleSpotlightUpFromSlider = null; + const titleRef = useRef(null); + const player = useRef({}); + const playbackRate = useRef(null); + // Internal State + const audio = useRef(null); + const prevCommand = useRef(noAutoPlay ? "pause" : "play"); + const speedIndex = useRef(0); + const id = Math.random().toString(36).substr(2, 8); + const sliderScrubbing = useRef(); + const sliderKnobProportion = useRef(0); + const pulsedPlaybackRate = useRef(); + const pulsedPlaybackState = useRef(); + const mediaControlsSpotlightId = useRef(`${spotlightId}_mediaControls`); + const showMiniFeedback = useRef(false); + const [state, setSettings] = useState({ + currentTime: 0, + duration: 0, + error: false, + loading: false, + paused: noAutoPlay, + playbackRate: 1, + + // Non-standard state computed from properties + bottomControlsRendered: true, + feedbackAction: "idle", + feedbackVisible: false, + infoVisible: false, + mediaControlsVisible: true, + mediaSliderVisible: true, + miniFeedbackVisible: false, + proportionLoaded: 0, + proportionPlayed: 0, + sourceUnavailable: false, + titleVisible: true, + }); + + const prevStateRef = usePrevious(state); + const prevPropsRef = usePrevious(arguments[0]); + const context = useContext(FloatingLayerContext); + const floatingLayerController = useRef(); + const hideFeedback = () => { + if (state.feedbackVisible && state.feedbackAction !== "focus") { + setSettings((prevState) => ({ + ...prevState, + feedbackVisible: false, + feedbackAction: "idle", + })); + } + }; + + const hideTitle = () => { + setSettings((prevState) => ({ ...prevState, titleVisible: false })); + }; + + const stopDelayedFeedbackHide = useCallback(() => { + new Job(hideFeedback).stop(); + }, [hideFeedback]); + + const stopDelayedTitleHide = useCallback(() => { + new Job(hideTitle).stop(); + }, [hideTitle]); + + const doAutoClose = () => { + stopDelayedFeedbackHide(); + stopDelayedTitleHide(); + setSettings((prevState) => ({ + ...prevState, + feedbackVisible: false, + mediaControlsVisible: false, + mediaSliderVisible: + prevState.mediaSliderVisible && prevState.miniFeedbackVisible, + infoVisible: false, + })); + }; + + const autoCloseJob = new Job(doAutoClose); + const startAutoCloseTimeout = useCallback(() => { + // If state.more is used as a reference for when this function should fire, timing for + // detection of when "more" is pressed vs when the state is updated is mismatched. Using an + // instance variable that's only set and used for this express purpose seems cleanest. + if (autoCloseTimeout && state.mediaControlsVisible) { + // autoCloseJob.startAfter(autoCloseTimeout); + } + }, [autoCloseTimeout, state.mediaControlsVisible]); + + const activityDetected = useCallback(() => { + startAutoCloseTimeout(); + }, [startAutoCloseTimeout]); + + const startDelayedFeedbackHide = useCallback(() => { + if (feedbackHideDelay) { + new Job(hideFeedback).startAfter(feedbackHideDelay); + } + }, [feedbackHideDelay, hideFeedback]); + + const startDelayedTitleHide = useCallback(() => { + if (titleHideDelay) { + new Job(hideTitle).startAfter(titleHideDelay); + } + }, [hideTitle, titleHideDelay]); + + /** + * Shows media controls. + * + * @function + * @memberof goldstone/VideoPlayer.VideoPlayerBase.prototype + * @public + */ + const showControls = useCallback(() => { + if (disabled) { + return; + } + startDelayedFeedbackHide(); + startDelayedTitleHide(); + setSettings((prevState) => ({ + ...prevState, + bottomControlsRendered: true, + feedbackAction: "idle", + feedbackVisible: true, + mediaControlsVisible: true, + mediaSliderVisible: true, + miniFeedbackVisible: false, + titleVisible: true, + })); + }, [disabled, startDelayedFeedbackHide, startDelayedTitleHide, state]); + + const showControlsFromPointer = () => { + Spotlight.setPointerMode(false); + showControls(); + }; + + const handleGlobalKeyDown = handle( + returnsTrue(activityDetected), + forKey("down"), + () => + !state.mediaControlsVisible && + !Spotlight.getCurrent() && + Spotlight.getPointerMode() && + !spotlightDisabled, + preventDefault, + stopImmediate, + showControlsFromPointer + ); + + const showFeedback = useCallback(() => { + if (state.mediaControlsVisible) { + setSettings((prevState) => ({ ...prevState, feedbackVisible: true })); + } else { + const shouldShowSlider = + pulsedPlaybackState.current !== null || + calcNumberValueOfPlaybackRate(playbackRate.current) !== 1; + + if ( + showMiniFeedback.current && + (!state.miniFeedbackVisible || + state.mediaSliderVisible !== shouldShowSlider) + ) { + setSettings((prevState) => ({ + ...prevState, + mediaSliderVisible: shouldShowSlider && !noMediaSliderFeedback, + miniFeedbackVisible: !( + prevState.loading || + !prevState.duration || + prevState.error + ), + })); + } + } + }, [ + noMediaSliderFeedback, + state.mediaControlsVisible, + state.mediaSliderVisible, + state.miniFeedbackVisible, + ]); + + /** + * Set the media playback time index + * + * @function + * @memberof goldstone/VideoPlayer.VideoPlayerBase.prototype + * @param {Number} timeIndex - Time index to seek + * @public + */ + const seek = useCallback( + (timeIndex) => { + if ( + !seekDisabled && + !isNaN(audio.current.duration) && + !state.sourceUnavailable + ) { + audio.current.currentTime = timeIndex; + } else { + forward("onSeekFailed", {}, arguments[0]); + } + }, + [seekDisabled, state.sourceUnavailable] + ); + + const hideMiniFeedback = () => { + if (state.miniFeedbackVisible) { + showMiniFeedback.current = false; + setSettings((prevState) => ({ + ...prevState, + mediaSliderVisible: false, + miniFeedbackVisible: false, + })); + } + }; + + const hideMiniFeedbackJob = new Job(hideMiniFeedback); + + const startDelayedMiniFeedbackHide = useCallback( + (delay = miniFeedbackHideDelay) => { + if (delay) { + hideMiniFeedbackJob.startAfter(delay); + } + }, + [hideMiniFeedbackJob, miniFeedbackHideDelay] + ); + + /** + * Step a given amount of time away from the current playback position. + * Like [seek]{@link goldstone/VideoPlayer.VideoPlayer#seek} but relative. + * + * @function + * @memberof goldstone/VideoPlayer.VideoPlayerBase.prototype + * @param {Number} distance - Time value to jump + * @public + */ + const jump = useCallback( + (distance) => { + if (state.sourceUnavailable) { + return; + } + + pulsedPlaybackRate.current = toUpperCase( + new DurationFmt({ length: "long" }).format({ second: jumpBy }) + ); + pulsedPlaybackState.current = + distance > 0 ? "jumpForward" : "jumpBackward"; + showFeedback(); + startDelayedFeedbackHide(); + seek(state.currentTime + distance); + startDelayedMiniFeedbackHide(); + }, + [ + jumpBy, + seek, + showFeedback, + startDelayedFeedbackHide, + startDelayedMiniFeedbackHide, + state.currentTime, + state.sourceUnavailable, + ] + ); + + const stopDelayedMiniFeedbackHide = useCallback(() => { + hideMiniFeedbackJob.stop(); + }, [hideMiniFeedbackJob]); + + const clearPulsedPlayback = () => { + pulsedPlaybackRate.current = null; + pulsedPlaybackState.current = null; + }; + + const stopAutoCloseTimeout = useCallback(() => { + autoCloseJob.stop(); + }, [autoCloseJob]); + + const renderBottomControl = new Job(() => { + if (!state.bottomControlsRendered) { + setSettings((prevState) => ({ + ...prevState, + bottomControlsRendered: true, + })); + } + }); + + useEffect(() => { + on("mousemove", activityDetected); + if (platform.touch) { + on("touchmove", activityDetected); + } + document.addEventListener("keydown", handleGlobalKeyDown, { + capture: true, + }); + document.addEventListener("wheel", activityDetected, { capture: true }); + startDelayedFeedbackHide(); + if (context && typeof context === "function") { + floatingLayerController.current = context(() => {}); + } + + // Cleanup + return () => { + off("mousemove", activityDetected); + if (platform.touch) { + off("touchmove", activityDetected); + } + document.removeEventListener("keydown", handleGlobalKeyDown, { + capture: true, + }); + document.removeEventListener("wheel", activityDetected, { + capture: true, + }); + stopAutoCloseTimeout(); + stopDelayedTitleHide(); + stopDelayedFeedbackHide(); + stopDelayedMiniFeedbackHide(); + renderBottomControl.stop(); + if (floatingLayerController.current) { + floatingLayerController.current.unregister(); + } + }; + }, [ + activityDetected, + context, + handleGlobalKeyDown, + renderBottomControl, + startDelayedFeedbackHide, + stopAutoCloseTimeout, + stopDelayedFeedbackHide, + stopDelayedMiniFeedbackHide, + stopDelayedTitleHide, + ]); + + const getHeightForElement = (elementName) => { + const element = player.current.querySelector(`.${css[elementName]}`); + if (element) { + return element.offsetHeight; + } else { + return 0; + } + }; + + useEffect(() => { + if ( + titleRef.current && + state.infoVisible && + (!prevStateRef.infoVisible || + !equals(infoComponents, prevPropsRef.infoComponents)) + ) { + titleRef.current.style.setProperty( + "--infoComponentsOffset", + `${getHeightForElement("infoComponents")}px` + ); + } + + if ( + (!state.mediaControlsVisible && + prevStateRef && + prevStateRef.mediaControlsVisible !== state.mediaControlsVisible) || + (!state.mediaSliderVisible && + prevStateRef && + prevStateRef.mediaSliderVisible !== state.mediaSliderVisible) + ) { + if (typeof floatingLayerController.current !== "undefined") { + floatingLayerController.current.notify({ action: "closeAll" }); + } + } + + if ( + spotlightId !== prevPropsRef && + prevPropsRef && + prevPropsRef.spotlightId + ) { + mediaControlsSpotlightId.current = `${spotlightId}_mediaControls`; + } + + if ( + !state.mediaControlsVisible && + prevStateRef && + prevStateRef.mediaControlsVisible + ) { + forwardControlsAvailable({ available: false }, arguments[0]); + stopAutoCloseTimeout(); + + if (!spotlightDisabled) { + // If last focused item were in the media controls or slider, we need to explicitly + // blur the element when MediaControls hide. See ENYO-5648 + const current = Spotlight.getCurrent(); + const bottomControls = document.querySelector(`.${css.bottom}`); + if (current && bottomControls && bottomControls.contains(current)) { + current.blur(); + } + // when in pointer mode, the focus call below will only update the last focused for + // the video player and not set the active container to the video player which will + // cause focus to land back on the media controls button when spotlight restores + // focus. + if (Spotlight.getPointerMode()) { + Spotlight.setActiveContainer(spotlightId); + } + // Set focus to the hidden spottable control - maintaining focus on available spottable + // controls, which prevents an additional 5-way attempt in order to re-show media controls + Spotlight.focus(`.${css.controlsHandleAbove}`); + } + } else if ( + state.mediaControlsVisible && + prevStateRef && + !prevStateRef.mediaControlsVisible + ) { + forwardControlsAvailable({ available: true }, arguments[0]); + startAutoCloseTimeout(); + if (!spotlightDisabled) { + const current = Spotlight.getCurrent(); + if (!current || player.current.contains(current)) { + // Set focus within media controls when they become visible. + Spotlight.focus(mediaControlsSpotlightId.current); + } + } + } + // Once video starts loading it queues bottom control render until idle + if ( + state.bottomControlsRendered && + prevStateRef && + !prevStateRef.bottomControlsRendered && + !state.mediaControlsVisible + ) { + showControls(); + } + }, [ + arguments, + infoComponents, + prevPropsRef, + prevStateRef, + showControls, + spotlightDisabled, + spotlightId, + startAutoCloseTimeout, + state.bottomControlsRendered, + state.infoVisible, + state.mediaControlsVisible, + state.mediaSliderVisible, + stopAutoCloseTimeout, + ]); + + const preventTimeChange = useCallback( + (time) => { + const isTimeBeyondSelection = () => { + // if selection isn't set or only contains the starting value, there isn't a valid selection + // with which to test the time + if (selection != null && selection.length >= 2) { + const [start, end] = selection; + + return time > end || time < start; + } + + return false; + }; + + return ( + isTimeBeyondSelection(time) && + !forwardWithPrevent( + "onSeekOutsideSelection", + { type: "onSeekOutsideSelection", time }, + arguments[0] + ) + ); + }, + [selection] + ); + + /** + * Hides media controls. + * + * @function + * @memberof goldstone/VideoPlayer.VideoPlayerBase.prototype + * @public + */ + const hideControls = useCallback(() => { + stopDelayedFeedbackHide(); + stopDelayedMiniFeedbackHide(); + stopDelayedTitleHide(); + stopAutoCloseTimeout(); + setSettings((prevState) => ({ + ...prevState, + feedbackAction: "idle", + feedbackVisible: false, + mediaControlsVisible: false, + mediaSliderVisible: false, + miniFeedbackVisible: false, + infoVisible: false, + })); + }, [ + state, + stopAutoCloseTimeout, + stopDelayedFeedbackHide, + stopDelayedMiniFeedbackHide, + stopDelayedTitleHide, + ]); + + // only show mini feedback if playback controls are invoked by a key event + const shouldShowMiniFeedback = (ev) => { + if (ev.type === "keyup") { + showMiniFeedback.current = true; + } + return true; + }; + + const handleLoadStart = () => { + prevCommand.current = noAutoPlay ? "pause" : "play"; + speedIndex.current = 0; + setSettings((prevState) => ({ + ...prevState, + currentTime: 0, + sourceUnavailable: true, + proportionPlayed: 0, + proportionLoaded: 0, + })); + + if (!noAutoShowMediaControls) { + if (!state.bottomControlsRendered) { + renderBottomControl.idle(); + } else { + showControls(); + } + } + }; + + const handleEndedEvent = () => { + forward("onEnded", {}, arguments[0]); + }; + + const handlePlay = handle(forwardPlay, shouldShowMiniFeedback, () => play()); + + const handlePause = handle(forwardPause, shouldShowMiniFeedback, () => + pause() + ); + + const handleJump = useCallback( + ({ keyCode }) => { + if (seekDisabled) { + forward("onSeekFailed", {}, arguments[0]); + } else { + const jumpByLeft = (is("left", keyCode) ? -1 : 1) * jumpBy; + const time = Math.min( + state.duration, + Math.max(0, state.currentTime + jumpByLeft) + ); + + if (preventTimeChange(time)) return; + + showMiniFeedback.current = true; + jump(jumpByLeft); + } + }, + [ + seekDisabled, + jumpBy, + state.duration, + state.currentTime, + preventTimeChange, + jump, + locale, + ] + ); + + // + // Media Interaction Methods + // + const handleEvent = () => { + const el = audio.current; + const updatedState = { + // Standard media properties + currentTime: el.currentTime, + duration: el.duration, + paused: el.playbackRate !== 1 || el.paused, + playbackRate: el.playbackRate, + + // Non-standard state computed from properties + error: el.error, + loading: el.loading, + proportionLoaded: el.proportionLoaded, + proportionPlayed: el.proportionPlayed || 0, + // note: `el.loading && state.sourceUnavailable == false` is equivalent to `oncanplaythrough` + sourceUnavailable: (el.loading && state.sourceUnavailable) || el.error, + }; + + // If there's an error, we're obviously not loading, no matter what the readyState is. + if (updatedState.error) updatedState.loading = false; + if ( + mediaProps.pauseAtEnd && + (el.currentTime === 0 || el.currentTime === el.duration) + ) { + pause(); + } + + setSettings((prevState) => { + if ( + typeof prevStateRef !== "undefined" && + !prevStateRef.miniFeedbackVisible && + prevStateRef.miniFeedbackVisible === state.miniFeedbackVisible && + !prevStateRef.mediaSliderVisible && + prevStateRef.mediaSliderVisible === state.mediaSliderVisible && + prevStateRef.loading === state.loading && + prevPropsRef.loading === loading && + (prevState.currentTime !== updatedState.currentTime || + prevState.proportionPlayed !== updatedState.proportionPlayed) + ) { + return prevState; + } + return { + ...prevState, + ...updatedState, + }; + }); + }; + + /** + * The primary means of interacting with the `