首先我們下載官網的SDK Sample
點我到官網下載頁面

 

解壓縮之後就可以開啟官方的 Sample Code 了
Run 出來看看範到程式是如何操作的

然後先找到我們的列印機
點擊Unselected State後會出現連結方式

這裡選擇用LAN來連線

找到了我們的列印機
連結成功後會如下圖

接下來我們就可以開始第一次的列印囉!
點擊Printer下的Sample
會出現語言選單

再來會有紙單大小的選項

都選擇完畢後
我們會來到這個畫面

有各式的Sample可供列印
我們就先印一張 Text Receipt 來看看吧

 

成功印下一張收據之後
我們再來看看Code是怎麼寫的

 

首先是與設備的連線
在MainFragment中的每一row都是ListView來裝載的
所以我們Tapped Unselected Status後會進到這裡

@Override
public void onItemClick(AdapterView<?> parent, View view,
                        int position, long id) {
    super.onItemClick(parent, view, position, id);

    Intent intent = new Intent(getActivity()
                    , CommonActivity.class);

    switch (position) {
        case 1: {   // Tapped Destination Device row
            intent.putExtra(
            CommonActivity.BUNDLE_KEY_ACTIVITY_LAYOUT
            , R.layout.activity_printer_search);
            intent.putExtra(
            CommonActivity.BUNDLE_KEY_TOOLBAR_TITLE
            , "Search Port");
            intent.putExtra(
            CommonActivity.BUNDLE_KEY_SHOW_HOME_BUTTON
            , true);
            intent.putExtra(
            CommonActivity.BUNDLE_KEY_SHOW_RELOAD_BUTTON
            , true);

            startActivityForResult(intent
            , PRINTER_SET_REQUEST_CODE);
            break;
        }

從這段Code可以看到我們進到CommonActivity中
在這個共同的Activity中
利用CommonActivity的常數來做為Intent的識別字串帶入參數
而Intent所帶入的第一個參數
就是連結哪一個layout
第二是Title的字串
第三&四是button的顯示
最後帶入Request Code 便於之後 Callback

 

進入CommonActivity後
我們看到的第一件事是這行

setContentView(intent.getIntExtra(
   CommonActivity.BUNDLE_KEY_ACTIVITY_LAYOUT
   , R.layout.fragment_dummy));  
   // Display a user selected menu list.

將畫面設定為R.layout.activity_printer_search Code如下

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android
    ="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/printerSearchFragment"
    android:name
    ="com.starmicronics.starprntsdk.SearchPortFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

可以從android:name看到此xml與SearchPortFragment連結
來到SearchPortFragment後第一個執行的method是onCreate

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setHasOptionsMenu(true);

    mProgressDialog = new ProgressDialog(getActivity());
    mProgressDialog.setMessage("Communicating...");
    mProgressDialog.setProgressStyle(
                    ProgressDialog.STYLE_SPINNER);
    mProgressDialog.setCancelable(false);

    updatePrinterList();
}

這裡創建了一個ProgressDialog來告知User等待
再來執行了一個method

private void updatePrinterList() {
    adapter.clear();

    InterfaceSelectDialogFragment dialog 
    = InterfaceSelectDialogFragment
      .newInstance(INTERFACE_SELECT_DIALOG);
    dialog.show(getChildFragmentManager());
}

先將原有資料清掉後
創建了一個DialogFragment Object
Constructor接收一個String Parameter為了等等Callback 可以識別

 

在DialogFragment 的onCreatDialog中
除了畫面的設定之外還取得了父級的Callback

mCallbackTarget 
  = (CommonAlertDialogFragment.Callback) getParentFragment();

再來則是本身的ClickListener裡面的onClick method
做了下面這些事

String[] selectedInterfaceArray;
switch (which) {
    case 0:
        selectedInterfaceArray 
        = new String[] {PrinterSetting.IF_TYPE_ETHERNET};
        break;
    case 1:
        selectedInterfaceArray 
        = new String[] {PrinterSetting.IF_TYPE_BLUETOOTH};
        break;
    以下省略 。。。
}

Intent intentForPassingData = new Intent();
intentForPassingData.putExtra(
   CommonActivity.BUNDLE_KEY_INTERFACE
   , selectedInterfaceArray);

mCallbackTarget.onDialogResult(
   getArguments().getString(DIALOG_TAG)
   , intentForPassingData);

dismiss();

這裡用一個 String Array儲存選擇的參數
利用Intent return Parameter
Callback.onDialogResult 第一個Parameter是識別的String

 

讓我們回到SearchPortFragment
這裡實作了CommonAlertDialogFragment.Callback
找到我們剛剛回傳的地方如下

public void onDialogResult(String tag, Intent data) {
switch (tag) {
case INTERFACE_SELECT_DIALOG : {
    String[] selectedInterfaces 
    = data.getStringArrayExtra(
      CommonActivity.BUNDLE_KEY_INTERFACE);
    boolean isCanceled = data.hasExtra(
    CommonAlertDialogFragment.LABEL_NEGATIVE);

    if (selectedInterfaces != null) {
        addTitle("List");

        if (selectedInterfaces.length <= 1 
            && selectedInterfaces[0].equals(
               PrinterSetting.IF_TYPE_MANUAL)) {
            mMacAddress = "";

            PrinterSetting         setting 
            = new PrinterSetting(getActivity());
            PortNameDialogFragment dialog  
            = PortNameDialogFragment.newInstance(
            PORT_NAME_INPUT_DIALOG, setting.getPortName());

            dialog.show(getChildFragmentManager());
        }
        else {
         for (String selectedInterface : selectedInterfaces){
                SearchTask searchTask = new SearchTask();
                searchTask.execute(selectedInterface);
            }

            mProgressDialog.show();
        }
    }
    else if (isCanceled) {
        getActivity().finish();
    }
    break;
}

這一段是在做selectedInterfaces的判斷
我們所進入的是黃色 {  } 的這一段
執行SearchTask 並且讓ProgressDialog顯示

@Override
protected Void doInBackground(String... interfaceType) {
    try {
        mPortList = StarIOPort.searchPrinter(interfaceType[0], getActivity());
    }
    catch (StarIOPortException e) {
        mPortList = new ArrayList<>();
    }

    return null;
}

而SerchTask在背後做的是
把interfaceType&Activity傳進StarIOPort搜尋設備
searchPrinter 這個method會 return ArrayList

 

那就讓我們來看看searchPrinter做了什麼吧

public static synchronized ArrayList<PortInfo> 
       searchPrinter(String var0, Context var1)  
       throws StarIOPortException {

    String var2 = var0.toUpperCase();
    new ArrayList();
    ArrayList var3;
    if(var2.startsWith("TCP:")) {
        var3 = TCPPort.c();
    } else if(var2.startsWith("BT:")) {
        var3 = i.c();
        if(!var2.equals("BT:")) {
            ArrayList var4 = new ArrayList();
            String var5 = var0.substring(3);
            Iterator var6 = var3.iterator();

            while(var6.hasNext()) {
                PortInfo var7 = (PortInfo)var6.next();
                String var8 = var7.getPortName().substring(3);
                if(var8.startsWith(var5)) {
                    var4.add(var7);
                }
            }

            return var4;
        }
    } else {
        if(!var2.startsWith("USB:")) {
           throw new StarIOPortException("Invalid argument.");
        }
        var3 = f.a(var1);
    }
    return var3;
}

這裡的 if 判斷interface 是 LAN、Bluetooth or USB 之中的哪一種
以我們現在的操作為例
我們使用LAN的interface會進入黃色 {  } 區域執行TCPPort.c ( )
這個method做的事是利用 java.net.DatagramSocket
對區網發送Broadcast 內容為自訂的加密格式or訊息格式
當收到此格式時
所有match的 IP 都會回應相對的格式資料
然後此method會解析資料,包進PortInfo Object中
以ArrayList的方式return有效裝置

//點我看Socket專題報導

 

取得每個有效的PortInfo之後
讓我們回到SearchTask

@Override
protected void onPostExecute(Void doNotUse) {
    for (PortInfo info : mPortList) {
        addItem(info);
    }

    if (mProgressDialog != null) {
        mProgressDialog.dismiss();
    }
}

這一段純粹在做UI的顯示
取消剛剛要User等待的ProgressDialog
並歷遍所有PorInfo
把PortName、ModelName、MacAddress顯示出來供User選擇

 

當我們點擊後
會跑到SearchPortFragment中的onItemClick
執行下方Code

ModelConfirmDialogFragment dialog = ModelConfirmDialogFragment
   .newInstance(MODEL_CONFIRM_DIALOG, model);
dialog.show(getChildFragmentManager());

出現一個確認的Dialog

當我們按下YES之後,執行下一段的Code

public void onClick(DialogInterface dialog, int which) {
    Intent intentForPassingData = new Intent();
    intentForPassingData.putExtra(LABEL_POSITIVE
     , LABEL_POSITIVE);
    intentForPassingData.putExtra(SELECTED_INDEX
     , getArguments().getInt(SELECTED_INDEX));
   intentForPassingData.putExtra(CommonActivity.SELECTED_MODEL
     , getArguments().getInt(CommonActivity.SELECTED_MODEL));

    callbackToTarget(args.getString(DIALOG_TAG)
     , intentForPassingData);

    dialog.dismiss();
}

這裡讓Intent帶入三樣參數
一個是LABEL_POSITIVE的識別字串
還有選擇的INDEX與選擇的連接MODEL
最後Callback回到SearchPortFragment

case MODEL_CONFIRM_DIALOG: {
    boolean isPressedYes = data.hasExtra(
            CommonAlertDialogFragment.LABEL_POSITIVE);
    int selectedModel  = data.getIntExtra(
        CommonActivity.SELECTED_MODEL, ModelCapability.NONE);

    if (isPressedYes) {
        mPortSettings = ModelCapability
           .getPortSettings(selectedModel);
        mEmulation    = ModelCapability
           .getEmulation(selectedModel);

       if (ModelCapability.getDrawerOpenStatus(selectedModel)) {
           DrawerOpenActiveSelectDialogFragment dialog 
           = DrawerOpenActiveSelectDialogFragment.newInstance(
                  DRAWER_OPEN_ACTIVE_SELECT_DIALOG);
           dialog.show(getChildFragmentManager());
       }
       else {
            mDrawerOpenStatus = true;
            registerPrinter();

            getActivity().finish();
        }
    }

在Calback中找到MODEL_CONFIRM_DIALOG的LABEL
首先利用hasExtra( )來確認User按下YES
並取得SELECTED_MODEL
下面的mPortSettings & eEmulation
是不同型號產品,所需要的對應設定值

下一個 If 判斷我們選擇的產品有沒有現金抽屜
有的話再生產一個DrawerOpenActivitySelectDialogFragment
這個Dialog只有兩個選項
快速or慢速開啟抽屜,Callback回來ture or false
之後執行registerPrinter( )時會用得到

 

那接下來就讓我們看看registerPrinter( )做了什麼事吧

private void registerPrinter() {
   PrinterSetting setting = new PrinterSetting(getActivity());
   setting.write(mModelName
                , mPortName
                , mMacAddress
                , mPortSettings
                , mEmulation
                , mDrawerOpenStatus);
}

這些全都是剛剛我們所選擇的ProtInfo、設定參數等
再來看看setting.write裡面有什麼

public void write(String modelName, String portName
    , String macAddress, String portSettings
    , Emulation emulation, Boolean drawerOpenActiveHigh) {
    SharedPreferences prefs 
    = PreferenceManager.getDefaultSharedPreferences(mContext);

    int emulationInt = -1;

for (Pair<Emulation, Integer> emulationPair : mEmulationList){
        if (emulation == emulationPair.first) {
            emulationInt = emulationPair.second;
            break;
        }
    }

    prefs.edit()
            .putString(PREF_KEY_MODEL_NAME, modelName)
            .putString(PREF_KEY_PORT_NAME, portName)
            .putString(PREF_KEY_MAC_ADDRESS, macAddress)
            .putString(PREF_KEY_PORT_SETTINGS, portSettings)
            .putInt(PREF_KEY_EMULATION, emulationInt)
            .putBoolean(PREF_KEY_DRAWER_OPEN_STATUS
                       , drawerOpenActiveHigh)
            .apply();
}

write這個method其實就是把我們剛剛得到的參數寫進SharedPreferences裡
方便之後需要的時候可以調用
而第一個 For 迴圈是要找出產品所對應的模式
Example: StarPRNT,StarLINE,StarGraphic......

至此,我們找到了裝置
並且成功連線
接下來就是Print了

========================================================
========================================================

當我們在MainFragment 按下 Print下方的Sample時執行這段Code

case 3: {   // Tapped Printer row
    LanguageSelectDialogFragment dialog 
    = LanguageSelectDialogFragment
          .newInstance(PRINTER_LANG_SELECT_DIALOG);
    dialog.show(getChildFragmentManager());
    break;
}

建立了一個LanguageSelectDialog
而return的是PrinterSetting.LANGUAGE的值,如下

int selectedLanguage = PrinterSetting.LANGUAGE_ENGLISH;

switch (which) {
 case 0: selectedLanguage = PrinterSetting.LANGUAGE_ENGLISH; 
   break;
 case 1: selectedLanguage = PrinterSetting.LANGUAGE_JAPANESE; 
   break;
 case 2: selectedLanguage = PrinterSetting.LANGUAGE_FRENCH; 
   break;
   以下省略。。。
}

然後CallBack回MainFragment的onDialogResult( )
再叫出紙張大小的選擇Dialog

case PRINTER_LANG_SELECT_DIALOG: {
        。。。。。。
        PaperSizeSelectDialogFragment dialog 
        = PaperSizeSelectDialogFragment
        .newInstance(PRINTER_PAPER_SIZE_SELECT_DIALOG
        , language);
        dialog.show(getChildFragmentManager());
    break;

PaperSizeSelectDialog設值、還有回傳的方式都與LanguageSelect相同
所以我們再次回到了 MainFragment的onDialogResult( )

case PRINTER_PAPER_SIZE_SELECT_DIALOG: {
    int language  = data.getIntExtra(
    CommonActivity.BUNDLE_KEY_LANGUAGE
    , PrinterSetting.LANGUAGE_ENGLISH);

    int paperSize = data.getIntExtra(
    CommonActivity.BUNDLE_KEY_PAPER_SIZE
    , PrinterSetting.PAPER_SIZE_THREE_INCH);

    Intent intent = new Intent(getActivity()
                              , CommonActivity.class);
    intent.putExtra(CommonActivity.BUNDLE_KEY_ACTIVITY_LAYOUT
                    , R.layout.activity_printer);
    intent.putExtra(CommonActivity.BUNDLE_KEY_TOOLBAR_TITLE
                    , "Printer");
    intent.putExtra(CommonActivity.BUNDLE_KEY_LANGUAGE
                    , language);
    intent.putExtra(CommonActivity.BUNDLE_KEY_PAPER_SIZE
                    , paperSize);
    intent.putExtra(
         CommonActivity.BUNDLE_KEY_SHOW_HOME_BUTTON, true);

    startActivity(intent);
    break;
}

和上次的print_search不同的是
這次我們要前往的是R.layout.activity_printer
給 Intent必要的Parameters後執行 startActivity( )

 

CommonActivity和之前SearchPort一樣
創建畫面時會導到PrinterFragment
也會建立一個通知User等待的ProgressDialog
值得一提的有兩個地方
第一:

PrinterSetting setting = new PrinterSetting(getActivity());
Emulation emulation = setting.getEmulation();

boolean canPrintTextReceipt     
         = emulation != Emulation.StarGraphic;
boolean canPrintUtf8TextReceipt 
        = emulation != Emulation.StarGraphic 
        && emulation != Emulation.EscPos 
        && emulation != Emulation.EscPosMobile;
boolean canPrintRasterReceipt   
        = emulation != Emulation.StarDotImpact;

addTitle("Like a StarIO-SDK Sample");
addMenu(languageCode + " " + paperSizeStr      
        + " Text Receipt",canPrintTextReceipt);
addMenu(languageCode + " " + paperSizeStr      
        + " Text Receipt (UTF8)",canPrintUtf8TextReceipt);
addMenu(languageCode + " " + paperSizeStr      
        + " Raster Receipt",canPrintRasterReceipt);

這段程式碼先找從Setting中找出裝置的Emulation 模式
然後去比對,不一樣的模式可以印的東西也不一樣
建立 三個boolean的Parameter 並且設在addMenu( )中
設定false則此選項為 unclickable

第二:

ILocalizeReceipts localizeReceipts 
                  = ILocalizeReceipts.createLocalizeReceipts(
                                       mLanguage, mPaperSize);
String languageCode      = localizeReceipts.getLanguageCode();
String paperSizeStr      = localizeReceipts.getPaperSizeStr();
String scalePaperSizeStr 
       = localizeReceipts.getScalePaperSizeStr();

ILocalizeReceipts 是一個 abstract class
裡面會做一些語言、紙張大小的設定
還有一些abstract method 等著實作
不過傳入Language & PaperSize 後
return 出來的是繼承實作後對應語言的Class
Example:

switch (language) {
    case PrinterSetting.LANGUAGE_ENGLISH:
        localizeReceipts = new EnglishReceiptsImpl(); break;
    case PrinterSetting.LANGUAGE_JAPANESE:
        localizeReceipts = new JapaneseReceiptsImpl(); break;

由於我們這次選的是英文
所以會 return EnglishReceiptsImpl
EnglishReceiptsImpl 中會有各類型的Receipt、Coupon...等可做選擇

依我們剛剛的操作
點擊了第一項 Text Receipt 列印

PrintTask printTask = new PrintTask();
printTask.execute(position);

這時程式會建立一個 PrintTask
那這個Task做了什麼呢?

@Override
protected Communication.Result doInBackground(
                                         Integer... params) {
    byte[] commands;
    int    selectedIndex = params[0];

   PrinterSetting setting = new PrinterSetting(getActivity());
   Emulation emulation = setting.getEmulation();

   ILocalizeReceipts localizeReceipts 
                 = ILocalizeReceipts.createLocalizeReceipts(
                                      mLanguage, mPaperSize);

    switch (selectedIndex) {
        default:
        case 1:
            commands 
            = PrinterFunctions.createTextReceiptData(
              emulation, localizeReceipts, false);
            break;
        case 2:
            commands 
            = PrinterFunctions.createTextReceiptData(
              emulation, localizeReceipts, true);
            break;
        省略數行。。。        
    }

    Communication.Result result;

    result = Communication.sendCommands(commands
             , setting.getPortName()
             , setting.getPortSettings()
             , 10000
             , getActivity());     // 10000mS!!!

    return result;
}

首先我們會從PrinterSetting取得我們的Enulation

public Emulation getEmulation() {
    SharedPreferences prefs 
    = PreferenceManager.getDefaultSharedPreferences(mContext);
    int emulationInt = prefs.getInt(PREF_KEY_EMULATION, -1);

 for(Pair<Emulation, Integer> emulationPair : mEmulationList){
     if (emulationInt == emulationPair.second) {
         return emulationPair.first;
     }
 }
    return Emulation.None;
}

還記得我們方才用了 write 寫進了 SharedPreferences 嗎?
這裡雖然是 new 了一個新的 Object
也沒傳遞任何 Parameter 進來
但實際上卻是從我們之前寫好的 SharedPreferences 讀的
所以可以順利得到我們連結裝置的 Emulation

再來的 switch 則是依我們剛剛的選擇來 Create Data
下面是我們選擇的第一個 Data 建立過程

public static byte[] createTextReceiptData(Emulation emulation
        , ILocalizeReceipts localizeReceipts, boolean utf8) {
    ICommandBuilder builder 
    = StarIoExt.createCommandBuilder(emulation);

    builder.beginDocument();
    localizeReceipts.appendTextReceiptData(builder, utf8);
    builder.appendCutPaper(CutPaperAction.PartialCutWithFeed);
    builder.endDocument();

    return builder.getCommands();
}

依照Emulation來取得 ICommandBuilder
localizeReceipts來生產 Data
但 localizeReceipts 其實是 EnglishReceiptsImpl
所以生產 Data 的 Code 在這

public void appendTextReceiptData(ICommandBuilder builder
                                 , boolean utf8) {
        switch (mPaperSize) {
            case PrinterSetting.PAPER_SIZE_TWO_INCH:
                append2inchTextReceiptData(builder, utf8);
                break;
            case PrinterSetting.PAPER_SIZE_THREE_INCH:
                append3inchTextReceiptData(builder, utf8);
                break;    
            省略。。。
        }
    }

先依紙張大小選擇相應的 method

@Override
public void append2inchTextReceiptData(ICommandBuilder builder
                                      , boolean utf8) {
    Charset encoding;
    if (utf8) {
        encoding = Charset.forName("UTF-8");
        builder.appendCodePage(CodePageType.UTF8);
    }
    else {
        encoding = Charset.forName("US-ASCII");
        builder.appendCodePage(CodePageType.CP998);
    }
    builder.appendInternational(InternationalType.USA);
    builder.appendCharacterSpace(0);

    builder.appendAlignment(AlignmentPosition.Center);
    builder.append((
            "Star Clothing Boutique\n" +
                    "123 Star Road\n" +
                    "City, State 12345\n" +
                    "\n").getBytes(encoding));    
    省略。。。
}

一開始會設定文字是UTF-8 or US-ASCII
再來為 Builder 設定 Location、文字間距,置中對齊
最後才開始加人文字
完成之後,就可以送出 Command 了

 @Override
    protected void onPostExecute(Communication.Result result){
        if (!mIsForeground) {
            return;
        }

        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }

        String msg;

        switch (result) {
            case Success:
                msg = "Success!";
                break;
            case ErrorOpenPort:
                msg = "Fail to openPort";
                break;
            省略。。。
        }

        CommonAlertDialogFragment dialog 
        = CommonAlertDialogFragment.newInstance(
                            "CommResultDialog");
        dialog.setTitle("Communication Result");
        dialog.setMessage(msg);
        dialog.setPositiveButton("OK");
        dialog.show(getChildFragmentManager());
    }
}

Command 會 return 執行的狀態
把 msg Dialog 顯示給 User 看
就完成我們了這一次的列印了 !!!


參考資料:

www.starmicronics.com/support/sdkdocumentation.aspx#jpos
 

 

 

 

 

 

 


arrow
arrow
    全站熱搜

    顏澤偉 發表在 痞客邦 留言(0) 人氣()