Dropbox

既にAyamadoriさんがブログ「書くことないです。」に最新のNokia Series 40 Touch and Type端末のMIDP開発環境について書かれています。

私はQWERTYキーボードを中心に分かったことを書いてみたいと思います。

キーボード素直です。ずっとS60 3rdからフルキーボードのキーコードには悩み続けてきました。E7に至っては訳がわからないというのが正直な感想でした。それから考えると物足りないくらいに素直な挙動です。

E90などと違うのはCtrl+Cなどでコントロール・コードでは無く”C”そのものが返ってくることです。ただしCtrlキーそのものがキーコードを発生してくれるのでMIDPのアプリケーション内で処理可能です。また、CtrlだけではなくSymや数字シフトなどもキーコードが出ています。CtrlもSymキーもMIDPのCanvasでは何も働きませんので、逆に自分の好きなように役割を与えられるとも言えます。

カーソル移動キーが無い。「書くことないです」でも触れられていますが、方向キー、所謂10字キーが無いデバイスです。その代わりに画面を上下左右にスワイプすることで対応するキーコードが得られます。また左右のメニュー・キーもありません。これも画面のタップで代用します。S60 5thのタッチデバイスで表示される、あまり使いたいとは思わないゲーム・キーなバーチャル・キーボードは無くなったようです。

QWERTYキーで文字入力中に編集のためにスワイプというのは今ひとつな気もします。KKJConvではやはりキーボード操作でカーソルを移動できるようにしたいと思います。

液晶にタッチすれば何とか今までと同様の動作が出来ると思いきやダメなケースも見つかりました。Google Mapsは未だMIDP版が配布されていますが、これが全く操作不能でした。Google Mapsは画面を大きく使うためにCanvasをフルスクリーンで動作させています。検証はしていませんが、どうもこれが災いして画面のタッチが全く効きません。10キーだけは反応するですが、起動時に表示されたダイアログを消すことが出来なかったので、全く使い物になりませんでした。

Dropbox APIが新しくなったというニュースを見かけて、とあるアプリケーションに使ってみようと重い腰を上げテストしてみました。

iOS v1.0にはドキュメントが用意されていません。SDKに添付されているサンプル・プロジェクトが動くことを確認した後はtutorialに従って基本的に動作確認です。ところがファイルをアップロードしようとしたらupLoadFileがdeprecatedであると言われてしまいました。

ChagelogやDBRestClient.hを読めば事情は把握できるので良しとしましょう。せっかくのtutorialなので最新のAPIに変更しておいて欲しいものです。

新しいupLoadFileは、

- (void)uploadFile:(NSString *)filename toPath:(NSString *)path withParentRev:(NSString *)parentRev
    fromPath:(NSString *)sourcePath;

という形式です。parentRevと言うパラメータが増えています。ここにnilを指定してアップロードすることも出来ます。その場合、同じファイル名がサーバー上に存在する場合上書きをせずファイル名の後に(1)などと付加されて新しいファイルとして保管されます。現行ファイルを上書きする場合はloadMetadataにて現在のファイルのrevを取得しなければなりません。

ものすごく単純なコード例を書いておきます。
前提としてdocDirはアプリのDocumentフォルダーを指し、そこにファイルtest.txtが存在するものとします。またTutorial通りの手順でrestClientは初期化されているものとします。

- (void)uploadFile1
{
    [[self restClient] loadMetadata:@"/test.txt"];
}

-(void)uploadFile2:(DBMetadata*)meta
{
    NSString *testfile = [docDir stringByAppendingPathComponent:@"test.txt"];
    [[self restClient] uploadFile:@"test.txt" toPath:@"/" withParentRev:meta.rev fromPath:testfile];
}

// DBRestCleintDelegte
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata
{
    [self uploadFile2:metadata];
}

- (void)restClient:(DBRestClient*)client loadMetadataFailedWithError:(NSError*)error
{
    [self uploadFile2:nil];
}

revは元々、古いバージョンのファイルをリストアしたりするためのものと思います。詳しくは考えていませんが(ぉぃ、とりあえず動いてはいるようです。

久しぶりにiOSアプリケーションを更新しサブミットしたらInvalid Binaryになってしまいました。Xcode 4.2のArchiveからValidationは問題なし。Xcode4.2上ではSubmitも問題なしですがiTunes Connectを見るとInvalid Binaryと表示されていました。以前ですと問題があると何かしらエラーの理由が記されたメールが送られてくるのですが、それもありません。

まったく、手がかりの無いところから原因を探らねばなりません。Googolで検索すると、同様の現象について沢山の事例が見つかりました。ひとつひとつ検証した結果Info.plistでのアイコン指定の問題でした。

前提としてはiOS Deployment Targetが3.0なUniversal Applicationです。

最初は、

CFBundleIconFile icon.png
CFBundleIconFiles
  Item 0 icon.png
  Item 1 icon@x2.png
  Item 2 icon-72.png

みたいな感じでした。これで以前は問題なかったと思います。

ここに下のアイテムを追加してInvalid Binaryが解消しました。

CFBundleIconFile~ipad icon-72.png

本来ならXcodeのValidationでエラーを見つけてくれるべきなんでしょうね。試しにCFBundleIconFileにファイル名を消してみるとValidationの際にかなり詳しいエラーメッセージが出ました。

Nokia E7-00のKKJConv対応で、どうやっても解決出来無い部分が残ってしまいました(これはN97miniやN97から同じかも知れないです)。E7-00に搭載されているQWERTYキーボードがMIDPアプリケーションからどのように見えるかという問題です。

[ 改行キーでキーコードが発生しない ]

改行キーが何も役割を与えられていない訳ではありません。通常のMIDPアプリケーションで改行キーを押すと左側ソフトキーに対応するメニューが開くようになっています。タッチデバイスということで何かしらの配慮から、こうなったのかも知れませんがQWERTYキーボードとして使用する上では、かなり不自然で不便です。メニューの存在しない(正確にはリスナーを登録しない)テストプログラムも試してみましたが、やはりkeyPressed(int)やkeyReleased(int)でキーコードを得ることは出来ませんでした。

CtrlキーがあるE7-00ではCtrl+jで改行キーを代用出来るのですが、ここにも落とし穴(後述します)あるので’@’キーを改行キーの代用として割り当てています。

[ シフトモードが分からない ]

通常のシフト、数字記号用のシフト、あるいはSymキーを押すと通常の画面ではシフトモードが画面上部に表示されます。ところがMIDPでは、非フルスクリーンな表示でもシフトモードが表示されません。シフトモードは通常と同じく、一回押すとシフト、2回押すとシフトロックという動作なので、それを自分で把握して入力しなければ行けません。

E90やE63もMIDPではシフト状態の表示はありません。しかしながらシフトを押しても次に入力される英字には影響されず常に小文字が入力されました。つまりアプリケーションにシフト状態の管理が任されていました。それ故、何の表示もないことが正しかった訳です。またE63では数字記号モードはMIDPに入る前に効いていますが、それだけを使用者が自己管理すれば良かったので然程混乱することはありません。

[ 数字記号シフト、Symキーもシフトキーと同じキーコードが発生する ]

どれもシフトの仲間だからということでシフトキーと同じキーコードを割り当てたのかも知れませんが、それが役に立つ場面が思いあたりません。E90やE63では数字記号シフトは何もキーコードを発生しませんでした。それ故にシフト状態をアプリケーション内部で管理するということが容易に出来ていました。

KKJConvとしては、いっそのことシフト表示を止めてしまうかと思いましたが、今のところはシフト表示に合わせて大文字、小文字の相互変換を行っています。ロジックの都合によりシフトを押したままで英字入力が二文字目からは小文字に成ってしまいます。これも、致し方なしです。

[ Ctrl + 英字が効かない事がある ]

英字を入力する際、本体のシフトモードに応じてMIDPアプリケーションに渡されるキーコードが小文字だったり大文字だったりするのは構いません。ところが、Ctrlキーと英字キーを同時に押して発生させるコントロール・コードがシフトモードによって左右されています。シフトモードが大文字に成っている時にCtrl+Jが押されると、キーコードは’J’が発生してしまいます。E90, E63では常に期待どおりのコントロール・コードが発生します。特にE63では数字記号シフトにロックされていてもCtrl+jなら改行キーとして機能します。

Ctrlキーはもともと押下状態かどうかがMIDPアプリケーションでは感知出来ないのでKKJConv側では対処の方法がありません。

ps. 一縷の望みを託してForum Nokiaにてバグレポートいたしました。直ると良いなぁ。

春節前から上海の南京東路にあるノキア旗艦店にてNOKIA E7-00が展示されていました。先日、華南の同志とおもいっきりいじり倒してきましたので、その時の印象です(写真は、同志の撮影たものを無断使用 🙂 )。

まだ販売予定は世界数カ国ですので、今の時点で実機に触れるのは限られ場所と思います。という恵まれた状況にありながら自分の興味の中心にあることだけひたすらテストしていましたのでレビューになってないです。予めお断りしておきます。

[ キーボードはイケテます ] キーボードの開閉は、見たとおりで片手でディスプレイをスライドすれば気持よく開きます。この辺りはノキア、問題なしです。キーの押し込んだ時の感触が適度で携帯端末としては十分に優秀です。

[ サクサクとは言えないか ] 普段iPod Touchを使っているせいか画面の反応は少し鈍くも感じましたが、許容範囲でしょう。アプリケーションによってサクサクだったり鈍かったりしていたような気がします。徐々に良くなっていくことでしょう。

[ カメラは残念 ] Eシリーズでありながら写真の編集アプリが内蔵されていました。顔だけ変形させたりスタンプを押したりするお遊び系アプリです。その反面、搭載されているカメラはフルフォーカスと称する自動焦点の無いものです。このため、紙の書類を撮影しても文字が読めるほどに焦点は合いませんし、細かない部品を撮ろうにもマクロは効きません。最近のノキアはNシリーズのみがCarl Zeissブランドのオートフォーカス・カメラが搭載されていて、他のシリーズは全て自動焦点が有りません。ビジネス・ユーザーだってCarl Zeissじゃなくて良いので自動焦点なカメラが欲しいと思うのですけどね。

[ MIDPはどうやろ? ] さて、ここからが私の本題であるMIDP (Javaアプリ)関連の話になります。

Read the rest of this entry

練習で作ったiPhoneとiPadの両方で走るuniversal binaryのテスト・プロジェクトを公開いたします。自分自身のテンプレートとして使うために極力シンプルなものにしましたので、容易に読み解く事が出来ると思います。細かいところまで正しいコードを書いているとは限りませんが大きな方針は間違ったものでは無いと思います。

[ コードの権利 ]
コードの大半はXcodeが作り出したものです。また、ごく標準的なものですので、私自身が権利を主張するようなものは何もありません。また、無保証での提供となります。ご自身の責任においてご利用ください。

[ プロジェクトの概要 ]
iPhoneで起動するとTableViewを表示し、クリックによりTextViewにNavigationコントロールにより遷移する標準的なiPhoneアプリ(DrillDownって言うの?)です。同じbianryをiPadで実行するとSplitViewにて同様の動作を行ないます。簡単なプログラムですが、実際のアプリケーションで行うようにデータはUIとは別の共通クラスにより提供するようにしています。

[ プロジェクト作成の概要 ]
Xcode 3.2.3 SDK 4.0.1を使用して作成しました。プロジェクト自体を公開していますので、全てはそれを見て頂ければ良いのですが、どうやってiPhoneプロジェクトからuniversalなプロジェクトに変更していったのかなどを理解いただくため大きなステップを紹介いたします。

< iPhoneアプリの作成 >
– 新規プロジェクトをiPhone用のNavaigation-based Applicationとして作成します。
– TableViewをタップした後に表示するTextView用のクラスとnibを’DetailViewController’として作成。ごく一般的な内容なので詳細はプロジェクトを参照ください。
– データを供給するクラス’MyEngineを作成。RootViewControllerに組み込みます。
ここまででiPhoneアプリとしては完成です。

< Universal Binary化 >
– ターゲット UniTest2を選びプロジェクト, 現在のターゲットを iPad 用にアップグレードする。この時’One Universal application’を選択する。
– UniTest2AppDelegateクラスのUINavigationControllerのプロパティをコメントアウトし代わりにUIViewControllerクラスのプロパティを作成します。元のMainWindow.xibの該当接続を貼り直します。
-> 起動時にiPhoneかiPadかでinfo.plistを参照しMainWindow.xibかMainWindow-iPad.xibが選択されま す。ただしWindowにviewを割付るのはAppDelegateクラスの中なので、ここの接続を正しく行う必要があります。
– 内容を表示するためのTextViewを含んだクラスおよびnibを’DetailViewController-iPad’という名前で作成する。’Targeted for iPad’オプションを忘れないように。このクラスはUIPopoverControllerDelegateとUISPlitViewControllerDelegateになります。クラス名はDetailViewController_iPadに成っているので注意。
– TableViewControllerのサブクラスとしてRootViewController-iPadというファイル名でクラスを作成します(クラス名はRootViewController_iPadになってしまうのでファイル名は別の方が良かったかも)。これはUISplitViewのtable部分のcontrollerになります。
– iPadで最初に表示されるviewを含むMainWindow-iPad.xibを作成します。MainWindow-iPadという名前はinfo.plistで既定値として指定されているものです。これにSplit View Controllerを追加し各種接続を行ないます。

KKJConvのプログラム内でランドスケープ、ポートレイトの切り替え(すなわち画面の回転)が出来ないものかと調査してみました。APIドキュメントを眺めていただけでは分からなかったのですが本家Sunのフォーラムに、そのものズバリの答えが書いてありました。それを読んでいただければ内容は分かるのですが、自分自身のメモ代わりにもなるので簡単に解説を書いておきます。

自前でバッファーを用意します。大きさは画面を横向きにしたものにします。そこに通常通りGraphicsクラスを使用して画面の描画を行います。その後 drawRegion メソッドで本物の画面に回転方向を指定して内容を転送するだけです。

私のテストプログラムでは、Canvasクラスを継承したもののクラス変数としてバッファーを定義、

Image imgBuff;

コンストラクターで領域を確保します。

imgBuff = Image.createImage(iWidth, iHeight);

iWidthは実画面のHieght, iHeightは実画面のWidthが入っているものとします。paintメソッドにて、このimgBuffに描画して最後に実画面に反映します。

public void paint(Graphics real_g)
{
    Graphics g = imgBuff.getGraphics();
    // g に対して通常のdrawを行う
    real_g.drawRegion(imgBuff, 0, 0, iWidth, iHeight,
    Sprite.TRANS_ROT90, 0, 0, Graphics.LEFT|Graphics.TOP);
}

モバイル以外のプログラミングについて書くのは初めてかも。

簡単だと思って始めたらハマってしまうのはプログラミングには、よく有ることですがwebプログラミングでは、その可能性が高くなります。ドキュメントで良く分からない場合は、Google先生に頼れば実例が見つかるものですがwebプログラミングの場合、その検索結果を見ても混沌とした状況に陥る場合があります。今回は、その一例です。備忘録として書き残しておきます。

webページの中にイメージ等を表示して、その中をクリックしたりマウスを動かしたりしたときに、そのマウスの位置をイメージ内部の相対的な座標として得る方法です。マウスをクリックしたり動かした時のイベントをjavascriptで得る場合IEを除いては要素内では無く、ウインドウなどを基準とした座標が返って来ます。ただしwebブラウザーによって扱いが様々なのが現状です。

では、イメージなどの要素の位置が得られれば、イベントで得られたマウスの位置との差分を取れば目的は達成できる訳ですが、要素=オブジェクトが複雑にweb画面上に構成されていると生のjavascriptだとかなり面倒なコーディングが必要となります。

こういった事をなるべく簡単に処理するために幾つかのjavascriptライブラリが公開されています。その一つであるjQueryを使用したところ、かなり簡単に処理することができました。

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('img#test').click(function(e){
var x = e.pageX - $('img#test').position().left;
var y = e.pageY - $('img#test').position().top;
alert("X=" + x + " Y=" + y);
});
});
</script>

上のようなコードがヘッダー部分にあったとして<img id="test"で始まるイメージの内部をクリックするとイメージ内部の相対的な座標が表示されます。

5800XMでMIDPアプリを動かすのは一部難があります。というのもハードウェア・キーを省略し徹底的にタッチ・スクリーンを活用するようになっているからです。MIDPアプリに対しては組み込み済みJVMが提供するバーチャルなキーボードが使えるのですがキーが限られたものになっています。ナビキー+αではKKJConvは言うに及ばずPiClipなども動かせません。ちなみに現行の5800XMではApplication Managerで設定することにより2種類のバーチャル・キーボードとキーボード無しの状態がMIDPアプリに対して選択可能です。

ではMIDPアプリ側から5800XMに対応するための方法を探ってみようと考えてみました。MIDP2.0のCanvasクラスにはpointerPressed(int x, int y), pointerReleased(int x, int y), pointerDragged(int x, int y)といったタッチ・インターフェースにも使えそうなコールバック・メソッドが備わっています(必ずしもタッチとは限らなくて他のポインティング・デバイスも対象と思います) 。また、それらのイベントが有効かどうかを調べるboolean hasPointerEvents()というメソッドもあります。

と、いうことで簡単なテストプログラムで試してみました。

public void pointerReleased(int x, int y)
{
    iPointX = x;
    iPointY = y;
    this.repaint();
}

えらく単純な部分だけの紹介ですが、これに加えてpaintルーチンの中でiPointXとiPointYの数値を画面に出すようにしておくとタッチした場所に応じたx, yが分かるというものです。

Nokia S605thエミュレーターで試してみると、ちゃんとタッチ・イベントを拾うことが出来ました。余談ですが以前はS60エミュレーターとMIDPのエミュレーターは別でしたが5thからは一つのエミュレーターで両方のテストが出来ます。MIDPのテストは、エミュレーターのFile/OpenでJARファイルを選び、実機同様にインストールを行います。

これで低レベルなタッチ・イベントが取得出来ることは確認できました。ゲームやアプリ独自のバーチャル・キーボードなどは、これで作成出来ますね。ってKKJConv Virtual Keyboardは期待しないでね (^_^;)

標準UIの提供する部品については何もしなくても概ねタッチすると反応するみたいです。ということで10キーなどに操作を頼っている部分だけタッチ・イベントの取得で代替すれば何とかなりそうです。今後S605thデバイスがメジャーになってくると対応するMIDPアプリも増えてくるものと期待します。

タイトルは何だか偉そうですがListBoxを少し試してみただけです。

5800XMを入手して最初に導入してみたのはptsNotesです。互換性に問題がある旨のメッセージが出ますが導入することは出来ます。色々と問題は有りますが、ごく基本的な動作は可能です。メインのリストをタッチしてノートを開くことも出来ました。これは、予めタッチで発生するイベントを組み込んでいたからです。

void CPtsNotesMainList::HandleListBoxEventL(CEikListBox* aListBox, TListBoxEvent aListBoxEvent)
    {
    if ((aListBoxEvent == MEikListBoxObserver::EEventEnterKeyPressed)||
        (aListBoxEvent == MEikListBoxObserver::EEventItemClicked))
        {
        // ノートを開く処理
        }
    }

みたいな感じで書いていました。このプログラムはS60 3rdMRというSDKで作成していますがEEventItemClickedというイベントが5800XMの画面タッチで捕らえられていることが確認できました。

S60 5thの流儀ではダブルクリックで文書を開く方が良いようですしptsNotesではリストのアイテムを洗濯すると画面の一部に内容のプレビューを表示する機能も働かせたいです。そこで、下のように変更してみました。

void CPtsNotesMainList::HandleListBoxEventL(CEikListBox* aListBox, TListBoxEvent aListBoxEvent)
    {
    if ((aListBoxEvent == MEikListBoxObserver::EEventEnterKeyPressed)||
        (aListBoxEvent == MEikListBoxObserver::EEventItemDoubleClicked))
        {
         // ノートを開く処理
        }
    else if (aListBoxEvent == MEikListBoxObserver::EEventItemClicked)
        {
        // プレビューの処理
        }
    }

ということで、何となくタッチイベントも処理できるようにはなりました。しかしながら、他に不可解な問題が散見されているので、実行ファイル自体はS60 5thのSDKを使ってビルドしないとダメかも知れません。少なくともS603rdのAPIに定義されているイベントが、そのまま使用できるのでソースコードは手を加えることなく共用出来そうです。