2013年12月28日土曜日

数年ぶりにパーツボックスを発掘

意外なところにありました。当面必要な物はひと通り。


ピンヘッダも貫通、オスメス両方揃ってます。


チタコンやTTL/CMOSの印刷は消えてしまって読めなくなってる。まぁ老眼ってこともあるんだけど。しかし、セメント抵抗を今後使うことはないだろなー。


これなんだろ。フォトカプラー? ご存知のかた、コメントいただけると幸いです。

2013年12月5日木曜日

システム構築のうちとそと

近所の石碑…言葉選びって難しいわ

はてなの匿名記事エクセルでできることができない何百万のシステム・・を読んで。

私はずっとこれを言われてきた人間で、Excel登場前からSOHO向けのビジネスアプリを受託開発してきました。ただ、Excelどころかパソコンのない会社にシステムを導入する場合=システムを導入しなければならないほど定型化した業務が増えてきた場合には、ビシッとした伝票と帳簿システムが固まっている場合が多いんですよね。

なので、期首、月次、週次、日次、都度ぐらいで処理のタイミングを区切って、そこでどんな伝票を起こしてどう記録するかをヒアリングするところから始めてデータの流れに置き換えていき、無駄な処理をあぶりだしたり、一部利用者から「面倒だ」と文句が出るかもしれないけど必要な処理を提案したりでシステムを設計>開発しました。

そこにExcelなどのスプレッドシートが登場します。最初にお目にかかったのはTRS-80版Visicalcだったかな。Multiplanの時代まではあまり「被害」はありませんでした。むしろリレーショナル型データベースと相性が良いので、入力と出力がはっきりしていて打ち合わせもラクでした。それまでプリントアウトや伝票のコピーに赤ボールペンで注釈入れていたのが、キーボードの操作だけで完成イメージに近づけることができるようになったので誤解が生じる余地は少なくなりました。少なくとも帳票デザインに関しては「これで」「わかりました」で大体片付いてたと思います。なにより出来上がってから「これ、イメージと違うよ」ということはかなり少なくなりました。

そこにExcelです。上記記事にある通り、Excelのデータ形式はとても自由です。主要部分を定型化しておいた上で、ちょっとした例外処理を手作業で簡単に追加できるし、年に2回しか使わない帳票であっても慣れている人であれば作ることができます。

この「自分でも作ることができる」という誤解をユーザに与えたのがExcelの最大の罪だと思います。

日本人なら誰でもおにぎりを作れると思いますが、お客さんの心の中にしかないおにぎりの味やら姿やら食感をすべて定義した上でその作り方をコンピュータというやたら頭の回転だけは早い愚か者に一字一句間違いなく教えないといけないんですよね。うっかりすると、「天むす」を頼んだはずが「たぬきうどん」になっちゃうかもしれないし(違

それでも「Excelなら簡単にできる」って言われたら…「おにぎりの作り方を教えることがいかに大変か」と話しても不毛なので、どうしてExcelを使わないのかを相手自身に説明させて自身で理解してもらうところから始めましょうかね。要件定義もトラブルシューティングも大事なのは切り分けですから。

--

なお、タイトルは私が学校行ってたころの名著「コンパイラのうちとそと」へのオマージュです。擦り切れるまで読んだっけなぁ。

余談の余談ですが私は当時BASICのコーディングのアルバイトをしていました。単価は1行100円、コンビニのバイト時給が400円の時代にすげえ!と思ったもんですよ…最初の2週間ぐらいはねw

2013年12月4日水曜日

シールドに関する挫折

Amazonで「サインスマート Arduino(Uno) をはじめよう互換キット(19プロジェクト:LCD、プロトタイプシールド、XBeeシールド付)」 を買った。


それとは別にXBeeエクスプローラUSB を買った。XBee端末(?)が二つ揃った。これでこの週末はXBeeで遊べる!
…と思ったら。どっちもArduinoに載せるためのシールドだけで、XBeeはありませんでした。というわけで、スイッチサイエンスさんに、XBee ZBを2個、ついでにEtherシールドとPoEアダプタを注文しました。

ついでの方が高額ですが。

2013年11月20日水曜日

ひさしぶりにArduinoで遊んでみる:TGS-2450ニオイセンサー

■追記■

簡単に使える空気質センサーで作りなおしてグラフも描画してみました(2016-05-27)。

■ニオイセンサー■

秋月で2個買って来た(ヒーターの制御間違えるとすぐ焼損するらしい)TGS-2450硫黄化合物ガス検出器を使ってみた。

TGS2450は硫化水素などの他にアルコール、アンモニアなど「臭い」を構成する物質をうまいこと検出してくれる素子。ニオイセンサーの完成品は人間の感性に近づけるための回路やら他のセンサーでかなり高いんだけど、この1個300円のセンサーでも「今日の俺、臭い?」かどうかを客観的に知る程度には十分使える。

ADコンバータでそのまま読み取るには抵抗値の変化幅や電源の条件が少し厳しいのでオペアンプを使いたいところだけど、手持ちの在庫がないので我慢。この辺の記事の通り作ってみた。

本格的にArduino > においセンサー

ベースはスイッチサイエンスさんのArduinoをはじめようキット。この他に100Ωの抵抗一袋とトランジスタ2SC1815を一袋、秋月で買って来た。ArduinoとMac/PCとの接続はA-BタイプのUSBケーブルが必要。外付けHDDなどを買うと付いてくるけど、もし手持ちがなければ忘れずに買って来ること。


キットのブレッドボード上に配線、抵抗の足はピンセットなどのエッジを使って曲げるときれいに直角になります。で、適当な長さにカット。長さ揃えた方が仕上がりが綺麗になります。ブレッドボードへのレイアウトはまず足の多い素子から先に配置し、縦方向(a-e, f-j)の穴が足りなくならないように適度にジャンパで逃がして抵抗でまたいだりして決めて行きます。レイアウトが決まったら、ピンセットを使って背の低い素子から深く差し込みます。この写真はまだ途中、完成品撮影するのを忘れた。このあとトランジスタのエミッタから電源につながるジャンパとArduinoにつながる配線(5V電源、GND、スイッチング用デジタル出力2本、アナログ入力1本)を差し込みます。

配線を間違えると良くて動かないかTGS-2450損傷、ヘタすればArduinoまで死にます。滅多にないですが、最悪の場合はMacが死ぬこともありますので、慎重に慎重にチェックしてください。回路図では電源とGNDラインが省略されていることが多いので、そのへんもお忘れなく。


開発ツールのArduino IDEは、Mac版をダウンロードしてインストールするだけ。ドライバは不要。今回使ったのは、安定版の1.0.5。インストールが終わったら、ArduinoとMacをUSBケーブルで接続します。起動したらツール>シリアルポートで、/dev/cu.usbmodem621を選ぶ。末尾の数字は変わるかもしれません。

上記「Arduino @ ウィキ」さんのページからソースをArduino IDEにコピペ。cmd+Rでチェック&コンパイル&実行開始です。数秒するとArduino上の黄色LEDが約1秒間隔と0.25秒間隔で点滅しているはずです。ダメならとりあえずUSBを引っこ抜いて、配線を確認してください。

ツール>シリアルモニタでモニタ画面を開きます。1秒ごとに何か表示が出ます。化けているようなら、通信速度を19200bpsに切り替えます。電圧が1秒に1回、表示されます。ってまたスクリーンショット撮り忘れたわ。

2.5Vぐらいから、ゆっくり数分かけて4V近くまで上昇します。で、数値が安定してきたところで、センサーをなるべく臭くなるように「ハー」っと吹いて見ます。すぐに数字が下がるはず。ティッシュにアルコールなどを染み込ませて近くで振っても下がります。臭いほど下がります。

これで出来上がりです。このままでは持ち運びも不便なので、装置として使うためにはユニバーサルボードを買って来てハンダ付けします。単体で使うには電源と表示器とケースが必要です。PC/MacとUSBと接続したままで使うなら、ADから読み込んだ値を適当な数値に変換してシリアルポートに流すようスケッチを変更し、PC/Mac側はなにか適当なターミナルソフトかターミナルでシリアルポートから読み込んだ値を表示してやれば最低限動きます。

さて、次は何をしようか…。

余談ですが、我が家には死蔵していたテクトロのSTamigoというハンディオシロがあります。テスターとしてもオシロとしても使えて電池で動く、当時としては画期的デバイスだったのですが…さすがに古いせいかオシロ機能がイマイチ正しく働きません。連続してパルス出力が来ているのに(LEDで確認)、表示は4個だけ出てあとは沈黙したり。とりあえずテスタとしては正常に使えるので、良いんですけど…ごめんよ、何年も放置して。


2013年11月13日水曜日

3Dプリント基板

3Dプリンタでプラスチックと銅を盛れるようになったら、ご家庭で簡単に3D構造のプリント基板を作れるようになるなぁ。部品は表面実装するしかないか。

でも、24層基板基板とか12層ドーナツ状プリント基板なんて出来たらいろいろ遊べる。

ただし『クラインの壺』は禁止で。

2013年10月24日木曜日

MavericksのMail.appがgmailに対応していない

gmail.comのメールボックスがこの状態

Mavericksにアップデート、Mail.appは一件正常に起動していますが、「全表示」ではなくgmail.comアカウントのmailbox単体で見ると中身が空になっています。

当該gmail.comアカウントにメールを送信すると、メールボックスにはその1通だけが表示されます。

同期、再構築、一旦アカウントを削除して再登録…など試みましたが、空のままです。

ブラウザから当該gmail.comをアクセスすると以前のメールはそのまま残っていますし、iPhone上では正常に表示されるので消えたわけではないようですが…早急に対策して欲しいものです>Apple

■追記 2013/10/28■

コメントにて、GmailにWebブラウザからアクセスして設定を変更したら解決した、とご教示いただきました。

MacOSX 10.9 Mavericks でのGmailの不具合を解決できたみたい(^^)

残念ながら私のところはすでにそういう設定だったのですが、逆に「非表示」に切り替えたら動いてしまいました。何でしょうねぇ…動いたから良しとします。

ありがとうございました>shigeさん

■追記 2013/11/19■

アップルからアップデータが出ましたね。再ロード(再インデックス?)にかなり時間かかりましたが、とりあえずInboxについては正常に戻りました。

2013年10月22日火曜日

Xcode5にしたら起動しなくなった

品川にて
アップデートが終わり、起動。インストールするから認証してくれ、なんてのを通りすぎたところで即死。ログ見てもこれぞというネタは思い当たらない。何度試しても同じ。マシンを再起動しても同じ。.xcodeprojをダブルクリックしても勿論ダメ。

対策として、

  • Xcodeを削除して再インストール
  • /Library/Developerと~/Library/Developerを削除

したけど、変わりなし。とりあえずhome以下が悪いのか否かを判断するために他のアカウントでログインするとちゃんと起動する。

最悪、開発作業だけ別アカウントでやるしかないなぁ、まったく起動しないよりはマシだし。で、別アカウントで「他のツール」とやらのインストールが終わるのを待ってから元のアカウントに戻る。

…起動しました。

何だったんでしょうか。

2013年9月19日木曜日

HDレコーダー:さよならRD

■過去3代RD■

RD-XS41、RD-XD91、RD-BZ800と過去3代RDでした。

その前はVHSとHDがついたビクター製、これがまだ動くから凄い。見習えよ>東芝

さて、RDは最初からトラブルの多いやつでした。特に予約録画:

  • 予約したのに音声とCMの静止画がずーっと入っているだけ
  • 予約録画したらただただ真っ黒
  • 何の説明もなくすっぽかし
  • バグが原因で2回フォーマットかけるはめになった41
  • とにかく予約録画失敗が多かった91
  • 突然外付けHDDを認証できないとか言い出したBZ800…。

それでも他社製同世代機と比較すると容量の割に安いしパソコンとの連携でできる事が多いので3代も買っていました。

でも、さすがに疲れました…だって、もうエエ加減安定したと思っていたBZ800ですが、よりによって「きんいろモザイク」の録画に失敗しやがったのですから…。


「BZ800世代のRDは最悪だった。でも、今は良い」という人もいます。41の時にも91の時にも同じセリフ聞いたよ、お前はボジョレー・ヌーヴォーかよ。

■機種選定■

というわけで、3番組同時録画の中級機、一応ソニーかパナソニックに狙いをしぼり、とりあえず買った人がレビューを書くAmazonと炎上気味のKakaku.comを参考に、ソニーBDZ-ET2000とパナソニックDMR-BZT750を候補としてヨドバシへ。

なお、3番組同時録画と書きましたが、ソニーはスカパーを含む3番組、パナソニックは3番組+スカパーです。AT-X愛用者の私としてはこれは結構重要な点ですが、最後の最後まで気づきませんでした。ソニーは操作の軽快さで定評があります。



■店頭にて■

実際の操作してみると、ネット上の評価と違ってソニーは意外ともっさり。もちろんRD-BZ800と比べればそこそこ速いんですが、番組一覧のリフレッシュに10秒以上かかるのはちょっとストレスが大きい。一方でパナソニックは爆速じゃった。リモコンを押しっぱなしにしていると、まるでひっかかることなく文字が読めないスピードでスクロールしていく。

機能面ではRDの美点だったフォルダ機能は最近の中級機には搭載されていないのだとか。それは困る。番組別にわけたり、「ヨメがあとで見るアニメ」「私が後で見るかもしれないアニメ」という分類でとりあえず投げ込んでおくことができない。店員さんに尋ねたところ、ソニーには所有者?みたいな機能でタグ付けすることができ、パナソニックは複数の番組を一つにまとめたりバラしたりすることができる。うちの場合とにかくアニメが多いので、まとめる束数に制限のないパナソニックの方が使いやすいと判断。

爆速とまとめ機能、3番組+スカパー同時録画、パナソニックの圧勝でした(※個人の感想です)。

店員さんには30分ほど付きあわせてしまったので、値引きなし(一応交渉はしたけど、無しだったのであまり押さずに1回で諦めた)で購入しました。

■DMR-BZT750を使って■

電源onの起動もまるで一昔前のアナログ液晶か予熱付きブラウン管のような速さ。何より笑ったのが、NHKのデータ放送。RDだとボタンを押してから早くても20秒ぐらいしないと天気予報に辿りつけないんだけど、パナソニックは押した瞬間に出てきやがる。こんなに使える子だったのか、とデータ放送を見直しました。

スカパーからNASに録画した番組もローカルHDの番組も全表示で一覧表示でき、爆速スクロールする。スカパーの番組も問題なく再生できる。もうね、今までのもっさりとろとろ時々クラッシュ、は一体何だったのかと。

■互換性について■

RDで焼いたBlurayディスクは今のところREもRも問題なく再生できてます。LAN経由でRDを見ると番組名一覧は表示できるのですが、選択すると数秒後に「再生できません」とアラートが出ます。

2013年8月25日日曜日

Force.com mobile SDK 2.0 for iOS

【重要なお知らせ】新プロジェクトはコマンドライン

前のバージョンまでは、XcodeのFile->New->New Project...でHyblid Force.com App / Native Force.com REST Appを選べばすぐにプロジェクトができていましたが、2.0ではコマンドラインから作る必要があります。簡単ですけども。

【重要なお知らせ】ARCに対応しました!

Xcode 5はARC対応のプロジェクトしか作れないですし。それにARC、慣れてしまえばそんなに悪いヤツじゃないですよw

■大まかな手順■

  • gitからcloneしてbuild
  • テンプレートをbuild
  • では、新しいプロジェクトを

参考ページは以下。
https://github.com/forcedotcom/SalesforceMobileSDK-iOS

以前より増えた分(テンプレートのbuild)
https://github.com/forcedotcom/SalesforceMobileSDK-iOS/blob/master/build.md

■gitからcloneしてbuild■

まず、適当なディレクトリに移動して、ターミナルから以下のコマンドを実行します。

git clone https://github.com/forcedotcom/SalesforceMobileSDK-iOS.git
回線速度にもよりますが、数十秒程度。終了してプロンプトが出たら、次はgitが作ったプロジェクトの中に異動してビルドします。

    cd SalesforceMobileSDK-iOS/
    ./install.sh

これは回線やマシンの速度の応じてやっぱり数十秒から数分。SSDだと妙に速くて感動です。以前はこれでXcode用のテンプレートが自動的にインストールされていたのですが。

■テンプレートをbuild■

それではテンプレートをbuildします。

    cd build/
    ant 

今度はそれほど時間かかりません。

■では、新しいプロジェクトを■

    cd artifacts/
    cd NativeAppTemplate/ または cd HyblidAppTemplate/
    ./createApp.sh

これでテンプレート生成シェルから使い方が出てきます。

Usage:
createApp.sh
   -t <Application Type> (native, hybrid_remote, hybrid_local)
   -n <Application Name>
   -c <Company Identifier> (com.myCompany.myApp)
   -g <Organization Name> (your company's/organization's name
   [-o <Output directory> (defaults to this script's directory)]
   [-a <Salesforce App Identifier>] (the Consumer Key for your app)]
   [-u <Salesforce App Callback URL] (the Callback URL for your app)]
   [-s <App Start Page> (defaults to index.html for hybrid_local, and /apex/VFStartPage for hybrid_remote)]

では、素直にプロジェクトを作ってみます。com.company / companyはあなたご自身のものを使ってください。蛇足ですが。

    ./createApp.sh -t native -n TestForceMobile20 -c com.company -g company

ほんの一瞬で以下のメッセージが出来て終了します。

Creating app in SalesforceMobileSDK-iOS/build/artifacts/NativeAppTemplate/TestForceMobile20
Successfully created native app 'TestForceMobile20'.

あとは適当なディレクトリにコピーして、.xcodeprojをダブルクリックすればXcodeが起動します。

■プロジェクトについて■

コマンドラインで生成した時に指定しなかった場合にはAppDelegate.mのRemoteAccessConsumerKeyとOAuthRedirectURIを設定します。

生成されたコードはばっちりARC対応です。__weakが邪魔っすね。まだ慣れません。AppDelegate.mは1.xよりはかなり洗練されていて、いじりやすくなりました。例によってStoryboardには対応していませんが、setupRootViewControllerでnibではなくStoryboardから生成されるようにすれば問題ないはずです。

取り急ぎ、以上。

2013年8月11日日曜日

ワイシャツの洗濯

■クリーニング屋に不満がある■

うちの近所のクリーニング店はどうもいかん。特にワイシャツ方面:
  • 安いところは襟汚れが落ちない
  • 高いところはまぁ良い仕上がりだったけど潰れた。プリペイ残あるのに
  • どこも高温乾燥しているせいか縮む
  • 糊付けないでと言ってもパリパリ
こんな感じ。なので自分で洗ってるんですが、アイロンは苦手だし面倒なので掛けたくない。とりあえず試行錯誤の結果、ある程度うまくいくようになったので、まとめてみました。

基本的にはきちんとたたんでからネットに入れて洗う。マルイのワイシャツ専用洗濯ネットってどうなんでしょね。良いとは思うものの、ネット1枚でワイシャツ1枚しか洗えないのがイヤなので買ってません。高いし(1枚1000円)。

■前準備■

使い古した歯ブラシでワイシャツの襟と袖口に液体洗剤を塗りつける。

適当な作業場所で襟のボタンをとめてからシャツを広げます。うちは風呂フタの上でやってます。LED電球なので写真の色温度がヒドいことになってますがご容赦ください。

脇の下の縫い目を左右に引っ張るようにするといい感じにシワが取れます。これを3-4枚、写真のように重ねて行きます。重ねる時には、ノーアイロン性能の高いものが上に来るようにすると良いです。今回は綿100%のダンガリー☓2、エディー・バウアーの綿100%シワ無し加工シャツ、ビズポロ…の順番。ビズポロは普通に洗濯脱水しても皺にならないのですが、スペーサーとして入れてます。

次に、片袖ずつ内側に折っていきます。なんか「デュワ〜」って感じ。


最後に、ネットに入る大きさに折ります。私はデブなので幅65cmないと入らないですが、普通の人なら60x45cmのネットに入ると思います。

あとは、普通に洗濯しますが、脱水はしません。洗濯>すすぎ>すすぎ…が終わったところで止めておきます。水槽からネットごとワイシャツを取り出して、濡れたままハンガーに干します。

ノーアイロンはもちろんアイロン簡単シャツならこれで十分パリっと仕上がりますし、綿100%のダンガリーも程よいざっくり加減になります。

2013年8月3日土曜日

NAS + スカパー録画の危機

要約:NASを再起動したらスカパーが見えなくなった

■発端■

バッファローのLX-WV4.0TL/R1というNASにスカパー!の放送を録画してます。2-3年、何の文句もなく動いていたのですが、久しぶりにファイルサーバとして使っているパーティションがアクセスできません。

ホコリも溜まっているので、一度電源を落として清掃しました。で、再起動したら無事ファイルは見えるようになったものの…スカパー!チューナーから、NASが見えなくなってしまいました。ホームサーバ一覧画面には虚しく「オフライン」と表示されるだけ。

■原因■

いくらなんでも再起動が原因ではないと思いますが、スカパー!のサーバとしての認証が消えてしまったようです。

スカパーチューナー側でサーバリストを表示すると、同じ名前のNASが2台並んでいて、1台は接続済、もう一台は未接続。接続済サーバを削除して未接続を再登録すれば解決します。とても簡単な話なのですが、一つ問題があります。

スカパーチューナーは、録画先サーバを変更すると録画が全消去されるという面白い仕様になっています。こればっかりはどうしようもないので…予約リスト画面をiPhoneで撮影してから接続(および予約リスト)を全消去し、改めて予約しなおしました。

いやはや…。

■試行錯誤したリスト■

  • NASをWebコントロール画面から再起動
  • NASのDTCP/IPサービスを再起動
  • NASの電源を落として、数分後再起動
  • NASのアップデータを確認(最新版だった)
  • NASのマニュアルを確認(まったく参考にならん)
  • 「スカパー HDD オフライン」でググった→同様の症状がヒット

2013年7月15日月曜日

あまりにも暑いので、やってみた。




なお、QRコードはcmanで生成しました。文字列はwikipediaから。

QRコード落語、とか思いついたけど、「子別れ」ってタイトルの後に何百個並べればいいのかわからないのでやめた。

2013年7月10日水曜日

サンダーバードの未来との差分

香港にて。

朝、サンダーバードの展示会についてのニュースをやっていた。

その中で、あれもこれも実用化されたけど、当時は携帯電話もインターネットも想定外だったねぇ、みたいな話が出ていた。

確かに、電子機器の発達は異常なほどだけど、交通機関は意外と発展してない気がする。チューブ式の高速鉄道や、車輪のない空飛ぶ自動車はどうなったんだw もちろん、サンダーバードの頃に比べれば、空の旅は特別なものではなくなっているし発展途上国でも自動車が多すぎて公害問題を引き起こしてたりと、量的な問題は大きく変わっているんだけども。

でも、ニュースを見ながら思った。電話など通信技術の発達が移動手段の進歩を止めちゃったんじゃないかなー。話をするなら顔を合わせなくちゃいけないし、必死こいて高速移動な未来を目指していたんだけど…どこにいても海外とも無料で顔を見ながら話ができる世の中になっちゃったら、余計な放射線浴びながら高々度超高速ジェット機乗ろうって気分も無くなっちゃう…ような気がする。

ついでにいえば、公共交通機関がきちんと定時運行されることは高速化と同じぐらいの意味があるよね。時間の読めない乗り物だとそれなりにマージンを見ないといけないけど、マージンの30分も電車に乗ってる30分も同じだから、みんながそのマージンを織り込まなくて済むということは、電車が30分速くなったのと同じこと。

なのでJRさん、はやくホームドアを全駅に設置してくださいね。

iPhoneのキーボードが出た時にUITextFieldの高さを変える方法(iOS 6)

よく、ブログなどに書かれているのは、「出てきたキーボードの高さ分をUITextFieldのheightから引いてやる」って感じだけど…それだと英語キーボードから日本語キーボードに切り替え、さらに予測変換窓が出た時に過剰に小さくなってしまったりします。

ということで、それを回避できるコードを張っておきます。UITextFieldが画面一杯に貼ってあるのが前提です。

■準備:Notificationの設定■

UIKeyboardWillShowNotificationでkeyboardWillShowを呼び出すよう設定します。


- (void)viewDidLoad
{
    [super viewDidLoad];

    NSNotificationCenter *center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self
               selector:@selector(keyboardWillShow:)
                   name:UIKeyboardWillShowNotification
                 object:nil];
}


■本体■

UIKeyboardWillChangeFrameっていうnotificationが別にあるんですが、willShowだけでキーボードが現れる時、英語と日本語切り替えた時、日本語キーボードで予測変換窓が現れた時に呼び出されてしまいます。良いのかそれで?

ミソは、convertRectです。keyboardFrameEndから返ってくるのはwindowの座標系で、ツールバーの高さやOrientationなどが配慮されていません。ので、現在のviewの座標系に変換します。これで「キーボードのorigin.y座標=使える画面の下限座標」となりますから、これでtext fieldの高さを設定し直します。

同じViewの上でキーボードを出したり消したりする必要のある時にはUIKeyboardWillHideNotificationを使います。ノリは同じで、viewのframeを取得してその下限を元にtext fieldなどの高さを調整するだけです。



- (void)keyboardWillShow:(NSNotification*)notification
{
    CGRect keyboardFrameEnd = [[notification.userInfo 
           objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd
                                     fromView:self.view.window];
    
    originFrame = self.textInput.frame;    
    float newHeight = keyboardFrameEnd.origin.y - originFrame.origin.y;

    [self.textInput setFrame:CGRectMake(originFrame.origin.x, 
                                        originFrame.origin.y, 
                                        originFrame.size.width, 
                                        newHeight)];
}


■後始末■

最後にnotificationを消し忘れないように。私はよく忘れます。ではでは。


- (void)dealloc {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self 
                      name:UIKeyboardWillShowNotification 
                    object:nil];

    [textInput release];
    [super dealloc];
}

2013年7月9日火曜日

古いアプリをiOS6対応にしたらupside downにならない件を解決

■症状■

古いプロジェクトをiOS 6対応にしたら、portrait upside downにならない。NotificationでOrientationが変わったことは検出できるけど、Viewが回転しないので横向きになったまま。

iOS6のデフォルトではiPhoneではupside down以外が許可、iPadでは全方向が許可されていますので、それがそのまま効いていて変更できていない、ということになります。


■一般的な対策■

iOS 6ではshouldAutorotateToInterfaceOrientationが廃止になったので、対象ViewControllerでsupportedInterfaceOrientationsとshouldAutorotateを使う。


- (BOOL)shouldAutorotate {
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}

iOS 6対応として最初から作ったプロジェクトはこれで動きます。しかし、viewやらcontrollerをいろいろいじっている古くからのプロジェクトはこれではダメでした。

個人的困ったのはSalesforce対応アプリです。Salesforce mobile SDK for iOSの最新版ver 1.5でも未だにStoryboardやARCに対応していないので、中身は結構古いまま。テンプレートからiOS 6 targetにしても、upside downは働いてくれません。

その一方で…iOS 5をターゲットにすると、Web ViewのログインでID入力欄をタップしただけでSIGABRTするのは私だけでしょうか?


■原因■

root view controllerが別にあるためview controller上で上記shouldAutorotate / suportedInterfaceOrientationsをいじったとしても、標準のUINavigationController等で上書きされてしまう。


■解決策■

私のプロジェクトでは、Storyboard上のUINavigationControllerからメインviewを呼び出していましたので、UINavigationControllerのサブクラスNavigationControllerを作り、上記shouldAutorotate / supportedInterfaceOrientationsを貼ります。その上で、Storyboard上のnavigation controllerのcustom classをNavigationControllerに変更します。これでうまく行きました。いやはや。



■Salesforce mobile SDK for iOSの場合■

上記Salesforce mobile SDKの場合、テンプレートから作成されるプロジェクトはAppDelegate.hのnewRootViewControllerでViewとUINavigationControllerを生成しています。

上記と同じ手順でUINavigationControllerのサブクラスNavigationControllerを作り、AppDelegate.mにインクルードしてから、newRootViewControllerを書き換えます(書き換え箇所は赤字)。


- (UIViewController*)newRootViewController {
    RootViewController *rootVC = [[RootViewController alloc] initWithNibName:nil bundle:nil];
    NavigationController *navVC = [[NavigationController alloc] initWithRootViewController:rootVC];
    [rootVC release];
    
    return navVC;
}



2013年7月8日月曜日

Salesforce mobile SDK for iOS 1.5

五十肩とか自転車レースとか転倒等によりすっかりご無沙汰してしまいました。今日はSalesforce mobile SDK for iOS 1.5のお話。

■感想:速い…?■

1.3当時は更新がうまいこと反映されないなどの問題があったので切っていたのですが、今回試してみたら起動がヤケに速い。記事によるとデフォルトでonになったとのこと。せっかくなので試してみたら、以前の問題は解消していて、これならキャッシュとして十分機能していると判断し、今回からONにしました。

Authentificateも早くなったような気がします。私のアホなアプリ「FourChatter」はあまり処理らしい処理をしていないのに(いないからこそ?)、2回目以降の起動時間が1/4ぐらいになりました。

■問題点■

ご存知の通り、iOS 6でOrientationの仕様が大きく変わっています。そのせいかどうか、SDKから作ったテンプレートアプリのinfo.plist上でPortrait Upside Downに設定しても、upside downになってくれません。他の方向へは問題なくrotateしますし、UIDeviceOrientationDidChangeNotificationでnotificationを受けるとちゃんと「upside downになるで」と通知が来ますが、表示は横を向いたままです。

追記:解決しました→古いアプリをiOS6対応にしたらupside downにならない件を解決

SFNativeRestAppDelegate.mを見ると「iOS 6になったらこの書き方はダメ」って書いてある書き方が多々あるけど、うーむ…。

かといってiOS 5でビルドするとSalesforceへのログイン画面に触っただけで落ちるし…困ったもんだ。

■そこに別の問題が■

アップデートしたアプリをAppleへ送付しようとしたのですが、失敗しました。Validate...は成功しますが、Distributeでエラーが…。


ググるとJVM関連のトラブルのようで、JDK 6が立ち上がるようにすれば良いという解決策などが出ているのですが、ダメでした…。この件、アップルに問い合わせメールを送って対応待ちです。いやはや。

2013年6月1日土曜日

Fitbit oneのリストバンドが朝になると外れている件、を解決

NHK技術研究所公開中ですね。玄関脇のIEEE記念看板
睡眠を記録するためにFitbit oneを入れて手首に装着するリストバンド、ふと夜中に目を覚ますと必ず外れている。

ベルクロがちょっとはみ出した部分が服や布団に張り付いて、バリバリ→やめてー、になっている模様。

はみ出ないようにきちっと巻いたり、ベルクロの位置を調整したり工夫してみたけど、ダメ。そのうち、めんどくさくなってしなくなっていまして。

が、昨夜久しぶりに装着してみたのですが、ふと思いついて自由に回転するようにユルユルに巻いてみた。

解決しました。
実際は2:30起床で止め忘れた。それ以前に睡眠の質が悪すぎるorz

結論:Fitbit oneのリストバンドはゆるく装着しましょう。

2013年5月26日日曜日

SalesforceからiPhoneへPush notificationを送る

とんかつ専門店のご飯が妙に美味いのはなぜ?
上の写真はまったく関係ありません。

週末、何かネタを探していたら、www.parse.comというサービスに関する記事が見つかりました。最近ではPush Notification代行サーバもいろいろあるのですが、これは余計な機能がないぶんシンプルで安い。

そんなわけで、chatter上に「$go message」と書くと、triggerが作動してForce.comのhttp calloutからparse.comのAPIを叩き手元のiPhoneにメッセージが届く、というのを作ってみました。

■まず、Push Notificationを試す■

試すと言いますか、こちらの記事を参考に、いや、そのまま実行してみます。

Remote Push Notification ASPサービスを試す

Parse、一発で動いてPushされました。いやー、いままでこんなに簡単にPushできたのって初めてかもしれない。10分か15分くらいで動いてしまいました。



■FeedItemのTriggerからParseのREST APIを呼ぶ■

さて、ここからはForce.com上での作業。TriggerからREST APIを呼ぶことはできないので、Triggerからバッチを起動、バッチからcall outする作戦。

忘れないうちに、まずはお約束のリモートサイトの設定。
  管理者設定>セキュリティのコントロール>リモートサイトの設定
これでhttps://api.parse.com/を登録しておきます。

■Batchを作る■

Developer ConsoleからClass CallParseを定義、IterableなBatchApexでParse.comのAPIを叩きます(コードは末尾)。

■Triggerを作る■

例によってDeveloper ConsoleからFeedItem上のキーワード $go を検出してBatch Apexを呼び出すコードを書きます(コードは末尾)。

■動かない■

一応、Salesforce上で
  管理者設定>監視>デバッグログ
を設定してから、Chatter上にメッセージを書いてみる。

…動かない。デバッグログを見ると、

05:50:02.039 (39869000)|CALLOUT_REQUEST|[29]|System.HttpRequest[Endpoint=https://api.parse.com/1/push, Method=POST]
05:50:02.175 (175302000)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:661
05:50:02.175 (175393000)|CALLOUT_RESPONSE|[29]|System.HttpResponse[Status=Bad Request, StatusCode=400]
05:50:02.175 (175420000)|HEAP_ALLOCATE|[29]|Bytes:130
05:50:02.175 (175438000)|SYSTEM_METHOD_EXIT|[29]|System.Http.send(ANY)

と出ていました。ちくしょう、どうせ一発で動くと思ってねぇですよ←涙目 なので、Apex側にコードを入れて
    System.debug(res.getBody());
    System.debug(res.getStatus());
    System.debug(res.getStatusCode());
検証してみたところ
    16:43:08.153 (153194000)|USER_DEBUG|[36]|DEBUG|{"code":107,"error":"This endpoint only supports Content-Type: application/json requests, not : application/json."}
という結果が。

ソースを改めて見なおしたら、

    req.setHeader('Content-Type : ', 'application/json');

なんてバカなコーディングを発見。直したら無事動作。




以下コードを張っておきます。iPhone側はparse.comのサンプルを貼っただけです。

■Trigger■


trigger KickFromChatter on FeedItem (before insert) {

    FeedItem[] feeds = Trigger.new;
    List<FeedItem> arrayFeedItem = new List<FeedItem>();
    
    for (FeedItem feed : feeds) {
        String strBody = feed.Body;
        if (strBody != null && strBody.contains('$go')) {
            arrayFeedItem.add(feed);
        }
    }
    
    if (arrayFeedItem.size() > 0) {
        CallParse batch = new CallParse();
        batch.arrayFeedItem = arrayFeedItem;
        
        Database.executeBatch(batch);
    }
}


■Batch Apex■


global class CallParse implements Database.batchable<FeedItem>, Database.AllowsCallouts {
    
    global List<FeedItem> ArrayFeedItem;
    
    global Iterable<FeedItem> start(Database.BatchableContext info) {
        Iterable<FeedItem> i = arrayFeedItem;
        
        return i;
    }
    
    global void execute(Database.BatchableContext info, List<FeedItem> feeds) {
        for (FeedItem feed : feeds) {
            String str = feed.Body;
            String strPayload = '{"channels" : [""], "data" : {"alert" : "' + str + '"}}';
            Blob blobPayload = Blob.valueOf(strPayload);
            
            // Instantiate a new http object
            Http h = new Http();
            
            HttpRequest req = new HttpRequest();
            req.setEndpoint('https://api.parse.com/1/push');
            req.setMethod('POST');
            
            req.setHeader('X-Parse-Application-Id', 
                                   'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
            req.setHeader('X-Parse-REST-API-Key',
                                   'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
            req.setHeader('Content-Type',
                                   'application/json');
            
            req.setBody(strPayload);
            
            // Send the request, and return a response
            HttpResponse res = h.send(req);
            
            System.debug(res.getBody());
            System.debug(res.getStatus());
            System.debug(res.getStatusCode());
        }
    }
    
    global void finish(Database.BatchableContext info) {
    }
    
}


2013年5月8日水曜日

Force.com:添付ファイルの全文検索

ChatterにMS-Officeのファイルをアップロードし、ブラウザでログインして「検索」から探すとファイルの中身をちゃんと探してくれるのですが、APIからFINDを使ってもヒットせず苦労していましたが、解決策が見つかったのでメモ。

■ContentVersionにあった■

Chatterにアップロードしたファイルを全文検索で探し出したい。

とりあえず、ファイルがどこに格納されているかをコンソールでSOQLで探したところ、ContentVersionにそれらしいレコードが見つかりました。その結果を踏まえてEnterprise/WSCで以下のSOSLを実行したところ、うまく検索してくれました。

  FIND {語句} RETURNING ContentVersion (Id, FirstPublishLocationId, IsLatest, PathOnClient)

ここで、IsLatest = trueが最新版を示すフラグ、PathOnClientはファイル名です。FirstPublishLocationIdには関連するオブジェクトのIDが入っているので、たとえばChatterからアップロードした場合には、
  SELECT Id, Body FROM FeedItem WHERE Id = cvFirstPublishLocationId
てなことを書けば探し出せます。

■試行錯誤■

何もパラメータなしに
  FIND {語句} 
とすると一応全ファイルが対象になるはず。そうでなくとも
  FIND {語句} RETURNING FeedItem (Id, Body, CreatedDate)
などと書けばヒットする、はず。

でも、どっちもダメでした。

それとPDFもダメでした。日本語のPDFはほぼ全滅、英語版PDFもヒットしないことの方が多いという状況です。いろいろな文書ファイルで試してますが、Office 2007ファイルについては今のところ漏れなくヒットします。

マカーの私としては、PDFが対象外ってのはちょっと困るのですが…。

以上、ご参考になれば幸いです。

2013年5月6日月曜日

「倉橋浩一」

でググると、今のところは私の記事がトップに来るけど、日本大学に同姓同名の人がいるな。Facebookでは同じ読みの人が沢山出てくる。

そろそろ何かアウトプットしないといけないなぁ…。

■太っていてびっくり■
OOエンジニアの輪! ~ 第 23 回 倉橋 浩一 さんの巻 ~

■バカというのは治らない病気■
倉橋浩一@WebObjectsは、YS-11を心より愛す|【Tech総研】

■懐かしい…。■
じつはWebObjectsで飯食ってます(連載一覧)

■私の前が林信行さんで次が竹林賢さんってスゲエ■
林檎いとしや Vol.66 倉橋浩一 人生への投資効果

2013年4月18日木曜日

apex:actionSupportについて

小さなネタですが。昨日ひさしぶりにVisualforceを使いました。

apex:pageBlockTableの中でapex:actionSupportを使ったのですが、event="OnRowClick"に反応してくれません。apex:commandLinkに置き換えてactionで呼び出されるメソッドに問題がないことは確認済ですが、それ以前の問題として全然イベントに反応している様子が見られない。

よくよくReferenceを見なおしたらOnRowClickではなくonRowClickとある。

…動きました。えー、case sensitiveなのかよー …と八つ当たりした午後3時でした。

関係ないけど、Apexの大文字と小文字の扱いにところどころ違和感を覚える。まぁ昔と違って、

    public String fullname {get; set;}

と書くとgetter/setterがうまく認識されない、というバグだか仕様は治ったみたいだけど。

2013年4月4日木曜日

Fitbit買ったとです

サラリーマンに転職して3年で10kg太りました…せっかく35kg落としたのに。

基本的に「短期的に成果が出ないとイヤ」な性格ですが、50歳すぎるとちょっとやそっとじゃ痩せない。なので、「短期的な成果」はともかく「日々の運動記録の蓄積」を記録してそれをモチベーションにつなげようと思います。

というわけで、Fitbit oneを買いました。

でもさすがにローラー練習(エアロバイク)には対応していないはず。一応Polarを持っているので、
  • Fitbitでの日々の活動量
  • Withingsで体重と血圧自動記録
  • Polarからの心拍と自転車方面データ
このあたりを統合して表示できるサービスがないもんかと物色してみました。友達のssuwaさんからFitbit対応アプリのリストを教えてもらったので、一つ一つ検索してみたのですが…結局、希望する条件をすべてクリアしているのはMicrosoft HealthVaultでした。そんな予感はしたんですけどね…。

Microsoftは開発ツールだけは妙によくできているのに、アプリもサービスも何か違うんだよなぁ…。

で、結局、FitbitにPolarの結果(消費カロリーと運動時間)を手入力して現在に至る。友達2人しか登録してないんですが…それでも順位が下がると燃えますね。

歩いててもつい遠回りしたりひと駅手前で降りてしまうw

田町〜五反田、直線距離は近いけど、崖のような坂がある

まぁ週7万歩以上☓2週間たってもまだ痩せないわけですが。

2013年4月3日水曜日

JavaFXはじめました

最近JavaFXで遊んでます。

昔の惨状を覚えているので近づいていなかったのですが、FlashはECMAScriptなActionScriptがやっぱり好きになれないので他に何か良いクロスプラットフォームのRich Client開発環境ないか探していたらJavaFX 2.0の記事がヒットしまして。

調べてみたら、GUI builderもそれなりに使えるし、見た目も素のFlex程度には作れるし、まだ試してないけどChartもわりと種類がある。

コードがAndroidっぽくなってしまうところがアレですが。とりあえずNetBeansを使って試しています。ちょっとバギーだけど…Eclipseより見た目が良いし、コード補完も神レベルのXcodeには及ばないもののEclipseよりは絞りこまれているので楽。Threadなんかも慣れ親しんだJavaなので新しく覚えなおさなくて良い。

最大の問題は、調べ物をしようとすると外貨取引がサイドバーに並ぶことぐらいかな。

FX違いだってばよ。

2013年2月23日土曜日

Flashの画像を差し替える

ソースリストなど一切失われてしまって手元にあるのはSWFだけ。その上のビットマップを張り替える必要が生じて、いろいろ試行錯誤しました。

  • swfmill
  • swfmill on Moutain Lion
  • 画像差し替えのために
  • どうやってPNGをエンコードするか
  • Mountain Lion上で動かして見る



■ swfmill ■

というオープンソースのツールがあります。これはSWF<->XMLを相互に変換できるという素晴らしいものです。コマンドラインツールでソースとMac版 /  Windows版が配布されています。Windows版はdylibも含んでいて、インストーラを動かせばすぐ使えるようになるはずです(私の環境ではdylibをインストールする時にアンチウィルスさんに怒られる)。

Macでは以下のような作業が必要になります。
  1. サイトからMac版バイナリをダウンロード&解凍
  2. ターミナル上で必要なdylibを用意して、swfmillを実行
…と書くと簡単なんですが。

■ swfmill on Mountain Lion■

配布されているのはコマンドラインツールのバイナリです。前述の通りlibfreetype6などのdylibが/usr/local/libにインストールされている必要があります。いろいろ試行錯誤しましたが、結果としてこんな感じ
  1. http://xquartz.macosforge.org/landing/からXQuartzをダウンロード&インストール
  2. MacPortsをインストール
  3. ターミナル上で準備
    1. MacPortsを最新版にアップグレード
      • sudo port self update
    2. 必要なdylibを読み込むためにportを実行
      • sudo port libfreetype +universal
      • sudo port libpng +universal
    3. portが読み込みディレクトリとswfmillが想定するディレクトリが異なるのでシンボリックリンクを置く
      • sudo ln -s /usr/X11/lib/libfreetype.6.dylib /usr/local/lib/
      • sudo ln -s /usr/X11/lib/libpng12.0.dylib /usr/local/lib/
    4. swfmill起動してdylibが足りないというエラーが出たら、2のようにdylib名称の尻尾を取ってportし、3のようにしてシンボリックリンクを置く
これで使えるようになるはずです。

swfmillの使い方は、またターミナル上での作業ですが、
  • SWFからXMLへの変換
    • swfmill swf2xml source.swf result.xml
  • XMLからSWFへの変換
    • swfmill xml2swf source.xml result.swf
と実行するだけです。他にも沢山機能があるようですが、今回使ったのはこれだけ。

蛇足ですがswfmillをダウンロード&解凍したディレクトリの中で作業するのであれば、
  • ./swfmill
とする必要があります。

変換処理を確認するためにSWFからXMLに変換したものをそのままSWFに変換しなおして動作確認してみてください。私の場合は多少SWFのサイズが変わったものの、動作が変わらなかったので一応OKとしました。

■画像差し替えのために■

XMLをエディタなどで開きます。PNG/GIF画像はDefineBitsLossless2のタグ内にbase64でエンコードされています。私の場合、対象画像のwidthから一発で目的の画像を探し当てることが出来たのですが、そういうヒントがない場合はバイナリのサイズなどから虱潰しに試してみるしかないかもしれないですね…みなさんのご健闘を祈ります。さて、あとは、差し替える画像をテキストに変換するだけです。

■どうやってPNG画像をエンコードするか■

ここは悩みました…。JPEGは比較的カンタンで、単純にBase64に変換してその文字列を<data></data>タグで挟めば良いらしいです。でも、GIF / PNGに関しては、技術的な解説記事が出ているし、Rubyでのライブラリもあるんですが…「ドラッグ&ドロップとは言わないが、せめてコマンドライン一発で変換するツール」みたいなのは見当たりません。

SFWMILLの解説ページにXMLに新しい画像ファイルを取り込む方法は書いてあるのですが、いろいろタグの書き方を変えてみたもののうまくいきません。楽をするためならどんな苦労をすることも厭わないので、帰りの電車で眉間に縦ジワ寄せつつ考えていたら…ひらめいた。画像を入れたSWFを作って、それをswfmillで変換すれば良いということにw

  1. 元の画像をswfmillでXMLに変換しておく
    • swfmill swf2xml Source.swf Source.xml
  2. FlashBuilderを起動、新しいFlashプロジェクトを作る
  3. 画像ファイルをプロジェクトに追加
  4. デザインビューでImageを貼り、さっきの画像ファイルをEmbed
  5. あとは何もいじらないで、リリースビルド
  6. bin-releaseの中の <project-name>.swfをXMLに変換
    • swfmill swf2xml image.swf image.xml
  7. 変換したXML(image.xml)をエディタで開き<data>タグを探す。
  8. 新しい<data><data>....</data></data>を古いものの上にコピペ
  9. swfmillでXMLをSWFに変換
    • swfmill xml2swf Source.xml Result.swf

■Mountain Lion上で動かして見る■

そういえばコントロールパネルからapacheを起動できなくなったんですね。これまたターミナルから、

で起動させて、ブラウザ上で
    http://localhost/
とタイプし「It works!」が表示されれば起動は成功。あとはさっき出来上がったswfを
    sudo cp test.swf /Library/WebServer/Documents/
などとコピーして、ブラウザでURLを入力します。
    http://localhost/test.swf
これで思った通りに画像が差し替わっていれば出来上がりです。

それにしても…オレもいろんなことやるなぁ…。

2013年2月21日木曜日

iOS 6にすると起動しない

サマリー

  • 現象:起動しない
  • 調査
  • 解決策1:エミュレータを削除
  • 解決策2:デバッガを変更

夏休みの宿題は8月下旬に始める子供だった私は、例によってリリース直前になって「そういえばiOS 6で試してなかった」ということに気づいて慌てるのだった(完)。



■現象:起動しない■

Xcode 4.5上でiOS 5.1用に作っていたアプリをiOS 6で起動しようとしたら、エミュレータが起動した直後、スプラッシュも出ないうちにアプリが即死します。デバッグコンソールすら出てくれません。

■調査■

XcodeのView->Debug Area->Activate Consoleでログを見るとそこには
    error: failed to attach to process ID 0
というエラーメッセージが出ていました。これをそのままGoogle先生で検索すると、日本語のページと、困った時のStackoverflowさんが2件。経験上、Stackoverflowさんを先に見ます。

■解決策1:エミュレータを削除■

まずその1(「error: failed to attach to process ID 0」)。結果的にはこれが大正解でした。

iOSシミュレータを終了してから、
  • ホーム下のLibrary/Application Support/iPhone Simurator/<対象バージョン>/Applications内の中にあるファイルを全て削除
これだけです。

なお、それ以前にiOSシミュレータの「コンテンツと設定をリセット...」も試したのですが、は効きませんでした。というかリセットしたり言語設定を切り替えるとそのままハングすることがあったのですが、削除してからは問題なしです。

■解決策2:デバッガを変更■

次がこれ(「Failed to Attach to Process ID Xcode」)。デバッガをLLDBからGDBに変更します。
  • XcodeのProduct->Edit Scheme
  • スキーマ編集画面で左側のバーから「Run YourApp.app Debug」を選択し、右側の「Info」タブを選ぶ
  • DebuggerをGDBに切り替える
  • Product->Clean

これでもまだアプリが立ち上がらない場合には、さっきのDebugger選択画面でLaunchを「Wait for YourApp.app to launch」に切り替えて、エミュレータ上でアプリのアイコンをタップしてみてください。それでもダメならDebugger=None。

ただ、一度起動するとLLDBに戻してもうまくいくという報告もあるので(「[Objective-C]iOSシミュレータでのデバッグができなくなった」)、iOSシミュレータでバージョンを切り替えたりした時にエミュレータの設定がおかしくなったのが原因ではないかなと思っています。

これでもうまく行かない場合は、対象アーキテクチャからarm7sを外す(「iPhone 5からはarmv7sアーキテクチャ」)というのもあるようです。特に外部のライブラリがarm7s非対応の場合はこれしかありません。私は先にこっちを試したのですが、変化ありませんでした。

2013年2月14日木曜日

AndroidのソフトキーボードでEditViewが隠れる場合

AndroidでEditTextがソフトキーボードに隠れてしまう件。

シングルラインであれば、AndroidManifest.xmlの該当するactivityの中に
    
    android:windowSoftInputMode="adjustPan"

を追加すれば解決することが多いですが、マルチラインの場合には
   
    android:windowSoftInputMode="stateVisible | adjustPan"

と書かないと最初の1行目しか見えません。よくある「ScrollViewを使う」等のwork aroundも試したのですが、結局、これが一番よく効きました。上記はScrollViewなしでも効いてます。

ただ機種依存やその他の設定による影響もいろいろあるようなので(この記事が参考になりました→「Android キーボードの出現と同時に画面がスクロールしないときに」)、あくまでも私のところでは…という話です。

ってかデフォルトをadjustPanにしといてくれよ>Google

DDMS post-create initでフリーズ

何か今日は沢山のBad Know-howが収集できて嬉しいorz

ADT/Eclipseを普通に終了してからMac(Lion)を再起動、再度ADTを起動したらstatusに

  DDMS post-create init

という表示が出てそのままフリーズしてしまった。Eclipseを強制終了して再起動してもダメ。

ググったら、StackOverflowでwork aroundが見つかりました。ありがたやありがたや。Eclipseがしっかり終了しているのを確認し(生き残っている場合には強制終了)、ターミナルでWorkspaceへ移動し、以下のコマンドを実行します。

  rm .metadata/.lock

これでEclipseは起動しました。いやはや。

2013年2月10日日曜日

userIdの取得

■userIdを知る■

自分がポストしたデータは表示しない、という処理を実装する場合にuserIdを知る必要があります。

古いForce.com SDKでは

AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *userId = [[[app coordinator] credentials] userId];

と書くこともできましたが、今はAppDelegateからSFOAuthCredentialsのインスタンスを見ることができません。最新のSDKで同じ事をする場合には

NSString *userId = [[[[SFRestAPI sharedInstance] coordinator] credentials] userId];

と書けばOKです。

■Xcodeバージョンアップ■

Xcode 4.4で作ったプロジェクトを4.5に持って行ったらエラーの山が出来てしまいました(今更ですが)。結局プロジェクトを修正するのは諦めて、最新のForce.com mobile SDKを入れて新しくプロジェクトを作りました。リソースなどをコピー、AppDelegateなどはソースをにらめっこして必要なところだけをコピーしましたが、ViewControllerについてはそのまま上書きです。その作業中に上記のエラーに遭遇した次第。

あとは「認証中」画面のxibからstoryboardの置き換えが残っています。以前のテンプレートではSFNativeRootViewController.mのsetupAuthorizingViewControllerを書き換えればStoryboardに置き換えるのも簡単だったんですが…app delegate上でオーバーライドするのも面倒くさい。

起動時に一度呼び出されるだけで他の実行には影響のない画面なので、ここはそのままxibで済ませましたm(_ _)m

2013年2月9日土曜日

マクドナルドのレジ



マクドナルドのレジは、コーヒーひとつ買うのに会話が多すぎてめんどくさい。最近は日本語があまり得意でない店員や不慣れな店員も多くて「えーと、ホットコーヒーのM、持ち帰り、砂糖ミルク袋要らない、Edy払い」なんてのが一発で通るのも珍しいぐらい。

「注文所要時間を短くするためにメニューを無くしました」なんて的はずれなことばっかりやっていないで、マクドナルドのレジがこんな具合になってたらもっと使いやすいんだがなぁ。

  1. 事前にスマフォ上でメニュー、支払い方法、袋はどうするか?などすべて入力
  2. レジにかざすとオーダ情報がすべて伝達、Edyで決済
  3. あとは品物を受け取るだけ

注文が5秒で終わるw

情報の伝達は既存レジだとEdyが付いているから、アプリからクラウド上に注文あげておいて、EdyのIDから引っ張ってくればおk

時間帯にもよるんだろうけど、繁華街にある店だと1/3-1/2ぐらいの客がスマフォ/ケータイのクーポンを見せているような感じかな? そのうちの半分がアプリで注文するようになれば、せっかちな客の満足度は上がるしレジの流れも早くなる。

さらに、「1週間分のランチ予約でクーポン!」などの販促もリンクしやすいし精度が高くなる。A店が空いているなとなったら「今から17時までA店限定でポテトが半額!」なんてpush notificationを流せば特定の店の稼働率をコントロールすることも可能になる。プリペイと組み合わせれば事前予約がキャンセルされにくくなる。

マクドナルドに限らず、苦手意識のあるヒトが少なからず存在するというスターバックスでも売上増につながると思うけどね。

2013年2月1日金曜日

Android:今日ハマったこと

■layout.xmlが壊れる■ 

Graphical Layoutでの作業。画面の最上部にRelativeLayoutを置きます。

その中に「左端にボタンA、真ん中にTextView、右端にボタンB」を配置しました。ボタンAは左寄せ、ボタンBは右寄せ、TextViewはAの右かつBの左を指定します。

ソースを保存してもエラーなし。


で、実行しようとするとエラー。TextViewのandroid:layout_toLeftOfで「そんなidない」って言われる。

原因は…xml上で、ボタンA,TextView,ボタンBの順番で並んでいるのにTextViewが後にあるボタンBを参照しているから…でした。


そりゃそうなんですけどね…Graphical Layout上で作業しているとエラーが出ない、でも一度エラーになるとXMLのソースを見ても手動でIDを付け替えてみても、どうにもなりません。現象だけ見ると、開発ツールがバグっててID管理テーブルが壊れているような感じなもので「+id/buttonA」と定数を対応付けているのはどこだろう、なんて無駄な探索をしてしまいました…。いやはや…。


■Activityが見つからないエラー■

またやってしまった>マニュフェストへの登録忘れ。

1日1回、忘れたころにやってしまう…。

今日はいくつもREST APIコールを書いたので、おかげさまでthreadとかrunnableとかhandler.postなんてのがソラで書けるようになりました。でもListViewのAdapterについては原理は何とか理解したものの、コードは覚えられない。

2013年1月31日木曜日

ACS Android SDKでハマる

ACSでググっても化学とか冠状動脈とかばっかりで、なかなか情報にたどり着けないのが困ったもんです。Appcelerator Cluod ServicesだとTitaniumばっかりだし(当たり前だけど)。ということで、ACS Android SDKでのハマりねた。

■Whereをどう書けば?■

Titanium mobileのSDKと違って、Android SDKはほとんど素のREST API SDKです。iOS SDKだと、あまり便利とはいえないけどCCWhereなんてクラスもあって、それなりにラップしてくれます。でもAndroidだと、こんな感じで書く必要があります。

    Map<String, Object> q = new HashMap<String, Object>();
    q.put("order", "-updated_at");
    q.put("where", "{\"email\":\"" + strEmail + "\"}");
    CCResponse r = sdk.sendRequest("users/query.json", 
                                   CCRequestMethod.GET, q);


…なんかperlでも書いている気分だ…書いたことないけど…。

まぁでも「ほぼREST」だと思ってしまえば、ある意味気は楽です。

ところで、Android 4以降はネットワークアクセスを別スレッドにしないと動いてくれません。

    public void acsThread() {
        listFriendsRequest = null;
            
        new Thread(new Runnable() {
            @Override
            public void run() {
                acsProcess();
            }
        }).start();
    }

そして終了後に画面切り替えたりするにはHandlerを使う必要があります。

以上をひとまとめにしてACSへのログインをActivity上に書くとこんな感じ。会社で使っているコードを貼るわけにはいかないので、記憶などを頼りにADT上で書いたもの。実働ソースではないので、もし不具合が出たらすみません。

    public void acsLogin(String inEmail, String inPassword) {
        final String email    = inEmail;
        final String password = inPassword;
        final Handler handler = new Handler();
        
        final JSONObject result = null;
        
        new Thread(new Runnable() {

            @Override
            public void run() {
                CCResponse r = null;
                
                Map<String, Object> q = new HashMap<String, Object>();
                q.put("login",    email);
                q.put("password", password);
        
                try {
                    r = sdk.sendRequest("users/login.json", 
                                        CCRequestMethod.POST, q);
                    CCMeta m = r.getMeta();
                    if ("ok".equals(m.getStatus()) 
                            && m.getCode() == 200 
                            && "loginUser".equals(m.getMethod())) 
                    {
                        result = r.getResponseData()
                                  .getJSONArray("users")
                                  .getJSONObject(0);
                    }
                }
                catch (CocoafishError e) {
                    e.getMessage();
                }
                catch (IOException e) {
                    e.getMessage();
                }
                catch (Exception ex) {}

                if (result != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            jsonUser = result;
                            Intent intent = new Intent(MainActivity.this, 
                                                       NextActivity.class);
                            startActivity(intent);
                        }
                    });
                }
            }
        }).start();
    }

何かもう1歩あるくたびにつまずいてググらないと進めない。

まぁiOSアプリ書いている時もそうですけどねorz