It's very simple and Calibre makes it much less painful. Just add the following lines as Extra CSS to Calibre and then convert whatever Chinese text to ePub. This works for both traditional and simplified Chinese.
<@font-face {
font-family: "Arial", "SimSun", "Times New Roman", "MingLiU", "PMingLiU", "Cambria", "Consolas", "DroidFont", serif, sans-serif;
font-weight: normal;
font-style: normal;
src: url('res:///system/fonts/DroidSansFallback.ttf');
}
@font-face {
font-family: "Arial", "SimSun", "Times New Roman", "MingLiU", "PMingLiU", "Cambria", "Consolas", "DroidFont", serif, sans-serif;
font-weight: bold;
font-style: normal;
src: url('res:///system/fonts/DroidSansFallback.ttf');
}
@font-face {
font-family: "Arial", "SimSun", "Times New Roman", "MingLiU", "PMingLiU", "Cambria", "Consolas", "DroidFont", serif, sans-serif;
font-weight: normal;
font-style: italic;
src: url('res:///system/fonts/DroidSansFallback.ttf');
}
@font-face {
font-family: "Arial", "SimSun", "Times New Roman", "MingLiU", "PMingLiU", "Cambria", "Consolas", "DroidFont", serif, sans-serif;
font-weight: bold;
font-style: italic;
src: url('res:///system/fonts/DroidSansFallback.ttf');
}
Have fun of reading Chinese ePub!
I try your method, the ePub still show square, what other steps I need to do before it will show chinese.
I figure it out, I need to turn on the Publisher default under text.
jillpacker said:
I try your method, the ePub still show square, what other steps I need to do before it will show chinese.
Click to expand...
Click to collapse
Where did you get your ePub files from? The easiest way is to convert your ePub to ePub again with Calibre.
For example the Chinese ePub downloaded from Gutenberg site is not readable on Nook Tablet straight off Gutenberg. After conversion with the Extra CSS setting in Calibre all the 'blocks' are turned into Chinese characters.
jillpacker said:
I figure it out, I need to turn on the Publisher default under text.
Click to expand...
Click to collapse
Have fun and enjoy it!
Related
xxxxxxxxxxxxxxxx
[deleted]
@mods: Please close or even better delete!
.wysiwyg { background-attachment: scroll; background-repeat: repeat; background-position: 0% 0%; background-color: #ffffcc; background-image: none; color: #000000; font-family: Verdana, Arial, Arial; font-style: normal; font-variant: normal; font-weight: 400; font-size: 10pt; line-height: normal } p { margin: 0px; }
yep, sorry, deleted my post.
You can run the if/show command to control the display of a pop-up window based on the stack component and the position:fixed CSS file. The following code implements two custom pop-up windows:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
1. Initialize data models.
data() {
return {
list: [1, 2, 3],
isShow: false,
ifShow1: false,
ifShow2: false
}
},
2. Set the page layout.
<text onclick="showToast1">Custom pop-up window 1</text>
<text onclick="showToast2">Custom pop-up window 2</text>
<!-- Pop-up window -->
<div if="{{isShow}}" class="modal center-modal">
<div class="toast-box password-box">
<block>
<stack>
<div style="flex-direction: column;width: 100%;" if="{{ifShow1}}">
<div style="width: 100%;">
<text style="font-size: 32px;font-weight: 500;">Traffic violation</text>
<text style="font-size: 25px;text-align: right;padding-left: 400px" onclick="hide">Hide</text>
</div>
<list style="height: 550px">
<list-item type="list" for="list" style="height: 160px;margin-top: 20px">
<div style="padding-top: 1px;">
<image src="/Common/icon.png" style="border-radius: 100%;width: 100px;height: 100px;"></image>
<div style="flex-direction: column; margin-left: 20px">
<text style="font-size: 28px;font-weight: 600;">Level-2 title</text>
<text style="font-size: 26px;margin-top: 10px">Displaying product attributes, prices, release dates, and more.</text>
<div style="height: 2px;background-color:#808080;bottom: 0;margin-top: 25px" if="{{$idx<2}}">
</div>
</div>
</div>
</list-item>
</list>
</div>
<div style="flex-direction: column;width: 100%;" if="{{ifShow2}}">
<text style="font-size: 31px;font-weight: 700;">City services</text>
<text style="font-weight: 400;"><span>By tapping Agree, you agree to the above terms as well as the </span><a style="color: #0000ff;" onclick="toast">XX User Agreement</a>
<span> and </span><a style="color: #0000ff;" onclick="toast">Privacy Statement About XX.</a></text>
<div style="width: 100%;">
<text style="width: 50%; text-align: center; color: #0000ff;" onclick="hide">OK</text>
<text style="width: 50%; text-align: center; color: #0000ff;" onclick="hide">Cancel</text>
</div>
</div>
</stack>
</block>
</div>
</div>
3. Set the page style.
.container {
flex-direction: column;
justify-content: center;
align-items: center;
.body {
width: 100%;
height: 100%;
flex-direction: column;
align-items: center;
.img {
margin-top: 500px;
}
.txt {
margin-top: 570px;
}
}
// Pop-up window style.
.modal {
position: fixed;
flex-direction: column;
justify-content: flex-end;
width: 100%;
height: 100%;
padding: 0 34px 34px;
background-color: rgba(0, 0, 0, 0.18);
animation-name: Modal;
animation-duration: 130ms;
animation-timing-function: ease-in;
.toast-box {
width: 100%;
padding: 25px 50px 20.8px;
border-radius: 34px;
background-color: white;
flex-direction: column;
.toast-title {
font-size: 41.6px;
line-height: 56px;
font-weight: 500;
font-family: HWtext-65ST;
color: #000000;
}
.toast-tip {
font-family: HWtext-55ST;
font-size: 29px;
color: rgba(0, 0, 0, 0.6);
line-height: 40px;
margin-top: 3.6px;
margin-bottom: 25px;
}
.label-box {
height: 100px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
.label {
color: #000000;
width: 100%;
font-family: HWtext-55ST;
font-size: 34px;
}
input {
margin-right: -11px;
}
}
.manage {
height: 100px;
font-family: HWtext-65ST;
font-size: 29.12px;
color: #007dff;
font-weight: 500;
}
.cancel {
width: 100%;
height: 75px;
margin-top: 37.44px;
text-align: center;
font-family: HWtext-65ST;
font-size: 33.28px;
color: #007dff;
font-weight: 500;
}
.btn-box {
justify-content: space-between;
align-items: center;
.bind {
width: 47%;
height: 60px;
border-radius: 10px;
font-family: HWtext-65ST;
text-align: center;
font-size: 33.28px;
color: #007dff;
margin: 16px 0;
font-weight: 500;
}
.line {
height: 50px;
width: 1px;
background-color: rgba(0, 0, 0, 0.2);
}
.quit {
width: 47%;
height: 60px;
border-radius: 10px;
text-align: center;
font-family: HWtext-65ST;
font-size: 33.28px;
color: #007dff;
margin: 16px 0;
font-weight: 500;
}
.bind:active {
background-color: rgba(0, 0, 0, 0.1);
}
.quit:active {
background-color: rgba(0, 0, 0, 0.1);
}
}
.nocar-tip {
height: 99.84px;
font-size: 33.28px;
font-family: HWtext-55ST;
color: #000000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
}
.password-box {
margin-top: -110px;
padding: 30.63px 50px 20.8px;
.toast-title {
height: 55.64px;
margin-bottom: 30.12px;
}
.password-tip {
flex-direction: column;
margin-top: 3.6px;
margin-bottom: 25px;
.control-tip {
font-family: HWtext-55ST;
font-size: 24.96px;
color: rgba(0, 0, 0, 0.6);
}
}
.error-password-tip {
font-family: HWtext-55ST;
font-size: 24.96px;
color: #fa2a2d;
height: 32.76px;
margin-bottom: 37.4px;
margin-top: 0;
}
.btn-box {
.bind {
margin-top: 8px;
margin-bottom: 8px;
}
.quit {
margin-top: 8px;
margin-bottom: 8px;
}
}
.eyes-input-box {
width: 100%;
flex-direction: row;
justify-content: flex-end;
.input-password {
width: 100%;
height: 94.64px;
border: 1px solid #ffffff;
border-bottom-color: #000000;
padding: 11.44px 0;
margin-bottom: 20.8px;
}
.password-text {
width: 100%;
height: 94.64px;
padding: 11.44px 0;
border-bottom: 1px solid #000000;
margin-bottom: 20.8px;bind:active {
color: #000000;
font-size: 36px;
}
.eyes-img {
width: 49.92px;
height: 49.92px;
margin-top: 23px;
}
}
.password-loading-box {
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 150px;
.loading-text {
font-family: HWtext-55ST;
font-size: 33.28px;
color: rgba(0, 0, 0, 0.96);
}
.loading-circular {
width: 102px;
height: 102px;
color: #666666;
}
}
}
}
}
4. Bind an event.
showToast1() {
this.isShow = true;
this.ifShow1 = true
this.ifShow2 = false
},
showToast2() {
this.isShow = true;
this.ifShow1 = false
this.ifShow2 = true
},
hide(){
this.isShow = false;
},
toast() {
prompt.showToast({
message: 'View privacy agreement'
})
}
How can I read the global variables defined in app.ux from a JavaScript file?
For example, You can define the variable AppData in app.ux.
data: {
AppData: '123456',
}
The code in the JavaScript file is as follows:
export default {
getAppData() {
return getApp().$def.data.AppData
}
}
For more information, please refer to the Common methods section in Script.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Article Introduction
In this article we will develop Qibla finder application for Huawei Smart Watch device using Huawei DevEco Studio (HarmonyOS). We will fetch Location and Compass API’s of HarmonyOS JS language to develop Application.
1. Create New Project
Let’s create Smart Watch Project and choosing ability template, Wearable and Empty Feature Ability (JS)
Define project name, package name and relevant directory where you want to save your project.
2. Preparing Files and Permission
Let’s first add images and permissions which we will use for project.
All project images will be under common/images folder, check below screenshot.
Next we need to add permissions for Internet and Location under config.json file.
JSON:
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.LOCATION",
"reason": "get Qibla direction",
"usedScene": {
"ability": [
"default"
],
"when": "always"
}
}
]
3. Compass Development
In Compass screen development we will cover Location permission, Location fetching, location error and compass degree value.
Let’s start development without wasting more time.
Styling:
index.css:
CSS:
.stack {
width: 454px;
height: 454px;
justify-content: center;
background-color: whitesmoke;
}
.container {
background-color: whitesmoke;
}
.compass-container {
display: flex;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 454px;
height: 454px;
background-color: whitesmoke;
}
.container-location-loading{
background-image: url('/common/images/background.png');
flex-direction: column;
padding-top: 0px;
padding-bottom: 0px;
height: 456px;
width: 456px;
}
.error-container {
background-image: url('/common/images/background.png');
padding-top: 0px;
padding-bottom: 0px;
height: 456px;
width: 100%;
}
.location_loading{
object-fit:contain;
height: 456px;
width: 292px;
}
.column{
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
}
.needle{
left: 0px;
top: 0px;
width: 220px;
height: 220px;
background-color: transparent;
z-index:99;
}
.compass, .compass2{
left: 0px;
top: 0px;
width: 220px;
height: 220px;
background-color: transparent;
z-index:98;
}
.qibla_style{
display: flex;
left: 0px;
top: 0px;
width: 220px;
height: 220px;
background-color: transparent;
z-index:100000;
}
.error_title{
display: flex;
color: red;
width: 100%;
height: 100%;
justify-content: center;
align-content: center;
text-align: center;
font-size: 24px;
}
.button-container{
display: flex;
flex-direction: row;
justify-content: center;
align-content: space-around;
position: absolute;
bottom: 10px;
}
Layout:
Location Loading Animation:
Code:
<stack if="{{isLocationLoading === true && isLocationError === false}}" class="container-location-loading">
<image src="../../common/images/location_animation.gif" class="location_loading" />
</stack>
Location Loading Output:
Location Error and Retry:
Code:
<stack if="{{isLocationLoading === false && isLocationError === true}}" class="error-container">
<text class="error_title">Location not fetch. Try again</text>
<div class="button-container">
<button type="circle" icon="/common/images/exit.png" onclick="exit"></button>
<button type="circle" icon="/common/images/refresh.png" onclick="retry"></button>
</div>
</stack>
Location Error Output:
Compass UI:
Code:
<stack class="compass-container" if="{{isLocationLoading === false && isLocationError === false}}">
<stack class="compass" style="transform:{{compass_transform}}">
<div class="needle" style="transform:{{needle_transform}}">
<image src="/common/images/needle.png"></image>
</div>
<div class="compass" >
<image src="/common/images/compass.png"></image>
</div>
</stack>
<div class="qibla_style">
<image src="/common/images/qibla.png"></image>
</div>
</stack>
Compass UI Output:
JS code:
Structural - Code:
Code:
import sensor from '@system.sensor';
import brightness from '@system.brightness';
import geolocation from '@system.geolocation';
import app from '@system.app';
export default {}
Data:
Code:
data: {
compass_transform: "rotate(0deg)",
needle_transform: "rotate(0deg)",
isLocationLoading: true,
isLocationError: false,
}
Common - Code:
Code:
onReady() {
this.setBrightnessKeepScreenOn();
},
onInit() {
this.initLocationCompass();
},
exit(){
app.terminate();
},
retry(){
this.isLocationLoading = true;
this.isLocationError = false;
this.initLocationCompass();
},
onDestroy() {
sensor.unsubscribeCompass();
},
// Setting the screen to be steady on
setBrightnessKeepScreenOn: function () {
brightness.setKeepScreenOn({
keepScreenOn: true,
success: function () {
console.log("handling set keep screen on success")
},
fail: function (data, code) {
console.log("handling set keep screen on fail, code:" + code);
}
});
},
onBackPress() {
sensor.unsubscribeCompass();
}
Location Fetching - Code:
Code:
initLocationCompass(){
var _this = this;
this.locationLoading().then(result => {
console.info("Location: " + result);
_this.loadCompass(result);
_this.isLocationLoading = false;
_this.isLocationError = false;
}, error => {
console.info("Location: error ->" + error);
_this.isLocationLoading = false;
_this.isLocationError = true;
});
},
locationLoading() {
return new Promise(function (resolve, reject) {
return geolocation.getLocation({
success: function (data) {
console.log('success get location data. latitude:' + data.latitude + 'long:' + data.longitude);
return resolve({
latitude: data.latitude,
longitude: data.longitude
});
},
fail: function (data, code) {
console.log('fail to get location. code:' + code + ', data:' + data);
return reject({
error: 'fail to get location. code:' + code + ', data:' + data
});
},
});
});
},
Compass - Code:
Code:
loadCompass(coordinates = {
latitude: 24.7136,
longitude: 46.6753
}) {
var _this = this;
var qiblaDirection = parseFloat(this.qibla(coordinates));
console.log("compass: qiblaDirection: " + qiblaDirection);
_this.needle_transform = "rotate(" + qiblaDirection + "deg)";
sensor.subscribeCompass({
success: function (ret) {
var direction = ret.direction;
var compassDirection = (360 - direction);
_this.compass_transform = "rotate(" + compassDirection + "deg)";
console.log("compass: compassDirection: " + direction);
},
fail: function (data, code) {
console.error('compass: subscribe compass fail, code: ' + code + ', data: ' + data);
},
});
},
Qibla Direction - Code:
Code:
qibla(coordinates) {
const makkah = {
latitude: 21.42252,
longitude: 39.82621
};
const term1 = Math.sin(this.degreesToRadians(makkah.longitude) - this.degreesToRadians(coordinates.longitude));
const term2 = Math.cos(this.degreesToRadians(coordinates.latitude)) * Math.tan(this.degreesToRadians(makkah.latitude));
const term3 = Math.sin(this.degreesToRadians(coordinates.latitude)) * Math.cos(this.degreesToRadians(makkah.longitude) - this.degreesToRadians(coordinates.longitude));
const angle = Math.atan2(term1, term2 - term3);
return this.unwindAngle(this.radiansToDegrees(angle));
},
degreesToRadians(degrees) {
return degrees * Math.PI / 180.0;
},
radiansToDegrees(radians) {
return radians * 180.0 / Math.PI;
},
normalizeToScale(number, max) {
return number - max * Math.floor(number / max);
},
unwindAngle(angle) {
return this.normalizeToScale(angle, 360.0);
},
Compass Screen Notes:
To manage Layout, we need to use <stack> to overlap UI component in layers. First we need to fetch location data and identify the Qibla direction in degree. And call Compass JS API to get compass degree and manage the UI accordingly.
4. Result
Tips and Tricks:
Requesting some data from internet, you must need to add Internet permission in config.json file.
Fetching Location data, you must need to add Internet Permission in config.json file.
Use Dev Eco Studio Previewer to check the screen layout and design. Previewer is developer friendly to Hot release changes on fly.
For better management of big application it’s a good practice to centralize you common scripts and common style in common folder. Add images folder for complete application images.
In JS script when you make some variable, in callback functions you can store the reference of this to some variable and then call reference variable. Like var _this = this.
References:
HarmonyOS JS API Official Documentation: click here
Geographic Location Documentation: click here
Sensor Documentation: click here
Conclusion:
Developers can able to fetch user location data and compass data to make Qibla finder application. While developing application for HarmonyOS developer can get benefit for both JS and JAVA language.
Original Source
The promt.showDialog API of Quick App can be used to implement simple pop-up elements such as text and buttons, but cannot achieve complex display effects. UI designers will often design complex effects, so how do we actually achieve them as engineers?
For example, the design of a pop-up includes a list, in which images are displayed on the left and descriptions need to be shown on the right, as shown in Figure 1 below. Another pop-up instructs users to check the app’s privacy statement via a link, as shown in Figure 2.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Figure 1 Pop-up with a list Figure 2 Pop-up with a link
Proper arrangement of quick app elements and style attributes can help achieve such complex effects.
SolutionYou can use the stack element with the position: fixed CSS style to implement a custom pop-up. Whether to display the pop-up can be determined using the if/show command. Sample code is as follows:
1.Initialize data models:
HTML:
data() {
return {
list: [1, 2, 3],
isShow: false,
ifShow1: false,
ifShow2: false
}
},
2.Page layout:
HTML:
<text onclick="showToast1">Custom pop-up window 1</text>
<text onclick="showToast2">Custom pop-up window 2</text>
<!-- Pop-up window -->
<div if="{{isShow}}" class="modal center-modal">
<div class="toast-box password-box">
<block>
<stack>
<div style="flex-direction: column;width: 100%;" if="{{ifShow1}}">
<div style="width: 100%;">
<text style="font-size: 32px;font-weight: 500;">Traffic violation</text>
<text style="font-size: 25px;text-align: right;padding-left: 400px" onclick="hide">Hide</text>
</div>
<list style="height: 550px">
<list-item type="list" for="list" style="height: 160px;margin-top: 20px">
<div style="padding-top: 1px;">
<image src="/Common/icon.png" style="border-radius: 100%;width: 100px;height: 100px;"></image>
<div style="flex-direction: column; margin-left: 20px">
<text style="font-size: 28px;font-weight: 600;">Level-2 title</text>
<text style="font-size: 26px;margin-top: 10px">Displaying product attributes, prices, release dates, and more.</text>
<div style="height: 2px;background-color:#808080;bottom: 0;margin-top: 25px" if="{{$idx<2}}">
</div>
</div>
</div>
</list-item>
</list>
</div>
<div style="flex-direction: column;width: 100%;" if="{{ifShow2}}">
<text style="font-size: 31px;font-weight: 700;">City services</text>
<text style="font-weight: 400;"><span>By tapping Agree, you agree to the above terms as well as the </span><a style="color: #0000ff;" onclick="toast">XX User Agreement</a>
<span> and </span><a style="color: #0000ff;" onclick="toast">Privacy Statement About XX.</a></text>
<div style="width: 100%;">
<text style="width: 50%; text-align: center; color: #0000ff;" onclick="hide">OK</text>
<text style="width: 50%; text-align: center; color: #0000ff;" onclick="hide">Cancel</text>
</div>
</div>
</stack>
</block>
</div>
</div>
Page style:
CSS:
.container {
flex-direction: column;
justify-content: center;
align-items: center;
.body {
width: 100%;
height: 100%;
flex-direction: column;
align-items: center;
.img {
margin-top: 500px;
}
.txt {
margin-top: 570px;
}
}
// Pop-up style
.modal {
position: fixed;
flex-direction: column;
justify-content: flex-end;
width: 100%;
height: 100%;
padding: 0 34px 34px;
background-color: rgba(0, 0, 0, 0.18);
animation-name: Modal;
animation-duration: 130ms;
animation-timing-function: ease-in;
.toast-box {
width: 100%;
padding: 25px 50px 20.8px;
border-radius: 34px;
background-color: white;
flex-direction: column;
.toast-title {
font-size: 41.6px;
line-height: 56px;
font-weight: 500;
font-family: HWtext-65ST;
color: #000000;
}
.toast-tip {
font-family: HWtext-55ST;
font-size: 29px;
color: rgba(0, 0, 0, 0.6);
line-height: 40px;
margin-top: 3.6px;
margin-bottom: 25px;
}
.label-box {
height: 100px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
.label {
color: #000000;
width: 100%;
font-family: HWtext-55ST;
font-size: 34px;
}
input {
margin-right: -11px;
}
}
.manage {
height: 100px;
font-family: HWtext-65ST;
font-size: 29.12px;
color: #007dff;
font-weight: 500;
}
.cancel {
width: 100%;
height: 75px;
margin-top: 37.44px;
text-align: center;
font-family: HWtext-65ST;
font-size: 33.28px;
color: #007dff;
font-weight: 500;
}
.btn-box {
justify-content: space-between;
align-items: center;
.bind {
width: 47%;
height: 60px;
border-radius: 10px;
font-family: HWtext-65ST;
text-align: center;
font-size: 33.28px;
color: #007dff;
margin: 16px 0;
font-weight: 500;
}
.line {
height: 50px;
width: 1px;
background-color: rgba(0, 0, 0, 0.2);
}
.quit {
width: 47%;
height: 60px;
border-radius: 10px;
text-align: center;
font-family: HWtext-65ST;
font-size: 33.28px;
color: #007dff;
margin: 16px 0;
font-weight: 500;
}
.bind:active {
background-color: rgba(0, 0, 0, 0.1); // To be modified in the high-fidelity design.
}
.quit:active {
background-color: rgba(0, 0, 0, 0.1); // To be modified in the high-fidelity design.
}
}
.nocar-tip {
height: 99.84px;
font-size: 33.28px;
font-family: HWtext-55ST;
color: #000000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
}
.password-box {
margin-top: -110px;
padding: 30.63px 50px 20.8px;
.toast-title {
height: 55.64px;
margin-bottom: 30.12px;
}
.password-tip {
flex-direction: column;
margin-top: 3.6px;
margin-bottom: 25px;
.control-tip {
font-family: HWtext-55ST;
font-size: 24.96px;
color: rgba(0, 0, 0, 0.6);
}
}
.error-password-tip {
font-family: HWtext-55ST;
font-size: 24.96px;
color: #fa2a2d;
height: 32.76px;
margin-bottom: 37.4px;
margin-top: 0;
}
.btn-box {
.bind {
margin-top: 8px;
margin-bottom: 8px;
}
.quit {
margin-top: 8px;
margin-bottom: 8px;
}
}
.eyes-input-box {
width: 100%;
flex-direction: row;
justify-content: flex-end;
.input-password {
width: 100%;
height: 94.64px;
border: 1px solid #ffffff;
border-bottom-color: #000000;
padding: 11.44px 0;
margin-bottom: 20.8px;
}
.password-text {
width: 100%;
height: 94.64px;
padding: 11.44px 0;
border-bottom: 1px solid #000000;
margin-bottom: 20.8px;
color: #000000;
font-size: 36px;
}
.eyes-img {
width: 49.92px;
height: 49.92px;
margin-top: 23px;
}
}
.password-loading-box {
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 150px;
.loading-text {
font-family: HWtext-55ST;
font-size: 33.28px;
color: rgba(0, 0, 0, 0.96);
}
.loading-circular {
width: 102px;
height: 102px;
color: #666666;
}
}
}
}
}
For more information,please refer to the following:
Style of Quick App
Quick App materials
Common settings of Quick App
Is it supports in Xamarin?
ask011 said:
Is it supports in Xamarin?
Click to expand...
Click to collapse
Sorry. It supported in quick app. It is different from Xamarin. If you want to implement custom pop up in xamarin, you can try to study other tutorials perhaps.
Hope it works for Hormony os as well.
shikkerimath said:
Hope it works for Hormony os as well.
Click to expand...
Click to collapse
The JS UI framework supports custom components for you to extend existing components based on your application services. you can implement by following the document.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Article Introduction
In this article we will develop Prayer Times application for Huawei Smart Watch device using Huawei DevEco Studio (HarmonyOS). We will fetch Location using HarmonyOS JS language API’s and use some of the npm libraries (adhan, moment, moment-timezone, tz-lookup) to develop complete Real world Prayer Times Calculation Application.
1. Create New Project
Let’s create Smart Watch Project and choosing ability template, Empty Ability (JS)
Define project name, package name and relevant directory where you want to save your project. Choose the Device type “wearable” for which we are developing the application.
2. Preparing Files and Permission
Let’s first add images and permissions which we will use for project.
All project images will be under common/images folder, check below screenshot.
Next we need to add Location and Internet permissions under config.json file.
Code:
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.LOCATION",
"reason": "get user location to show prayer time",
"usedScene": {
"ability": [
"default"
],
"when": "always"
}
}
]
3. NPM libraries installation
We need to install following NPM libraries in the application:
adhan
moment
moment-timezone
tz-lookup
First we need to open the terminal under our DevEco studio project.
We need to change directory to entry folder.
Code:
cd entry
Now we need to install all the required libraries for our project.
Code:
npm i adhan moment moment-timezone tz-lookup -s
After installation our package.json file look like below:
Code:
{
"dependencies": {
"adhan": "^4.1.0",
"moment": "^2.29.1",
"moment-timezone": "^0.5.33",
"tz-lookup": "^6.1.25"
}
}
4. Prayer Time App Development
In Prayer time screen development we will cover Location permission, Location fetching, location error layout, prayer timer screen and today all prayers dialog screen.
Let’s start development without wasting more time.
Styling:
index.css: (Common screen styling)
Code:
/* common styling */
.container {
background-color: black;
justify-content: center;
}
.container-sub {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
padding-top: 24px;
}
.container-location-loading {
flex-direction: column;
padding-top: 0px;
padding-bottom: 0px;
height: 456px;
width: 456px;
}
.column {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
background-color: transparent;
}
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 80%;
height: 25px;
background-color: transparent;
}
.title {
text-align: center;
display: flex;
font-size: 16px;
}
.center {
text-align: center;
}
.location_loading {
object-fit: contain;
height: 456px;
width: 240px;
text-align: center;
align-items: center;
}
.current_time {
font-size: 18px;
text-align: center;
}
.mosque {
margin-top: 5px;
text-align: center;
fit-original-size: true;
}
.prayer_name {
text-align: center;
font-size: 16px;
margin-top: 2px;
margin-bottom: 5px;
}
.remaining_timer {
text-align: center;
font-size: 14px;
}
.button-circle {
background-color: transparent;
}
index.css: (Prayer BG & Color styling)
Code:
/* prayer BG & Color */
.prayer_bg {
background-position: top center;
background-size: 100% 280px;
}
.fajr_bg {
background-image: url('/common/images/prayer_bg/fajr.jpg');
}
.fajr_color {
background-color: #30170d;
}
.dhuhr_bg {
background-image: url('/common/images/prayer_bg/dhuhr.jpg');
}
.dhuhr_color {
background-color: #021823;
}
.asr_bg {
background-image: url('/common/images/prayer_bg/asr.jpg');
}
.asr_color {
background-color: #172B34;
}
.maghrib_bg {
background-image: url('/common/images/prayer_bg/maghrib.jpg');
}
.maghrib_color {
background-color: #010101;
}
.isha_bg {
background-image: url('/common/images/prayer_bg/isha.jpg');
}
.isha_color {
background-color: #082C44;
}
.night_bg {
background-image: url('/common/images/prayer_bg/night.jpg');
}
.night_color {
background-color: #131C39;
}
index.css: (Dialog styling)
Code:
/*Dialog styling*/
.dialog-main {
width: 100%;
}
.dialog-div {
display: flex;
flex-direction: column;
align-items: center;
}
.inner-txt {
width: 100%;
height: 300px;
flex-direction: column;
align-items: center;
}
.inner-btn {
width: 100%;
height: 154px;
align-items: center;
}
index.css: (List styling)
Code:
/*list styling*/
.list-wrapper {
width: 100%;
flex-direction: column;
}
.list-items {
width: 100%;
flex-direction: column;
padding: 0 24px;
}
.item-wrapper {
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
height: 34px;
margin: 8px 0;
}
.item-icon-wrapper {
width: 24px;
}
.item-icon {
width: 24px;
height: 24px;
object-fit: contain;
}
.item-name-description-wrapper {
flex-direction: column;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-shrink: 1;
width: 50%;
margin-right: 24px;
margin-left: 24px;
}
.item-name {
text-align: left;
color: #DBFFFFFF;
font-size: 16px;
}
.item-description {
text-align: left;
opacity: 0.75;
color: #99FFFFFF;
font-size: 14px;
}
.item-right-part-wrapper {
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.item-right-text {
margin-right: 4px;
margin-left: 8px;
font-size: 14px;
opacity: 0.75;
}
.item-right-arrow {
width: 12px;
height: 24px;
object-fit: contain;
}
.line {
stroke-width: 1px;
width: 100%;
background-color: #33FFFFFF;
margin-left: 40px;
}
index.css: (Birds animation styling)
Code:
/* Birds animation */
.birds_animation {
object-fit: scale-down;
position: absolute;
top: 0px;
left: -200px;
animation-name: Fly;
animation-duration: 15s;
animation-timing-function: ease;
animation-iteration-count: infinite;
}
@keyframes Fly {
from {
transform: translateX(-200px);
}
to {
transform: translateX(1000px);
}
}
Layout:
Index.hml: (Location Loading Animation)
Code:
<div if="{{ isLocationLoading === true }}" class="container-location-loading">
<image src="common/images/location_animation.gif" class="location_loading"/>
</div>
Index.hml: (Location Loading Output):
Index.hml: (Location Error & Retry)
Code:
<div class="column" if="{{ isLocationLoading === false && isLocationError === true }}">
<text class="title">Location not fetch, please try again later.</text>
</div>
Index.hml: (Prayer timer UI)
Code:
<div class="container-sub prayer_bg {{ prayer_bg }}" if="{{ isLocationLoading === false && isLocationError === false }}">
<image src="common/images/birds.gif" class="birds_animation"></image>
<text class="current_time">{{ currentTime }}</text>
<image class="mosque" src="common/images/mosque.png"></image>
<text class="prayer_name">{{nextPrayer}} {{nextPrayerTime}}</text>
<text if="{{isShowTargetTime}}" class="remaining_timer">{{nextPrayerRemaining}}</text>
<button type="circle" class="button-circle"
ontouchend="showPrayer" icon="common/images/down-arrow.png"></button>
</div>
Index.hml: (Prayer timer UI Output)
Index.hml: (Dialog all Prayer times)
Code:
<dialog id="simpledialog" class="dialog-main">
<div class="dialog-div {{ dialog_bg }}">
<button type="circle" class="button-circle"
ontouchend="closePrayer" icon="common/images/close.png"></button>
<div class="inner-txt">
<div class="prayers-list">
<div class="list-items-left">
<list class="list-wrapper" initialindex="{{ initial_index_value }}">
<block for="{{ prayer_data }}">
<list-item class="list-items" @click="changeList($idx)" id="{{ $idx }}">
<div class="item-wrapper">
<div class="item-icon-wrapper">
<image class="item-icon" src="{{ $item.item_icon }}"></image>
</div>
<div class="item-name-description-wrapper">
<text class="item-name">{{ $item.item_name }}</text>
<text class="item-description">{{ $item.item_description }}</text>
</div>
<div class="item-right-part-wrapper">
<image class="item-right-arrow" src="common/images/right_arrow_dark_mode.png"></image>
</div>
</div>
<div class="divider-line">
<divider class="line"></divider>
</div>
</list-item>
</block>
</list>
</div>
</div>
</div>
</div>
</dialog>
Index.hml: (Dialog all Prayer times Ouput)
Index.hml: (Complete code)
Code:
<div class="container {{ (isLocationLoading === false) ? 'column' : '' }}">
<div if="{{ isLocationLoading === true }}" class="container-location-loading">
<image src="common/images/location_animation.gif" class="location_loading"/>
</div>
<div class="column" if="{{ isLocationLoading === false && isLocationError === true }}">
<text class="title">Location not fetch, please try again later.</text>
</div>
<div class="container-sub prayer_bg {{ prayer_bg }}" if="{{ isLocationLoading === false && isLocationError === false }}">
<image src="common/images/birds.gif" class="birds_animation"></image>
<text class="current_time">{{ currentTime }}</text>
<image class="mosque" src="common/images/mosque.png"></image>
<text class="prayer_name">{{nextPrayer}} {{nextPrayerTime}}</text>
<text if="{{isShowTargetTime}}" class="remaining_timer">{{nextPrayerRemaining}}</text>
<button type="circle" class="button-circle"
ontouchend="showPrayer" icon="common/images/down-arrow.png"></button>
</div>
<dialog id="simpledialog" class="dialog-main">
<div class="dialog-div {{ dialog_bg }}">
<button type="circle" class="button-circle"
ontouchend="closePrayer" icon="common/images/close.png"></button>
<div class="inner-txt">
<div class="prayers-list">
<div class="list-items-left">
<list class="list-wrapper" initialindex="{{ initial_index_value }}">
<block for="{{ prayer_data }}">
<list-item class="list-items" @click="changeList($idx)" id="{{ $idx }}">
<div class="item-wrapper">
<div class="item-icon-wrapper">
<image class="item-icon" src="{{ $item.item_icon }}"></image>
</div>
<div class="item-name-description-wrapper">
<text class="item-name">{{ $item.item_name }}</text>
<text class="item-description">{{ $item.item_description }}</text>
</div>
<div class="item-right-part-wrapper">
<image class="item-right-arrow" src="common/images/right_arrow_dark_mode.png"></image>
</div>
</div>
<div class="divider-line">
<divider class="line"></divider>
</div>
</list-item>
</block>
</list>
</div>
</div>
</div>
</div>
</dialog>
</div>
JS code:
index.js: (Structural - Code)
Code:
import geolocation from '@system.geolocation';
import adhan from 'adhan';
import moment from 'moment';
import tz from 'moment-timezone';
var tzlookup = require("tz-lookup");
const TAG = 'app_log [index]';
export default {}
index.js: (Data - Code)
Code:
data: {
config: {
isTesting: true,
locationCoordinates: {
"latitude": 24.65382908421087,
"longitude": 46.73552629355017
},
timeZone: "Asia/Riyadh",
fakeDateTime: "2021-06-12 18:13:01"
},
prayer_data: [
{
item_id: "fajr",
item_icon: "common/images/prayer_icon/fajr.png",
item_name: 'Fajr',
item_description: ''
},
{
item_id: "dhuhr",
item_icon: "common/images/prayer_icon/dhuhr.png",
item_name: 'Dhuhr',
item_description: ''
},
{
item_id: "asr",
item_icon: "common/images/prayer_icon/asr.png",
item_name: 'Asr',
item_description: ''
},
{
item_id: "maghrib",
item_icon: "common/images/prayer_icon/maghrib.png",
item_name: 'Maghrib',
item_description: ''
},
{
item_id: "isha",
item_icon: "common/images/prayer_icon/isha.png",
item_name: 'Isha',
item_description: ''
},
],
defaultPrayerSetting: {
allowNotification: false,
prayerSetting: {
Madhab: 'Shafi',
calculationMethod: 'UmmAlQura',
adjustments: {
fajr: "0",
sunrise: "0",
dhuhr: "0",
asr: "0",
maghrib: "0",
isha: "0"
}
}
},
initial_index_value: 2,
isLocationError: false,
isLocationLoading: true,
locationCoordinates: null,
currentTime: null,
timeUpdateTimer: null,
nextPrayer: 'Night',
nextPrayerTime: '',
nextPrayerRemaining: '',
isShowTargetTime: true,
date: moment().toDate(),
prayer_bg: "night_bg",
dialog_bg: "night_color"
},
index.js: (Common - Code)
Code:
onInit() {
console.log(TAG + 'onInit');
if(this.config.isTesting === true){
this.locationCoordinates = this.config.locationCoordinates
moment.tz.setDefault(this.config.timeZone);
this.date = moment(this.config.fakeDateTime).toDate();
}
this.currentTime = moment(this.date).format('ddd LT');
this.timeUpdateTimer = setInterval(this.updateTimer, 2000);
},
onReady() {
console.log(TAG + 'onReady');
var _this = this;
if (this.locationCoordinates !== null) {
setTimeout(() => {
_this.calculatePrayerTime();
_this.isLocationLoading = false;
_this.isLocationError = false;
}, 4000);
} else {
this.locationLoading().then(result => {
_this.locationCoordinates = result;
console.info(TAG + "Location: " + result);
_this.calculatePrayerTime();
_this.isLocationLoading = false;
_this.isLocationError = false;
}, error => {
console.info(TAG + "Location: error ->" + error);
_this.isLocationLoading = false;
_this.isLocationError = true;
});
}
},
onShow() {
console.log(TAG + 'onShow');
},
onDestroy() {
console.log(TAG + 'onDestroy');
clearInterval(this.timeUpdateTimer);
this.timeUpdateTimer = null;
clearInterval(this.countDownTimer);
this.countDownTimer = null;
},
updateTimer() {
this.currentTime = moment().format('ddd LT');
if(this.config.isTesting === true){
this.currentTime = moment(this.config.fakeDateTime).format('ddd LT');
}
},
index.js: (Dialog - Code)
Code:
showPrayer(e) {
this.$element('simpledialog').show();
},
closePrayer(e) {
this.$element('simpledialog').close();
},
index.js: (Location Fetching - Code)
Code:
locationLoading() {
return new Promise(function (resolve, reject) {
return geolocation.getLocation({
success: function (data) {
console.log('success get location data. latitude:' + data.latitude + 'long:' + data.longitude);
return resolve({
latitude: data.latitude,
longitude: data.longitude
});
},
fail: function (data, code) {
console.log('fail to get location. code:' + code + ', data:' + data);
return reject({
error: 'fail to get location. code:' + code + ', data:' + data
});
},
});
});
},
index.js: (Prayer times - Code)
Code:
calculatePrayerTime() {
var _this = this;
var prayerSettings = this.defaultPrayerSetting;
console.log(TAG + 'prayer_setting: getPrayerSetting() ' + JSON.stringify(prayerSettings));
if (prayerSettings !== null) {
this.prayerSettings = prayerSettings;
var params = this.getPrayerParameter(this.prayerSettings);
var coordinates = new adhan.Coordinates(_this.locationCoordinates.latitude, _this.locationCoordinates.longitude);
var date = this.date;
var prayerTimes = new adhan.PrayerTimes(coordinates, date, params);
console.info(TAG + 'locationCoordinates ' + JSON.stringify(_this.locationCoordinates));
var timezone = tzlookup(_this.locationCoordinates.latitude, _this.locationCoordinates.longitude)
if(this.config.isTesting === true){
timezone = this.config.timeZone
}
console.log(TAG + "timezone: " + timezone);
var nextPrayer = prayerTimes.nextPrayer(date);
var currentPrayer = prayerTimes.currentPrayer(date);
console.info(TAG + 'nextPrayer ' + nextPrayer);
console.info(TAG + 'currentPrayer ' + currentPrayer);
if (nextPrayer.toString() === "none") {
_this.isShowTargetTime = false
_this.nextPrayer = "Night";
_this.managePrayerTime(prayerTimes, timezone, nextPrayer, currentPrayer)
} else {
_this.isShowTargetTime = true
_this.nextPrayer = nextPrayer;
var nextPrayerTime = prayerTimes.timeForPrayer(nextPrayer);
_this.nextPrayerTime = moment(nextPrayerTime).tz(timezone).format('h:mm A');
_this.setTimeInfo(nextPrayerTime.getTime());
_this.managePrayerTime(prayerTimes, timezone, nextPrayer, currentPrayer)
}
}
},
managePrayerTime(prayerTimes, timezone, nextPrayer, currentPrayer) {
var _this = this;
var fajrTime = moment(prayerTimes.fajr).tz(timezone).format('h:mm A');
var sunriseTime = moment(prayerTimes.sunrise).tz(timezone).format('h:mm A');
var dhuhrTime = moment(prayerTimes.dhuhr).tz(timezone).format('h:mm A');
var asrTime = moment(prayerTimes.asr).tz(timezone).format('h:mm A');
var maghribTime = moment(prayerTimes.maghrib).tz(timezone).format('h:mm A');
var ishaTime = moment(prayerTimes.isha).tz(timezone).format('h:mm A');
_this.prayer_data.map(item => {
if (item.item_id === "fajr") {
item.item_description = fajrTime;
}
if (item.item_id === "dhuhr") {
item.item_description = dhuhrTime;
}
if (item.item_id === "asr") {
item.item_description = asrTime;
}
if (item.item_id === "maghrib") {
item.item_description = maghribTime;
}
if (item.item_id === "isha") {
item.item_description = ishaTime;
}
if (nextPrayer.toString().toLowerCase() === item.item_id) {
_this.prayer_bg = item.item_id + "_bg";
_this.dialog_bg = item.item_id + "_color";
}
});
},
getPrayerParameter(prayerSettings) {
var params = adhan.CalculationMethod.UmmAlQura();
var prayerSetting = prayerSettings.prayerSetting;
if (prayerSetting.calculationMethod === 'MuslimWorldLeagueMuslimWorldLeague') {
params = adhan.CalculationMethod.MuslimWorldLeague();
} else if (prayerSetting.calculationMethod === 'Egyptian') {
params = adhan.CalculationMethod.Egyptian();
} else if (prayerSetting.calculationMethod === 'Karachi') {
params = adhan.CalculationMethod.Karachi();
} else if (prayerSetting.calculationMethod === 'Dubai') {
params = adhan.CalculationMethod.Dubai();
} else if (prayerSetting.calculationMethod === 'MoonsightingCommittee') {
params = adhan.CalculationMethod.MoonsightingCommittee();
} else if (prayerSetting.calculationMethod === 'NorthAmerica') {
params = adhan.CalculationMethod.NorthAmerica();
} else if (prayerSetting.calculationMethod === 'Kuwait') {
params = adhan.CalculationMethod.Kuwait();
} else if (prayerSetting.calculationMethod === 'Qatar') {
params = adhan.CalculationMethod.Qatar();
} else if (prayerSetting.calculationMethod === 'Singapore') {
params = adhan.CalculationMethod.Singapore();
} else if (prayerSetting.calculationMethod === 'Other') {
params = adhan.CalculationMethod.Other();
}
if (prayerSetting.Madhab === 'Shafi') {
params.madhab = adhan.Madhab.Shafi;
} else {
params.madhab = adhan.Madhab.Hanafi;
}
params.adjustments.fajr = parseInt(prayerSetting.adjustments.fajr) || 0;
params.adjustments.sunrise = parseInt(prayerSetting.adjustments.sunrise) || 0;
params.adjustments.dhuhr = parseInt(prayerSetting.adjustments.dhuhr) || 0;
params.adjustments.asr = parseInt(prayerSetting.adjustments.asr) || 0;
params.adjustments.maghrib = parseInt(prayerSetting.adjustments.maghrib) || 0;
params.adjustments.isha = parseInt(prayerSetting.adjustments.isha) || 0;
return params;
},
index.js: (Count down timer - Code)
Code:
setTimeInfo(next_time) {
console.log(TAG + "next_time: " + next_time);
this.CaculateTime(next_time);
this.countDownTimer = setInterval(() => {
this.CaculateTime(next_time);
}, 1000);
},
CaculateTime(timeObj) {
var myDate = new Date();
if (this.config.isTesting === true) {
this.date = moment(this.date).add(500, 'milliseconds').toDate();
myDate = this.date;
}
let currentTime = myDate.getTime();
var targetTime = parseInt(timeObj);
var remainTime = parseInt(targetTime - currentTime);
if (remainTime > 0) {
this.isShowTargetTime = true;
this.setRemainTime(remainTime);
//this.setTargetTime(targetTime);
}
},
setRemainTime(remainTime) {
let days = this.addZero(Math.floor(remainTime / (24 * 3600 * 1000))); // Calculate the number of days
let leavel = remainTime % (24 * 3600 * 1000); // Time remaining after counting days
let hours = this.addZero(Math.floor(leavel / (3600 * 1000))); // Calculate the number of hours remaining
let leavel2 = leavel % (3600 * 1000); // Number of milliseconds remaining after calculating the remaining hours
let minutes = this.addZero(Math.floor(leavel2 / (60 * 1000))); // Calculate the number of minutes remaining
// Calculate the difference seconds.
let leavel3 = leavel2 % (60 * 1000); // Number of milliseconds remaining after minutes are calculated
let seconds = this.addZero(Math.round(leavel3 / 1000));
this.nextPrayerRemaining = hours + ':' + minutes + ':' + seconds;
},
setTargetTime(targetTime) {
var _this = this
var times = new Date(targetTime);
let date = times.toLocaleDateString(); //Gets the current date
var tempSetHours = times.getHours(); //Gets the current number of hours (0 - 23)
let hours = this.addZero(tempSetHours)
var tempSetMinutes = times.getMinutes(); //Gets the current number of minutes (0 - 59)
let minutes = this.addZero(tempSetMinutes)
var tempSetSeconds = times.getSeconds(); //Gets the current number of seconds (0 - 59)
let seconds = this.addZero(tempSetSeconds)
this.targetTime = `${hours}:${minutes}:${seconds}`;
},
addZero: function (i) {
return i < 10 ? "0" + i : i + "";
},
Prayer time Screen Notes:
To manage different state of application on single screen, we can able to use logic layouts using if=”true/false” or show=”true/false” conditions on containers.
For testing of custom date and time developer need to modify config data variable (isTesting: true).
For production we need to apply isTesting: false and relay on real date and time.
For prayer time parameter we implement adhan npm libraries, developer can have access on prayer time adjustment (plus/minus) in minutes.
For better management of prayer time parameters always use local storage (key/value), to save user preferences in storage and adjust prayer time.
5. Result
Tips & Tricks:
HarmonyOS JS project while installing any NPM libraries, in terminal must be on entry folder of your project module.
For testing of any date and time, developer need to modify config data variable (isTesting: true)
For production or realtime device, developer need to modify config data variable (isTesting: false)
For prayer time adjustment developer can modify defaultPrayerSetting data variable and could store user preference in storage.
Requesting some data from internet, you must need to add Internet permission in config.json file.
Fetching Location data, you must need to add Internet Permission in config.json file.
Use Dev Eco Studio Previewer to check the screen layout and design. Previewer is developer friendly to Hot release changes on fly.
For better management of big application it’s a good practice to centralize you common scripts and common style in common folder. Add images folder for complete application images.
In JS script when you make some variable, in callback functions you can store the reference of this to some variable and then call reference variable. Like var _this = this.
References:
HarmonyOS JS API Official Documentation: https://developer.harmonyos.com/en/docs/documentation/doc-references/js-apis-overview-0000001056361791
Geographic Location Documentation: https://developer.harmonyos.com/en/docs/documentation/doc-references/js-apis-system-location-0000000000626089
Conclusion:
Developers can able to develop real world Prayer time calculation application, while fetch user location data and using npm ready-made libraries. While developing application for HarmonyOS developer can get benefit for both JS and JAVA language. Benefit for developing JS based HarmonyOS application developer can able to use npm based libraries and can reduce development time.
Original Source
Useful sharing, thanks!!
Very useful. Thanks for sharing