2017年5月22日 星期一

在 Android 的 Edit 中, 如何按下 Enter 即隱藏虛擬鍵盤

在 Windows 的應用程式中,我們常常為了讓使用者能夠快速輸入,在Edit元件中的onKeyUp或者 onKeyDown 事件中主動偵測使用者輸入的字元是否有換行符號 (Enter),當使用者按下了Enter,程式碼就主動把游標 Focus 帶到下個欄位,但在行動裝置中,又多了一個課題:『如果是多個欄位,就帶到下個欄位。但如果是單一欄位,或是最後一個欄位,就隱藏虛擬鍵盤』

這個課題筆者在 2014九月的筆記『Virtual Keyboard 的顯示與隱藏』裡面已經有介紹過一次,只是當時是聚焦在iOS系統上,因為當時筆者開發的平台大都聚焦在iOS,對Android系統的關切並不多。

最近在一些專案中,也把觸角伸到了Android平台,但筆者仍堅持『不到最後關頭,絕不輕言JAVA』的原則,認識筆者的朋友們也都知道,筆者會寫,但非不得已,絕不寫JAVA的原則。

加上Delphi從XE6之後,對Android的支援也很深入了,筆者就更能堅定這個立場。

言歸正傳,在TEdit元件中要在使用者按下虛擬鍵盤的Enter鍵時,隱藏虛擬鍵盤的話,要怎麼做呢?

顯然用2014九月那個作法(把focus轉移到別的元件), 對Android平台沒用,不然筆者也不用多寫這篇筆記了。

是的,熟習Android的使用者都知道,按下Android的實體Back鍵,就能隱藏虛擬鍵盤。所以這個作法也很簡單,就是『在onKeyUp事件中,把Key 改成 137 即可』

onKeyUp 事件中,有兩個 call by Reference 的參數,一個是 Key, 另一個是 KeyChar, 都讓我們可以在處理完鍵盤事件後,重新賦予新的值給系統。在Delphi Seattle, Berlin, Tokyo (前面的版本我已經移除了,無從驗證),這三個比較新的版本中,都定義有 vkHardwareBack這個值,其數值就是 137。

只需在 onKeyUp 事件中把 137 指派給 Key 這個變數即可隱藏鍵盤了,但通常我們程式碼可能會用於多種平台上,所以我的範例程式碼會加上 {$IFDEF Android} 這樣的判別式,以利多平台的程式維護:

procedure TFormMain.Edit_changeDeviceNameKeyUp(Sender: TObject; var Key: Word;
    var KeyChar: Char; Shift: TShiftState);
begin
   if Key = vkReturn then begin
      self.btn1.SetFocus;

{$IFDEF ANDROID}
      Key := vkHardwareBack
{$ENDIF}
   end;
end;
這樣應該很容易理解,對嗎?

2017年5月19日 星期五

在 iOS 10 之後, 使用手機相機與相簿應注意事項

iOS 的 SDK 每一年至少都會有一次大改版,從 2009 到 2016 年,版號已經到了第 10 版了,很輕易的就追上了 Mac OSX. 每一次的大改版都會有不少新的功能或新的規範,在 iOS 7的改版算的上是幅度最大的,把 iOS 1.0 到 iOS 6.0 以實物視覺與立體視覺的基礎來了個髮夾彎,轉向去跟 Android, Windows 一起走平面化極簡風。(不知道賈柏斯天上有靈會怎麼反應......)
  • iOS 8的改版中,要求使用地理資訊的App需要在 info.plist 當中自己寫出提示的文字
  • iOS 10 的改版中,更進一步要求使用相機、相簿的App也要自己提示。
如果沒有在 info.plist 裡面定義提示文字,App執行到該功能的時候,會直接發生閃退,這對於使用者來說很不方便,所以App開發的人員也只好為了使用者對 App 進行改版。

如果是使用 Xcode 來開發,就需要編輯 info.plist, 加入以下圖片中兩個灰色背景的設定,一個是使用相機,另一個是使用相簿。



這跟是否使用第三方的 Framework 無關喔,即使您使用了 Cocos2D, 或是 CocoaPad, 這些宣告也是跑不掉的。

如果使用的是 Delphi,則需要從專案設定選項 (Project Option)來設定,設定視窗如下圖所示:


叫出這個視窗的方法有兩個,一個是從Delphi的視窗中點選 Project 選項,選擇裡面的 Option。另一個則是用滑鼠右鍵點選視窗右邊的專案名稱,選擇 Menu 裡面的 Option 選項,都可以顯示出這個視窗。

設定好這兩個選項之後,在 Delphi 裡面呼叫 TTakePhotoFromCameraAction 或
TTakePhotoFromLibraryAction的時候,就不會閃退了.

要留意一點,透過 Delphi 取用相機或相簿的時候,OnDidFinishTaking 所取得的照片是 TBitMap 元件,我們可以先對圖片做一些簡單的處理,例如可以把圖片的 解析度弄小一點,或者改變圖片的大小,甚至是儲存成不同格式 (PNG或JPG)。

對 iOS 模擬器處理時,由於模擬器通常沒有相機,所以不用特別設定模擬器的相機使用描述。Delphi 的設定會依照不同的裝置平台做個別的設定,而且還分成 Debug/Release,所以我自己通常會直接編輯 All Configuration。

從 iOS 5S 之後,也已經沒有 32 bit 的新裝置了,所以如果您的 App 不打算提供給 32 bit 裝置使用的話,iOS Device - 32 bit platform 也可以不用理他了。



2017年5月12日 星期五

Delphi 快速使用 AES 加密的方法

從 1994 年開始,筆者就開始接觸加密與網路安全的世界,從魯立忠老師的指導當中獲益良多,後來在元智就讀研究所的時候,也以此為研究主題。

在當時,電子商務是顯學,Visa跟 Master Card還特別為了網路交易製作了厚厚三大本的商務通訊協定,命名為SET (Secure Electronic Transaction,安全電子交易),從客戶端、商店端、銀行端定義了綿綿密密的交易規範。

然而,網際網路的世界跟 Visa Master Card所熟悉的專用網路世界差的遠了,不是大狗們(Big dogs)說了算,很快的 SSL 128 被吹捧成『最安全的交易保護機制』,每年透過這『最安全的交易保護機制』成交的金額越攀越高。

破解網路而得逞的網路詐欺,始終維持在一個很低的比例,反而從商家端流出的詐欺資料年年創新高,SET也很快的成為一個歷史名詞。

但是,SET所本的一些加密基礎,並沒有就此被埋沒。X.509電子憑證、RC4, RC5, DES, 3-DES, RSA, SHA-1, SHA256, SHA-2, 還有我們這次要介紹的AES,也不斷的推陳出新,在世界上蓬勃發展。這些聽了令人打呵欠的主題跟名詞,在很多地方都會被用上,只是用了不同的面貌呈現給使用者而已。

像是在自然人憑證、健保卡裡面,都有個人電子憑證(X.509),每年五月我們都可以用這兩種憑證進行網路報稅。

或是像電子發票,當中就需要用到 AES 加密,依據財政部的『紙本電子發票二維條碼內容規範』第五頁所述:


左方二維條碼裡面,就需要用到 AES 對發票字軌10碼及隨機碼4碼以字串方式合併後使用AES加密,並採用Base64編碼轉換。

但是,在Delphi裡面好像沒有可以直接使用AES加密的單元可以使用。筆者在碩士論文的程式撰寫時,使用的是OpenSSL 0.4的函式庫,當時還叫做SSLeay呢。但是,這作法只能在Windows 平台上面順順的用,有沒有什麼方法可以讓我們在不同的作業系統下都能順利使用 AES 呢?

經過約莫兩三個小時上窮碧落下黃泉的搜尋,找到了一個在 SourceForge 上面的加密範例程式,更棒的是,它是用 Delphi + FireMonkey 寫的,不使用 DLL,而是使用純粹的Pascal 寫的 (感謝 Eldos 的 OpenSource, 但直接到 Eldos 網站的連結目前已經找不到了)。

換句話說,這是一個跨平台都可以正常運作的 Delphi 程式,不用依靠載入的 DLL 或 Lib,用這個範例來製作電子發票的驗證加密字串,就能夠很方便的達成了。

Source Forge 的範例程式可以從這個連結下載,下載之後,請看到裡面的範例專案『FlyUtilsAESCBC.dproj』,這個範例程式中,支援用字串作為AES加密金鑰(Key)對文字進行加解密,執行起來的畫面也很清楚,筆者做了一點點修改,修改後的執行畫面如下圖所示:
 

左圖是未執行加密作業前的畫面,右圖則是執行了加密作業之後的畫面。原始的範例程式中,只支援完整的字串作為 AES 金鑰,但我們常會用到二進位資訊來做金鑰,這種情形下,金鑰通常也會經過Base64編碼過。

所以筆者稍微改寫了一個function,新增了一個按鈕,就是畫面最底下的『AES加密with Byte Key』這個按鈕的event handler。

如果點選按鈕是 AES加密,則Key裡面的字串不會被做任何處理,直接會被當成AES金鑰,點選的如果是最底下的『AES加密with Byte Key』,則Key裡面的字串會先被做Base64 解碼,變成二進位資訊,AES金鑰就是這些二進位資訊了。

procedure TFormMain.Button1Click(Sender: TObject);
var
  KeyBit: TKeyBit;
  APaddingMode: TPaddingMode;
begin
   KeyBit := TKeyBit.kb256;
   APaddingMode := TPaddingMode.pmZeroPadding;
   Memo2.text := AESEncryptStrToBase64_Base64Key(Memo1.Text, Edit1.text, TEncoding.UTF8, KeyBit, '', APaddingMode, CheckBoxCBC.IsChecked,
      rlCRLF, rlCRLF, Process);
end;

這兒的 AESEncryptStrToBase64_Base64Key 是筆者照著原本 Eldos 的程式做了一點小手腳,方便大家把電子發票平台取得的金鑰直接貼上來就能用:
function AESEncryptStrToBase64_Base64Key(Value, Key: string; StrEncoding: TEncoding = nil;
  KeyBit: TKeyBit = kb128;
  InitVectorStr: string = '';
  APaddingMode: TPaddingMode = TPaddingMode.pmPKCS5or7RandomPadding; CBCMode: Boolean = True;
  ValueCRLFMode: TCRLFMode = rlCRLF;
  KeyCRLFMode: TCRLFMode = rlCRLF;
  OnProcessProc: TOnProcessProc = nil; ProcessProc: TProcessProc = nil): string;
var
   tStrm : TBytesStream;
   keyBytes: TBytes;
   IVBytes: TBytes;
   IdDecoderMIME1 : TIdDecoderMIME;
begin
   tStrm := TBytesStream.create;
   IdDecoderMIME1 := TIdDecoderMIME.Create(nil);
   try
      IdDecoderMIME1.DecodeBegin(tStrm);
      IdDecoderMIME1.Decode(Key);
      IdDecoderMIME1.DecodeEnd;

      keyBytes := tStrm.Bytes;
   finally
       tStrm.Free;
   end;

   tStrm := TBytesStream.create;
   try
      IdDecoderMIME1.DecodeBegin(tStrm);
      IdDecoderMIME1.Decode(InitVectorStr);
      IdDecoderMIME1.DecodeEnd;

      IVBytes := tStrm.Bytes;
   finally
       tStrm.Free;
   end;
   IdDecoderMIME1.Free;

  Result := EncodeBase64Bytes(AESEncryptStr_BytesKey(Value, keyBytes, IVBytes, TEncoding.UTF8, KeyBit, APaddingMode, CBCMode, ValueCRLFMode,
    KeyCRLFMode, OnProcessProc, ProcessProc));
end;

單獨使用這個 function 的話,ProcessProc 參數可以給 nil, 這是用來讓大家看到有進度列可以顯示處理進度用的,通常這些處理是在背景進行,沒有介面的時候直接給個 nil, 就可以不用管進度條了。

大家可以看到,上面的程式碼裡面,筆者使用了Indy的 Base64解碼元件『IdDecoderMIME』,因為一來它是原本 Delphi 安裝就內建的,使用上比較方便,二來筆者也熟悉這套元件,所以不另外找其他元件了。

AES 裡面除了 Key 之外,還可以指定 IV (initialization vector), 如果使用上需要使用二進位的 IV, 您可以把二進位的 IV 先做好 Base64 編碼,這個 function 也會將它先做 Base64 解碼之後,作為 IV 進行處理的。

寫到這裡,說明的差不多了,範例程式專案我也準備好了,有興趣的讀者請自行取用吧。範例在此