T1 built-in vice screen display app

T1 built-in vice screen display app

Updates

2018-01-16 

  • New fucntion: 14 inch vice screen support carousel video;
  • New fucntion: 14 inch vice screen support video seeding; 
  • New fucntion: 7 inch vice screen support slides; 
  • New fucntion: 7 inch vice screen to support a single video; 
  • New fucntion: 7 inch vice screen support video carousel; 
  • New fucntion: 7 inch vice screen support video seeding; 
  • Add method of determining the size of vice screen; 
  • Add new ways to manage the vice screen cache file; 
  • DS_Lib library updated to 1.0.16;

                                                                                                                                                                                                                  

First, the introduction

T1 dual-screen machine has three combinations: host, host +7 inch vice screen, host +14 inch vice screen. The main screen and the vice screen are running SUNMI OS system, communicated through the packaged interface.

The main screen is mainly used to run business APP, for example: cash register system. Vice screen mainly used for customer settlement, advertising content.

Developers have two ways to achieve displaying of vice screen:

  • T1 vice screen system have built-in default vice screen display APP and multiple common templates. Developers only need to refer to the template implementation code to achieve the vice screen content displaying;
  • Developing own vice screen display APP, you need to handle the data transceiver, content displaying and other actions;

If you do not have strong customization needs, we recommend you to use the default vice display APP, to save a lot of research and development costs.

This article will explain how to access the built-in vice screen guest program.

Second, how to debug the application

Since the main screen and vice screen communicate via USB, when the main / vice screen is plugged with USB cable, main/vice screens are will be disconnected and the device can not be debugged.

SUNMI solution: put development computers and debugging T1 host in a same local area network environment, through the network to debug.

How to debug:

    1、open the USB debugging, and debugging authority;

        Instructions:

    2、Device ADB debugging through the network;

        Instructions:

Third, how to access the default vice screen APP

(Emphasis: Sunmi OS is customized based on Android6.0. Android6.0 + requires some sensitive permissions and need dynamic application)

Sunmi default vice screen APP provides multiple displaying templates, developers only need to be realize the code of vice screen controlling on the main screen, and then send the data to the default vice screen App to achieve displaying. 

1、Initialize the configuration

Docking 7-inch or 14-inch default sub-screen APP initialization are the same process.

step 1:

    Download DemoApp resource file.

step 2:

    Refer to the DemoApp source code, clarification the following code in the build.gradle file under the Android Studio app module:

dependencies {
compile 'com.sunmi:DS_Lib:1.0.15' //Commercial library provides lib, contains the interface has been packaged
compile 'com.alibaba:fastjson:1.1.67.android' //fastjson any version
}

step 3:

    Configure the following statement under the node in the manifest file AndroidMainfest.xml:


....
"sunmi.ds.MsgReceiver"> //Receive data broadcast

"com.sunmi.hcservice">
"com.sunmi.hcservice.status">


step 4:

    Initialize the SDK code in proper place; you can refer to the implementation code in DSC_Demo.

DSKernel mDSKernel =DSKernel.newInstance();
mDSKernel.init(context, mConnCallback); //Bind the service callback
mDSKernel.addReceiveCallback(mReceiveCallback); //receive Dual-screen communications data callback

Then you can refer to the DemoApp source code, the default vice screen to apply the contents of the built-in template displaying.

proguard  ds_lib:

-keep interface sunmi.ds.**{ *; }

-keep class sunmi.ds.**{ *; }

In addition, you need to mix up Greendao and Fastjson, and the specific confusion rules are subject to the official supply of Greendao and Fastjson

2, built-in vice-screen displaying program templates

Supported templates comparison of 7-inch and 14 inch vice screen:

template 7寸 14寸
Text
Text + QR code
Single picture
Slide
Single video
Video carousel
List ×
List + single picture ×
List + Slide ×
List + single video ×
List + video carousel ×

2.1, two lines of text

1

Implementation code:

JSONObject json = new JSONObject();
json.put("title", title);    //title is the content of the line above
json.put("content", content);    //content is the content of the following line
String jsonStr = json.toString();    //Build the DataPacket class
DataPacket packet = UPacketFactory.buildShowText(DSKernel.getDSDPackageName(), jsonStr, callback);    //The first parameter is the package name of data receiving sub-application, you can refer the demo here, the second parameter is the displaying contents string, the third parameter is the result callback.

mDSKernel.sendData(packet);    //Call SendData to send text

2.2, QR code + text

2

Implementation code:

JSONObject json = new JSONObject();
try {
json.put("title", "微信支付");
json.put("content", "10.00");
} catch (JSONException e) {
e.printStackTrace();
}

mDSKernel.sendFile(DSKernel.getDSDPackageName(), json.toString(), Environment.getExternalStorageDirectory().getPath() + "/qrcode.png", new ISendCallback() {
@Override
public void onSendSuccess(long l) {
//display image
try {
JSONObject json = new JSONObject();
json.put("dataModel", "QRCODE");
json.put("data", "default");
mDSKernel.sendCMD(SF.DSD_PACKNAME, json.toString(), l, null);
} catch (JSONException e) {
e.printStackTrace();
}
}

@Override
public void onSendFail(int i, String s) { 
}

@Override
public void onSendProcess(long l, long l1) {
}
});

2.3, a single picture

3

Implementation code:

mDSKernel.sendFile(DSKernel.getDSDPackageName(), Environment.getExternalStorageDirectory().getPath() + "/img_01.png", new ISendCallback() {

@Override
public void onSendSuccess(long taskId) {
showPicture(taskId);
}

@Override
public void onSendFail(int errorId, String errorInfo) {
}

@Override
public void onSendProcess(long totle, long sended) {
}
});
}

/**
* 
* Show a single picture
*
* @param taskId
*/

private void showPicture(long taskId) {
//display image
try {
JSONObject json = new JSONObject();
json.put("dataModel", "SHOW_IMG_WELCOME");
json.put("data", "default");
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), json.toString(), taskId, null);
} catch (JSONException e) {
e.printStackTrace();
}
}

2.4, slide

4

Implementation code:

JSONObject json = new JSONObject();
json.put("rotation_time",5000);     //Slide switching time is calculated in milliseconds, the default is 10000 milliseconds

List pathList = new ArrayList<>();
pathList.add("/sdcard/img1.png");
pathList.add("/sdcard/img2.png");
...
mDSKernel.sendFiles(DSKernel.getDSDPackageName(), json.toString(), 
pathList, new ISendFilesCallback() {
public void onAllSendSuccess(long fileId) {
show(fileId);
}
public void onSendSuccess(final String s,final long l) {}
public void onSendFaile(int errorId, String errorInfo) {}
public void onSendFileFaile(String path, int errorId, String errorInfo){}
public void onSendProcess(String path, long total, long sended) {}
});

private void show(long fileId) {
String json = UPacketFactory.createJson(DataModel.IMAGES,"");
mDSKernel.sendCMD(DSKernel.getDSDPackageName(),json,fileId,null);
}

2.5, a single video

5

Implementation code:

mDSKernel.sendFile(DSKernel.getDSDPackageName(), Environment.getExternalStorageDirectory().getPath() + "/video_01.mp4", new sunmi.ds.callback.ISendCallback() {
@Override
public void onSendSuccess(long l) { 
playvideo(l); 
}

@Override
public void onSendFail(int i, String s) {
Log.d("highsixty", "发送单个文件视频文件失败 ------------>" + s); 
}

@Override
public void onSendProcess(final long l, final long l1) {
}
});
}

private void playvideo(long taskID) { 
String json = UPacketFactory.createJson(DataModel.VIDEO, "true");     //"true" video broadcast; false video playback from the beginning
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), json, taskID, null);
}

2.6, video carousel

6

Implementation code:

/**
* 
* Send multiple videos
*/

private void sendVideos() {
//Please judge whether the file exists or not
List files = new ArrayList<>();
files.add(Environment.getExternalStorageDirectory().getPath() + "/video_01.mp4");
files.add(Environment.getExternalStorageDirectory().getPath() + "/video_02.mp4");
files.add(Environment.getExternalStorageDirectory().getPath() + "/video_03.mp4");
mDSKernel.sendFiles(DSKernel.getDSDPackageName(), "", files, new ISendFilesCallback() {

@Override
public void onAllSendSuccess(long fileid) {
playvideos(fileid);
}

@Override
public void onSendSuccess(String path, long taskId) { 
}

@Override
public void onSendFaile(int errorId, String errorInfo) { 
}

@Override
public void onSendFileFaile(String path, int errorId, String errorInfo) { 
}

@Override
public void onSendProcess(String path, long totle, long sended) {
}
});
}

private void playvideos(long taskID) {
Log.d(TAG, "playvideos: ------------>" + taskID);
String json = UPacketFactory.createJson(DataModel.VIDEOS, "true");     //The second parameter true: video broadcast  false: video playback from beginning
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), json, taskID, null);
}

2.7, list

7

Implementation code:

{
"title": "三米奶茶店欢迎您", 
"head": {
"param1": "序列号",
"param2": "商品名",
"param3": "单价""param4": "数量""param5": "小结"
},
"list": [
{
"param1": "1",
"param2": "华夫饼",
"param3": "10.00""param4": "1""param5":"10.00"
},
{
"param1": "1",
"param2": "吞拿鱼华夫饼",
"param3": "12.00""param4": "1""param5":"12.00"
}
... ...//Here is the same format of data

],
"KVPList": [
{
"name": "收款",
"value": "¥40.00"
},
{
"name": "优惠",
"value": "¥3.00"
},
{
"name": "找零",
"value": "¥3.00"
},
{
"name": "实收",
"value": "¥37.00"
}
]
}

/**
*JSON data format description:
*1: title field for the title
*2: head field is the header field
*3: list is the products list, the number of fields in the header and table should be the same
*4: KVPList for the settlement of key-value list
*/

/**
*rule:
*When the display of graphic mixed: head params assignment minimum 1 maximum 4; params assignment of each element in list minimum 1, maximum 4; KVPList size minimum 1 maximum 4.
*/ 

/**
*receiverPackageName The secondary screen of the received data shows the app's package name DataType.DATA
*DataType.DATA 
*DataModel.TEXT 
*jsonStr display data content, the specific format, see the box below
callback callback
*/
DataPacket pack = buildPack(receiverPackageName, DataType.DATA, DataModel.TEXT, jsonStr, callback);
mDSKernel.sendData(pack);

2.8, list + single picture

8

Implementation code:

{
"title": "三米奶茶店欢迎您", 
"head": {
"param1": "序列号",
"param2": "商品名",
"param3": "单价""param4": "数量""param5": "小结"
},
"list": [
{
"param1": "1",
"param2": "华夫饼",
"param3": "10.00""param4": "1""param5":"10.00"
},
{
"param1": "1",
"param2": "吞拿鱼华夫饼",
"param3": "12.00""param4": "1""param5":"12.00"
}
... ...//Here is the same format of data

],
"KVPList": [
{
"name": "收款",
"value": "¥40.00"
},
{
"name": "优惠",
"value": "¥3.00"
},
{
"name": "找零",
"value": "¥3.00"
},
{
"name": "实收",
"value": "¥37.00"
}
]
}

/**
*JSON数据格式说明:
*1:title字段为标题
*2:head字段是表头字段
*3:list是商品列表,表头和表的字段数量要一致 
*4:KVPList为结算键值对列表
*/

/**
*规则:
*当显示图文混合时:head的params赋值个数最小1个最大4个;list中每个元素的params赋值个数最*小1个最大4个;KVPList的size
*最小1个最大4个。
*/ 

String filePath = "xxx/img.png";    //The path of the picture shown
mDSKernel.sendFile(DSKernel.getDSDPackageName(), filePath, new ISendCallback() {
public void onSendSuccess(long fileId) {
show(fileId, jsonStr);    //The picture is sent successfully, showing the text content
} 
public void onSendFail(int errorId, String errorInfo) {}
public void onSendProcess(long total, long sended) {}
});

void show(long fileId, String jsonStr){
jsonStr = UPacketFactory.createJson(DataModel.SHOW_IMG_LIST, jsonStr);    
//The first parameter DataModel.SHOW_IMG_LIST to display layout mode, jsonStr to display the contents of the characters
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), jsonStr, fileId,null);
}

2.9, list + slide

9

Implementation code:

{
"title": "三米奶茶店欢迎您", 
"head": {
"param1": "序列号",
"param2": "商品名",
"param3": "单价""param4": "数量""param5": "小结"
},
"list": [
{
"param1": "1",
"param2": "华夫饼",
"param3": "10.00""param4": "1""param5":"10.00"
},
{
"param1": "1",
"param2": "吞拿鱼华夫饼",
"param3": "12.00""param4": "1""param5":"12.00"
}
... ...//这里是相同格式的数据
],
"KVPList": [
{
"name": "收款",
"value": "¥40.00"
},
{
"name": "优惠",
"value": "¥3.00"
},
{
"name": "找零",
"value": "¥3.00"
},
{
"name": "实收",
"value": "¥37.00"
}
]
}

/**
*JSON数据格式说明:
*1:title字段为标题
*2:head字段是表头字段
*3:list是商品列表,表头和表的字段数量要一致 
*4:KVPList为结算键值对列表
*/

/**
*规则:
*当显示图文混合时:head的params赋值个数最小1个最大4个;list中每个元素的params赋值个数最*小1个最大4个;KVPList的size
*最小1个最大4个。
*/ 

List paths = new ArrayList<>();
paths.add(Environment.getExternalStorageDirectory().getPath() + "/sunmi1.png");
paths.add(Environment.getExternalStorageDirectory().getPath() + "/sunmi2.png");
paths.add(Environment.getExternalStorageDirectory().getPath() + "/sunmi3.png");
paths.add(Environment.getExternalStorageDirectory().getPath() + "/sunmi4.png");

mDSKernel.sendFiles(DSKernel.getDSDPackageName(), "", paths, new ISendFilesCallback() {
@Override
public void onAllSendSuccess(long fileId) { 
sendImgsListCMD(fileId,json); 
}

@Override
public void onSendSuccess(String path, long taskId) {}
@Override
public void onSendFaile(int errorId, String errorInfo) {}
@Override
public void onSendFileFaile(String path, int errorId, String errorInfo) {}
@Override
public void onSendProcess(String path, long totle, long sended) {}
});

void sendImgsListCMD(long fileId, String jsonStr){ 
jsonStr= UPacketFactory.createJson(DataModel.SHOW_IMGS_LIST, json);
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), jsonStr, fileId,null);
}

2.10, list + single video

10

Implementation code:

mDSKernel.sendFile(DSKernel.getDSDPackageName(), Environment.getExternalStorageDirectory().getPath() + "/video_01.mp4", new sunmi.ds.callback.ISendCallback() {
@Override
public void onSendSuccess(long l) { 
playVideoMenu(l); 
}

@Override
public void onSendFail(int i, String s) {
Log.d("highsixty", "发送单个文件视频和清单文件失败 ------------>" + s); 
}

@Override
public void onSendProcess(final long l, final long l1) {
}
});

private void playVideoMenu(long fileId) {
try {
JSONObject data = new JSONObject();
data.put("title", "商米奶茶店收银");
JSONObject head = new JSONObject();
head.put("param1", "序号");
head.put("param2", "商品名");
head.put("param3", "单价");
data.put("head", head);
data.put("flag", "true");     //When set as true, the video will play following the previous progress. Set as false, the video will be played from the beginning.
JSONArray list = new JSONArray();
for (int i = 1; i < 11; i++) {
JSONObject listItem = new JSONObject();
listItem.put("param1", "" + i);
listItem.put("param2", products.get(i - 1));
listItem.put("param3", prices.get(i - 1));
list.put(listItem);
}

data.put("list", list);
JSONArray KVPList = new JSONArray();
JSONObject KVPListOne = new JSONObject();
KVPListOne.put("name", "总计 ");
KVPListOne.put("value", "132.00");
JSONObject KVPListTwo = new JSONObject();
KVPListTwo.put("name", "优惠 ");
KVPListTwo.put("value", "12.00");
JSONObject KVPListThree = new JSONObject();
KVPListThree.put("name", "数量 ");
KVPListThree.put("value", "10");
JSONObject KVPListFour = new JSONObject();
KVPListFour.put("name", "应收 ");
KVPListFour.put("value", "120.00");
KVPList.put(0, KVPListOne);
KVPList.put(1, KVPListTwo);
KVPList.put(2, KVPListThree);
KVPList.put(3, KVPListFour);
data.put("KVPList", KVPList);
Log.d("HHHH", "onClick: ---------->" + data.toString());
String json = UPacketFactory.createJson(DataModel.SHOW_VIDEO_LIST, data.toString());
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), json, fileId, null);
} catch (Exception e) {
e.printStackTrace();
}
}

2.11, list + video carousel

11

Implementation code:

/**
*list + video carousel
*/
private void sendMenuVideos() {
Log.d(TAG, "sendMenuVideos: ------------->");
List files = new ArrayList<>();
files.add(Environment.getExternalStorageDirectory().getPath() + "/video_01.mp4");
files.add(Environment.getExternalStorageDirectory().getPath() + "/video_02.mp4");
files.add(Environment.getExternalStorageDirectory().getPath() + "/video_03.mp4");

mDSKernel.sendFiles(DSKernel.getDSDPackageName(), "", files, new ISendFilesCallback() {

@Override
public void onAllSendSuccess(long fileid) { 
playMenuVideos(fileid);
}

@Override
public void onSendSuccess(String path, long taskId) { 
}

@Override
public void onSendFaile(int errorId, String errorInfo) { 
}

@Override
public void onSendFileFaile(String path, int errorId, String errorInfo) { 
}

@Override
public void onSendProcess(String path, long totle, long sended) {
}
});
}

private void playMenuVideos(long taskID) { 
try {
JSONObject data = new JSONObject();
data.put("title", "商米奶茶店收银");
JSONObject head = new JSONObject();
head.put("param1", "序号");
head.put("param2", "商品名");
head.put("param3", "单价");
data.put("head", head);
data.put("flag", "true"); //"True" video broadcast; false play from the beginning
JSONArray list = new JSONArray();
for (int i = 1; i < 11; i++) {
JSONObject listItem = new JSONObject();
listItem.put("param1", "" + i);
listItem.put("param2", products.get(i - 1));
listItem.put("param3", prices.get(i - 1));
list.put(listItem);
}
data.put("list", list);
JSONArray KVPList = new JSONArray();
JSONObject KVPListOne = new JSONObject();
KVPListOne.put("name", "总计 ");
KVPListOne.put("value", "132.00");
JSONObject KVPListTwo = new JSONObject();
KVPListTwo.put("name", "优惠 ");
KVPListTwo.put("value", "12.00");
JSONObject KVPListThree = new JSONObject();
KVPListThree.put("name", "数量 ");
KVPListThree.put("value", "10");
JSONObject KVPListFour = new JSONObject();
KVPListFour.put("name", "应收 ");
KVPListFour.put("value", "120.00");
KVPList.put(0, KVPListOne);
KVPList.put(1, KVPListTwo);
KVPList.put(2, KVPListThree);
KVPList.put(3, KVPListFour);
data.put("KVPList", KVPList);
Log.d("HHHH", "onClick: ---------->" + data.toString());
String json = UPacketFactory.createJson(DataModel.MENUVIDEOS, data.toString());
mDSKernel.sendCMD(DSKernel.getDSDPackageName(), json, taskID, null);
} catch (JSONException e) {
e.printStackTrace();
}
}

3, determine the size of the secondary screen

The main screen application can determine the size of the vice screen by acquiring the vice screen Model value and output the business logic of the corresponding displaying content according to the size.

3.1, Access to the sub-screen Model value from Main model

String subModel = Settings.Global.getString(getContentResolver(), "sunmi_sub_model")

//Return value: "t1sub14" corresponds to 14 inch vice screen, "t1sub7" corresponds to 7 inch vice screen. Returning other values indicates that the acquisition failed.

3.2, Access the Vice-screen Model value from vice screen

//Obtain secondary screen model from secondary screen
case R.id.btn_get_sub_model:
JSONObject jsonObject2 = new JSONObject();
try {
jsonObject2.put("dataModel", "GET_MODEL");
jsonObject2.put("data", "");
} catch (JSONException e) {
e.printStackTrace();
}

DataPacket p2 = new DataPacket.Builder(DSData.DataType.CMD).recPackName(SF.SUNMI_DSD_PACKNAME).data(jsonObject2.toString())
.addCallback(new ISendCallback() {
@Override
public void onSendSuccess(long taskId) {
} 

@Override 
public void onSendFail(int errorId, String errorInfo) { } 

@Override
public void onSendProcess(long totle, long sended) { }
}).build();

mDSKernel.sendQuery(p2, new QueryCallback() { 
@Override 
public void onReceiveData(final DSData data) { Log.d("highsixty", "onReceiveData: ------------>" + data.data); runOnUiThread(new Runnable() { 
@Override 
public void run() { 
Toast.makeText(MainActivity.this, "从副屏获取副屏model-->" + data.data, Toast.LENGTH_LONG).show(); 
}
});
}
}); 
break;
//Return value: "t1sub14" corresponds to 14 inch vice screen, "t1sub7" corresponds to 7 inch vice screen. Returning other values indicates that the acquisition failed.

4, clear the vice screen cache

4.1, Query specified cache directory size of the vice screen

JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("dataModel", "GETVICECACHEFILESIZE");
jsonObject.put("data", Environment.getExternalStorageDirectory().getAbsolutePath() + "/HCService/" + getPackageName().replace(".", "_"));
} catch (JSONException e) {
e.printStackTrace();
}

DataPacket packet = new DataPacket.Builder(DSData.DataType.CMD).recPackName(SF.SUNMI_DSD_PACKNAME).data(jsonObject.toString())
.addCallback(null).build();
mDSKernel.sendQuery(packet, new QueryCallback() {
@Override
public void onReceiveData(final DSData data) {
Log.d("highsixty", "onReceiveData: ------------>" + data.data);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "副屏缓存文件大小字节数为" + data.data, Toast.LENGTH_LONG).show();
}
});
}
});

4.2, clear designated cache directory of the vice screen

DataPacket packet2 = UPacketFactory.remove_folders(DSKernel.getDSDPackageName(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/HCService/" + getPackageName().replace(".", "_"), new ISendCallback() {
@Override
public void onSendSuccess(long taskId) {
showToast("清除缓存文件成功");
}

@Override
public void onSendFail(int errorId, String errorInfo) {
showToast("清除缓存文件失败");
}

@Override
public void onSendProcess(long totle, long sended) {
}
});

mDSKernel.sendCMD(packet2);

4.3, check the exists of specified cache file 

To avoid sending duplicate multimedia files, developers are advised to check whether the multimedia file has been cached by calling DSKernel.checkFileExist (long fileId, final ICheckFileCallback callback) before sending the multimedia file. If it is already cached, it can be played directly by specifying file ID .

//Note: This method checks fileId corresponding file exists in the vice screen, The first parameter is the file id returned by the vice screen before sending the file, which is unique for all files, and the second parameter is the result callback

mDSKernel.checkFileExist(fileId, new ICheckFileCallback(){
@Override
public void onCheckFail() {}

@Override
public void onResult(boolean arg0) {
//The return value is true
//The return value false does not exist
} 
});

4.4, clear the vice screen specific cache file

mDSKernel.deleteFileExist(FileID, new ICheckFileCallback() {
@Override
public void onCheckFail() {
Log.d(TAG, "onCheckFail: ----------->");
}

@Override
public void onResult(boolean exist) {
Log.d(TAG, "onResult: ---------->" + exist);
}
});

5, picture / video displaying rules

Picture / video scaling rules:

    By default, the image / video is scaled with at least two edges touching the display container boundary.

Picture / video recommended resolution:

    7 inch:

            Full screen image: 1024 * 600

            Full screen video: 1024 * 600

    14 inch:

            Full-screen display image / video: 1920 * 1080

            Image / Video + List: 1186 * 1080

Recommended file size:

    Picture: ≤1MB / sheet

    Video: ≤5MB / month

Recommended number of slide files:

    Picture: ≤10 sheets

Recommended number of carousel video files:

    Video: ≤10


Note: Due to the time required to transfer the file to the vice screen, there may be a delay in displaying files. So try to cache the pictures and video files to the vice screen in advance when it is idle, and when it needs to be displayed on the vice screen, it is called according to the file ID.