一ヶ月Titaniumを使って学んだこと。

  • 21
    いいね
  • 4
    コメント
この記事は最終更新日から1年以上が経過しています。

一ヶ月でやったこと。

とあるサーバーにアクセスして、日付を記録するアプリの制作。

普通のタイムカードアプリですね。
ただ、集団で管理できるように記録をアプリの中だけではなく、外部サーバーでも保存出来るようにしようと言うことで制作を行いました。

Alloyを用いたプロジェクトで、 Android,iPhone 等で使用できるアプリを作りました。

制作の際に用いたノウハウや、躓いたり、今でもよくわかんねぇって点をつらつらと週順で書き記していきます。多分やばい記述とか普通にしているんで、誰かご教授して頂けると幸いです。(Titaniumって結構情報がWebに落ちてなくて辛いんだよなぁ(´・ω・`)

1週間目

サーバーにアクセスしよう。

とにかくサーバーにアクセスせねば。ということで用いたソースがこれ。
送り先はPHPです。

exports.timecard = function(data1,data2) {
    var pram={"data1" :data1,
            "data2" :data2};
    var url = "/* お好きな接続したいPHP等のURL */";
    var client = Ti.Network.createHTTPClient({
        onload : function(e) {
        //接続後に実行される部分
        Ti.API.info(this.responseText);
        },
        onerror : function(e) {
        Ti.API.info("errorです。");
        //接続失敗時に実行される部分
        },
        //タイムアウトの指定。(3000ms)
        timeout : 3000
    });
    switch(Ti.Platform.osname){//使用してるスマホOSのチェック
        case 'iphone':
        client.open("POST", url);
        client.setRequestHeader('Content-type','charset=utf-8');
        client.send(pram);
            break;
        case 'android':
        client.open("GET", url);
        client.setRequestHeader('Content-type','charset=utf-8');
        client.send(pram);
            break;
    }};
}

受取先のPHPはこんな処理。


if(count($_POST)>1){
  $sendData = $_POST;
}else{
  $sendData = $_GET;
  if($sendData['data1']=="true"){
    $sendData['data1']=true;
  }else if($sendData['data1']=="false"){
    $sendData['data1']=false;
  }
}

$data1=$sendData['data1'];
$data2=$sendData['data2'];

//なんか適当な処理

echo "実行結果";//アプリではthis.responseTextに「実行結果」というデータが入る。

こんな感じです。
まぁ既におかしいところが何点もあると思います。サーバー接続においての躓きポイントは
1,配列データをPHPで送りたいのに、AndroidはPOST型で正しく送れない。
2,GETだからなのか、Androidだからなのか、boolean型で送ったはずのデータがString型になっているため、ちょっと処理を挟まないといけない。辛い。
3,Jsonでデータを送ることもできるが、やり方の問題なのかAndroidでしっかり受け取れず、echoで誤魔化している。

一応ローカルなサーバーでアプリを使う人も身内ってことで妥協した点では有りますが……Getはなぁ……。

追記1
JSONの送信受信方法について詳しいことを書いていてくださった方がいるので、是非そちらを御覧ください!コメントありがとうございました。

2週間目

ローカルにデータを保存しよう。

ローカル(スマホ)にデータを保持させてやりたい処理があったので、
やってみました。

// 起動時にファイルの有無をチェックし、なければ作る。
var path = Ti.Filesystem.applicationDataDirectory + 'cache/';
var directory = Ti.Filesystem.getFile(path);
exports.startApp = function (e){
    var file=Ti.Filesystem.getFile(path+'profile.txt');
    //ディレクトリが存在しない場合はディレクトリとファイルを作る。
    if(!directory.exists() || !file.exists()){
        directory.createDirectory();
        file.write('初期値');
            file = Ti.Filesystem.getFile(path+'profile2.txt');
        file.write('初期値2');
            file = Ti.Filesystem.getFile(path+'profile3.txt');
        file.write('初期値3');
    }
};

exports.checkFile = function (e){
    var file      = Ti.Filesystem.getFile(path+"profile.txt");
    var fileData = file.read().toString();
    return fileData;
};

この部分が実行されることでcacheというディレクトリを作って、その中に3つファイルを作って保存することが出来る。書き込みも似たような感じでできます。読み込みは2つ目の関数で出来ます。

3週間目

ローカル通知を出そう。

できるだけスマホのバッテリーを使うのを避けるためにローカル通知を出そうと思い、プログラムを組みました。iPhoneとAndroidで使える関数が違うため、別々なものを用意します。
まずはiPhoneです。


exports.iosStartWorkPush = function(popTime) {
            notification = Ti.App.iOS.scheduleLocalNotification({
                date: new Date(new Date().getTime()+popTime),//Pushを出す時間(1000ms=1sec)
                repeat: "daily",//リピートの設定。dailyは日毎
                alertBody: "出勤時間です",
                alertAction: "今日も頑張りましょう!",
                sound: "default",//iPhoneデフォルト音
            });
        }
    };

こんな感じで作るとiPhoneはアプリがバックグラウンドから消されても、popTimeで設定した数値後に通知センターに通知が行くようになります。

次にAndroidなのですが、僕の探し方が悪いのかアプリが消えると通知が出来ないやり方しか見つけられませんでした。良い方法をご存じの方はご教授お願いします・・・。

まず前準備として、tiapp.xmlのAndroid部分に下記のような記述をします。

tiapp.xml

   <android xmlns:android="http://schemas.android.com/apk/res/android">
        <services>
            <service type="interval" url="push.js"/>
        </services>
    </android>

その次に、下記のような感じで

app.js

function localPopIntent(popTime){
    var service_intent = Ti.Android.createServiceIntent({
        url: 'push.js'
    });
    //service_intent.putExtra('interval', 5 * 60 * 1000);//5分間隔で繰り返し実行。
    service = Ti.Android.createService(service_intent);
    service.start();
}

push.js

    var intent = Ti.Android.createIntent({ //①
        action: Ti.Android.ACTION_VIEW, 
    });
    var pending = Ti.Android.createPendingIntent({  //②
        'intent' : intent,
    });    
    var notification = Ti.Android.createNotification({ //③
        contentIntent: pending,//タップ時に発行されるIntentの指定
    //通知に表示されるアイコンの選択
        icon: Ti.App.Android.R.drawable.appicon,
        //通知メッセージが届いた際に画面上部の通知領域に表示されるメッセージ
        tickerText    : "業務終了時刻になりました。",
        contentTitle  : '業務終了時刻です',
        contentText   : '今日もお疲れ様でした。',
        defaults:  Titanium.Android.DEFAULT_ALL, // DEFAULT_LIGHTS | DEFAULT_SOUND | DEFAULT_VIBRATE通知時の反応
        flags : Titanium.Android.FLAG_SHOW_LIGHTS//通知があることを知らせる為にLEDを光らせる
    });
    Ti.Android.NotificationManager.notify(1, notification);//通知を出す。

こんな感じでやることでAndroidでもローカル通知を出すことが出来ます。app.jsの関数が動くと即座に通知が一度出ます。
このままだと関数が動くたびに通知が予約されて、大量にローカルで通知が出ることとなるので、お好みでtruefalse等で調整したりすると良いと思います。

もっとスマートなやり方無いのかな?

◇追記1
Ti.Android.NotificationManager.notify(1, notification);
という記述なのですが、ここでいう「1」と言うのはidになってます。idの1に通知を予約しておきますよ。のようなニュアンスだと勝手に解釈しています。
どうもこれ、他のアプリとidかぶりすると上書きしてしまうのかもしれません。(´・ω・`)
違うアプリを開いてぽちぽちした後にタイムカードアプリに戻ってくると通知が来ないのなんでだろうと思っていて、ふとした時にここの数値を「27」とか適当なものに置き換えると通知が普通に出るように……。

4週間目

画面が回転するとレイアウトがひどく残念なコトになるので回転を禁止しよう。

iPhoneは回転しないけど、Androidだと自動回転がONになっていると勝手に回転して、レイアウトを崩してしまいました。
これを阻止するためにどうにかしようと思い、下記のような感じで回転をしないようにしました。

//画面の回転禁止
$.timecard.orientationModes=[Titanium.UI.PORTRAIT];

外部アクセスは非同期で動くのやめて欲しい。同期しているような動きにしよう。

Ti.Network.createHTTPClientですが、接続成功した後にonload()部分が動作して行きます。

なので、例えばとある関数で、phpにアクセスしてデータベースからデータをアプリに持ってきてそのデータを処理する。なんて際にデータをアプリに持ってくる前にデータの処理が始まってundefinedになる。なんて事になってしまうので、どうにかしよう。どうにかしようとすると、こんな感じになる。


var testEvent = function (listenEvt) {
    Ti.API.info(listenEvt.data);
};
//イベントの登録
Ti.App.addEventListener("testFire", testEvent);

function timecard(data1,data2){
    var pram={"data1" :data1,
            "data2" :data2};
    var url = "/* お好きな接続したいPHP等のURL */";
    var client = Ti.Network.createHTTPClient({
        onload : function(e) {
        //接続後に実行される部分
            Ti.App.fireEvent("testFire", {data: this.responseText});
        },
        onerror : function(e) {
        Ti.API.info("errorです。");
        //接続失敗時に実行される部分
        },
        //タイムアウトの指定。(3000ms)
        timeout : 3000
    });
    switch(Ti.Platform.osname){//使用してるスマホOSのチェック
        case 'iphone':
        client.open("POST", url);
        client.setRequestHeader('Content-type','charset=utf-8');
        client.send(pram);
            break;
        case 'android':
        client.open("GET", url);
        client.setRequestHeader('Content-type','charset=utf-8');
        client.send(pram);
            break;
    }};
}

fireEventを登録してあげて、onloadで実行されるようにすれば順番に動作してもらえます。データも関数の方へ持って行くことができるので後は煮るなり焼くなり好きにできるというわけですね。

稚拙なプログラムですが、参考になれば幸いです。