UNPKG

@silver-zepp/vis-log

Version:

An on-screen logger for ZeppOS Mini apps.

3 lines (2 loc) 14.8 kB
/** @about Visual Logger 1.5.3 @min_zeppos 2.0 @author: Silver, Zepp Health. @license: MIT */ import*as hmUI from"@zos/ui";import{px}from"@zos/utils";import{getDeviceInfoPlus}from"./helpers/get_device_info_plus";import{log as Logger}from"@zos/utils";export{hmUI};export{px};export const{width:DEVICE_WIDTH,height:DEVICE_HEIGHT}=getDeviceInfoPlus();export{getDeviceInfoPlus};const is_round_screen=getDeviceInfoPlus().shape=="R"?true:false;let logger;const PREFIX_LOG="LOG";const PREFIX_INFO="INFO";const PREFIX_WARN="WARN";const PREFIX_ERR="ERR";const PREFIX_DBG="DBG";const PREFIX_WARN_U="○";const PREFIX_ERR_U="⊗";const PREFIX_INFO_LOG_DBG_U="ⓘ";const DEFAULT_LINE_COUNT=5;let messages_arr=new Array(DEFAULT_LINE_COUNT).fill("");let repeats_arr=new Array(DEFAULT_LINE_COUNT).fill(0);const COLOR_WHITE=16777215;const DEFAULT_BACKGROUND_COLOR=3355443;const DEFAULT_PADDING_MULT=1.5;const DEFAULT_TEXT_SIZE=16;const DEFAULT_TEXT_STYLE=hmUI.text_style.ELLIPSIS;const DEFAULT_INITIAL_TIMEOUT=5e3;const DEFAULT_NEXT_TIMEOUT=2e3;const TEXT_STYLE={h:0,w:DEVICE_WIDTH,color:COLOR_WHITE,text_size:DEFAULT_TEXT_SIZE,align_h:hmUI.align.CENTER_H,text_style:DEFAULT_TEXT_STYLE};const BG_STYLE={x:0,w:DEVICE_WIDTH,color:DEFAULT_BACKGROUND_COLOR};export default class VisLog{#text_size=DEFAULT_TEXT_SIZE;#text_style=DEFAULT_TEXT_STYLE;#text_color=COLOR_WHITE;#background_color=DEFAULT_BACKGROUND_COLOR;#line_count=DEFAULT_LINE_COUNT;#log_from_top=true;#console_log_enabled=true;#prefix_enabled=true;#timeout_enabled=true;#visual_log_enabled=true;#text_widget=null;#background_widget=null;#timer=null;#background_enabled=true;#padding_multiplier=DEFAULT_PADDING_MULT;#margin=0;#is_widgets_created=false;#is_custom_margin=false;#view_container=null;#use_logger=false;#reverse_order=false;#chars_per_line_estimate_factor=.6;#round_screen_width_reduction_factor=.85;#filename=null;#appsettings_preffix="AppSettings";#appside_preffix="AppSide";#replay_preffix="Replay";#appside_init_timeout=2e4;constructor(filename=""){this.#filename=filename;if(!is_round_screen)this.#log_from_top=false;if(this.#viewContainerExists()){this.#createViewContainer()}}log(...args){this.#logWithPrefix(PREFIX_INFO_LOG_DBG_U,PREFIX_LOG,...args)}info(...args){this.#logWithPrefix(PREFIX_INFO_LOG_DBG_U,PREFIX_INFO,...args)}warn(...args){this.#logWithPrefix(PREFIX_WARN_U,PREFIX_WARN,...args)}error(...args){this.#logWithPrefix(PREFIX_ERR_U,PREFIX_ERR,...args)}debug(...args){this.#logWithPrefix(PREFIX_INFO_LOG_DBG_U,PREFIX_DBG,...args)}clear(){messages_arr=new Array(this.#line_count).fill("");repeats_arr=new Array(this.#line_count).fill(0);if(this.#viewContainerExists()){this.#renderText()}}refresh(){if(!this.#viewContainerExists())return;this.#destroyWidgets();this.#createViewContainer();this.#recreateWidgets();this.#renderText()}updateSettings(settings){if(typeof settings.filename==="string"){this.#filename=settings.filename;if(this.#use_logger){logger=Logger.getLogger(settings.filename)}}if(typeof settings.log_from_top==="boolean"){this.#log_from_top=settings.log_from_top}if(typeof settings.console_log_enabled==="boolean"){this.#console_log_enabled=settings.console_log_enabled}if(typeof settings.prefix_enabled==="boolean"){this.#prefix_enabled=settings.prefix_enabled}if(typeof settings.timeout_enabled==="boolean"){this.#timeout_enabled=settings.timeout_enabled}if(typeof settings.visual_log_enabled==="boolean"){this.#visual_log_enabled=settings.visual_log_enabled;if(this.#viewContainerExists()){if(this.#background_widget){this.#background_widget.setProperty(hmUI.prop.VISIBLE,settings.visual_log_enabled&&this.#background_enabled)}if(this.#text_widget){this.#text_widget.setProperty(hmUI.prop.VISIBLE,settings.visual_log_enabled)}}}if(typeof settings.background_enabled==="boolean"){this.#background_enabled=settings.background_enabled;if(this.#viewContainerExists()&&this.#background_widget){this.#background_widget.setProperty(hmUI.prop.VISIBLE,settings.background_enabled&&this.#visual_log_enabled)}}if(typeof settings.text_size==="number"){this.#text_size=settings.text_size}if(typeof settings.text_style==="number"){this.#text_style=settings.text_style}if(typeof settings.text_color==="number"){this.#text_color=settings.text_color}if(typeof settings.background_color==="number"){this.#background_color=settings.background_color}if(typeof settings.line_count==="number"){this.#line_count=settings.line_count;this.#trimArrays()}if(typeof settings.padding_multiplier==="number"){this.#padding_multiplier=settings.padding_multiplier}if(typeof settings.margin==="number"){this.#margin=settings.margin;this.#is_custom_margin=true}if(typeof settings.reverse_order==="boolean"){this.#reverse_order=settings.reverse_order}if(typeof settings.use_logger==="boolean"){this.#use_logger=settings.use_logger;logger=Logger.getLogger(this.#filename)}if(this.#viewContainerExists()){this.#renderText()}}destroy(){if(this.#timer){clearTimeout(this.#timer);this.#timer=null}this.#destroyWidgets();messages_arr=[];repeats_arr=[];this.#is_widgets_created=false;this.#is_custom_margin=false;logger=null}initSideRelay(vm,callback,options={}){if(typeof callback==="object"&&callback!==null){options=callback;callback=undefined}const{method="INIT_VISLOG",timeout=this.#appside_init_timeout,retries=2}=options;let attempt=0;const start_time=Date.now();const try2init=()=>{attempt++;if(vm.request===undefined){this.error("Vis for AppSide & AppSettings requires ZML to work!");return}vm.request({method:method}).then(response=>{const elapsed_ms=Date.now()-start_time;if(callback)callback(null,{response:response,elapsed_ms:elapsed_ms,attempts:attempt})}).catch(error=>{if(attempt<retries){setTimeout(try2init,1e3)}else{const err=new Error(`App-side service init failed after ${retries} attempts: ${error.message}`);this.error(`[${this.#appside_preffix}] ${err.message}`);if(callback)callback(err)}})};const to_id=setTimeout(()=>{const elapsed_ms=Date.now()-start_time;const to_err=new Error("App-side service initialization timeout");this.error(`[${this.#appside_preffix}] ${to_err.message}`);if(callback)callback(to_err,{elapsed_ms:elapsed_ms,attempts:attempt})},timeout);const og_cb=callback;callback=(error,result)=>{clearTimeout(to_id);if(og_cb)og_cb(error,result)};try2init()}handleSideServiceCall(data){if(!data||data.type!=="VIS_DBG_LOG"){return false}this.#displaySideServiceLog(data.data);return true}#displaySideServiceLog(log_entry){if(!log_entry||log_entry===null){this.warn("Received null log entry from settings");return}const{level,message,filename,source,is_replay,timestamp}=log_entry;if(!level||!message){this.warn("Received incomplete log entry:",JSON.stringify(log_entry));return}let prefix;if(is_replay){prefix=this.#replay_preffix}else if(source==="AppSide"){prefix=this.#appside_preffix}else{prefix=this.#appsettings_preffix}const msg=`[${filename||prefix}] ${message}`;switch(level.toUpperCase()){case"ERROR":this.error(msg);break;case"WARN":this.warn(msg);break;case"INFO":this.info(msg);break;case"DEBUG":this.debug(msg);break;case"LOG":default:this.log(msg);break}}#createViewContainer(){return this.#viewContainerExists()}#recreateWidgets(){if(!this.#viewContainerExists())return;if(this.#background_enabled){this.#background_widget=this.#view_container.createWidget(hmUI.widget.FILL_RECT,{...BG_STYLE})}this.#text_widget=this.#view_container.createWidget(hmUI.widget.TEXT,{...TEXT_STYLE});this.#is_widgets_created=true}#logWithPrefix(prefix_visual,prefix_console,...args){let msg_content=args.join(" ");let msg_for_array=msg_content;if(this.#prefix_enabled){msg_for_array=`${prefix_visual} ${msg_content}`}if(this.#shouldEnableVis()){if(!this.#view_container){this.#createViewContainer()}const last_message_index=messages_arr.length>0?messages_arr.length-1:-1;let is_repeated=false;if(last_message_index!==-1){is_repeated=msg_for_array===messages_arr[last_message_index]}if(is_repeated){repeats_arr[repeats_arr.length-1]++}else{messages_arr.push(msg_for_array);repeats_arr.push(1);this.#trimArrays()}if(this.#timeout_enabled){if(!this.#timer){this.#timer=this.#createTimer(DEFAULT_INITIAL_TIMEOUT,0,()=>this.#removeOldestMessage())}else{clearTimeout(this.#timer);this.#timer=this.#createTimer(DEFAULT_INITIAL_TIMEOUT,0,()=>this.#removeOldestMessage())}}if(this.#visual_log_enabled){this.#renderText()}}this.#consoleLog(prefix_console,msg_content)}#getActualMessagesCount(){return messages_arr.filter(msg=>typeof msg==="string"&&msg!=="").length}#removeOldestMessage(){const num_stored_messages=this.#getActualMessagesCount();if(num_stored_messages>0){messages_arr.shift();repeats_arr.shift();if(this.#visual_log_enabled&&this.#viewContainerExists()){this.#renderText()}const remaining_messages=this.#getActualMessagesCount();if(this.#timer){clearTimeout(this.#timer);this.#timer=null}if(remaining_messages>0&&this.#timeout_enabled){this.#timer=this.#createTimer(DEFAULT_NEXT_TIMEOUT,0,()=>this.#removeOldestMessage())}}else{if(this.#timer){clearTimeout(this.#timer);this.#timer=null}}}#trimArrays(){while(messages_arr.length>this.#line_count){messages_arr.shift();repeats_arr.shift()}}#wrapText(text_to_wrap,max_chars){if(max_chars<=0)return[text_to_wrap];const lines=[];let current_line="";const words=String(text_to_wrap).split(" ");for(const word of words){if(word.length===0&&current_line.length===0&&lines.length===0&&words.length===1){lines.push("");break}if(word.length===0&&current_line.length>0){continue}if(current_line.length===0){if(word.length<=max_chars){current_line=word}else{let temp_word=word;while(temp_word.length>max_chars){lines.push(temp_word.substring(0,max_chars));temp_word=temp_word.substring(max_chars)}current_line=temp_word}}else if(current_line.length+1+word.length<=max_chars){current_line+=" "+word}else{lines.push(current_line);if(word.length<=max_chars){current_line=word}else{let temp_word=word;while(temp_word.length>max_chars){lines.push(temp_word.substring(0,max_chars));temp_word=temp_word.substring(max_chars)}current_line=temp_word}}}if(current_line.length>0||lines.length===0&&words.length>0&&words[0].length===0||lines.length===0&&words.length===0){lines.push(current_line)}return lines.length>0?lines:[""]}#renderText(){if(!this.#viewContainerExists())return;let final_text_for_widget="";let total_visual_lines=0;const background_configs=[];const msg2render=this.#reverse_order?messages_arr.slice().reverse():messages_arr.slice();const repeats2render=this.#reverse_order?repeats_arr.slice().reverse():repeats_arr.slice();const effective_screen_width=is_round_screen?DEVICE_WIDTH*this.#round_screen_width_reduction_factor:DEVICE_WIDTH;const avg_char_width_px=this.#text_size*this.#chars_per_line_estimate_factor;let max_chars_per_line=avg_char_width_px>0?Math.floor(effective_screen_width/avg_char_width_px):20;max_chars_per_line=Math.max(max_chars_per_line,5);let current_log_entry_index=0;for(let i=0;i<msg2render.length;i++){if(typeof msg2render[i]==="string"&&msg2render[i]!==""){let entry_text_to_wrap="";if(repeats2render[i]>1){entry_text_to_wrap+=`[${repeats2render[i]}] `}entry_text_to_wrap+=msg2render[i];const wrapped_lines_for_current_entry=this.#wrapText(entry_text_to_wrap,max_chars_per_line);if(final_text_for_widget.length>0){final_text_for_widget+="\n"}final_text_for_widget+=wrapped_lines_for_current_entry.join("\n");if(this.#background_enabled){background_configs.push({start_visual_line:total_visual_lines,num_visual_lines:wrapped_lines_for_current_entry.length,original_log_index:this.#reverse_order?messages_arr.length-1-current_log_entry_index:current_log_entry_index})}total_visual_lines+=wrapped_lines_for_current_entry.length;current_log_entry_index++}}const single_line_height=this.#text_size*this.#padding_multiplier;const text_block_total_height=total_visual_lines*single_line_height;if(is_round_screen){if(!this.#is_custom_margin){this.#margin=this.#text_size}}let container_y,content_y_offset_in_container;if(this.#log_from_top){container_y=0;content_y_offset_in_container=this.#margin}else{container_y=DEVICE_HEIGHT-(text_block_total_height+this.#margin*2);content_y_offset_in_container=this.#margin}const container_total_height=text_block_total_height+this.#margin*2;if(!this.#view_container)this.#createViewContainer();this.#view_container.setProperty(hmUI.prop.MORE,{y:container_y,h:container_total_height});if(!this.#is_widgets_created)this.#recreateWidgets();if(this.#background_widget&&this.#background_enabled&&this.#visual_log_enabled){this.#background_widget.setProperty(hmUI.prop.MORE,{x:0,y:content_y_offset_in_container,h:text_block_total_height,w:DEVICE_WIDTH,color:this.#background_color});this.#background_widget.setProperty(hmUI.prop.VISIBLE,true)}else if(this.#background_widget){this.#background_widget.setProperty(hmUI.prop.VISIBLE,false)}if(this.#visual_log_enabled){if(this.#text_widget){this.#text_widget.setProperty(hmUI.prop.MORE,{x:0,y:content_y_offset_in_container,w:DEVICE_WIDTH,h:text_block_total_height,text:final_text_for_widget,text_size:this.#text_size,text_style:this.#text_style,color:this.#text_color});this.#text_widget.setProperty(hmUI.prop.VISIBLE,true)}}else{if(this.#text_widget)this.#text_widget.setProperty(hmUI.prop.VISIBLE,false)}}#destroyWidgets(){if(this.#view_container){if(this.#background_widget){hmUI.deleteWidget(this.#background_widget);this.#background_widget=null}if(this.#text_widget){hmUI.deleteWidget(this.#text_widget);this.#text_widget=null}hmUI.deleteWidget(this.#view_container);this.#view_container=null;this.#is_widgets_created=false}}#createTimer(startup_delay,repeat_delay,callback){const bound_callback=callback.bind(this);const timer=setTimeout(()=>{bound_callback();if(repeat_delay>0){}},startup_delay);return timer}#viewContainerExists(){if(this.#view_container){return true}try{if(typeof hmUI.createWidget!=="function"){return false}this.#view_container=hmUI.createWidget(hmUI.widget.VIEW_CONTAINER,{x:0,y:0,w:DEVICE_WIDTH,h:DEVICE_HEIGHT,scroll_enable:false,z_index:999});if(this.#view_container&&typeof this.#view_container.setProperty==="function"){return true}this.#view_container=null;return false}catch(error){this.#view_container=null;return false}}#shouldEnableVis(){return this.#viewContainerExists()&&this.#visual_log_enabled}#consoleLog(prefix,msg){if(this.#console_log_enabled){if(this.#use_logger){if(!logger&&this.#filename){logger=Logger.getLogger(this.#filename)}switch(prefix){case PREFIX_LOG:logger.log(msg);break;case PREFIX_INFO:logger.info(msg);break;case PREFIX_WARN:logger.warn(msg);break;case PREFIX_ERR:logger.error(msg);break;case PREFIX_DBG:logger.debug(msg);break;default:logger.log(msg);break}}else{if(this.#prefix_enabled&&this.#filename){msg=`[${prefix}] [${this.#filename}] ${msg}`}else if(this.#prefix_enabled){msg=`[${prefix}] ${msg}`}else if(this.#filename){msg=`[${this.#filename}] ${msg}`}console.log(msg)}}}}