2015年1月5日月曜日

Aipo と Google カレンダー連携 (5)

とりあえず出来たヽ(´ー`)ノ
以下,参考にしたサイトなど.

●GMail 受信インテントの受け取り方→ここ

●GMail にアクセス
最初は GMail API を使ってアクセスしようとしたが,Android 用のサンプルがなく断念.素直に IMAP アクセスにした.
IMAP アクセス は JavaMail でググれば大量にサンプルが出てくる.ただしライブラリは Android 用に修正したものでないとダメらしい.→ここ

JavaMail の GMail 拡張版は,うまく動作させることができなかった.→ここ

●JavaMail で GMail のゴミ箱に入れる
Aipo メールを処理した後,GMail のゴミ箱行きにする方法.→ここ

具体的なコード:
Folder inbox = store.getFolder( "Inbox" );
inbox.open( Folder.READ_WRITE );
int iMsgCount = inbox.getMessageCount();

Message messages[] = inbox.getMessages( 1, iMsgCount );
FetchProfile fp = new FetchProfile();
Folder trash = store.getFolder( "[Gmail]/ゴミ箱" );
inbox.fetch( messages, fp );

for( int i = 0; i < iMsgCount; ++i ){
    if( 捨てたいメール ){
        inbox.copyMessages( new Message[]{ messages[ i ] }, trash );
    }
}
[Gmail]/ゴミ箱 は多分 locale に依存するんだと思う.試しに [Gmail]/Trash をやってみたら怒られた.

●カレンダーの,イベント一覧取得,削除,追加する方法
これがかなりめんどくさかった(;´д⊂)
基本は,ここここなんだけど,このアプリは裏 (Service) でカレンダーデータにアクセスするため,getLoaderManager() が使えない.
ここみて適当に修正.

private static final int iIdxEventID    = 1;
private static final int iIdxTitle      = 2;

private CursorLoader m_Cursor;

private void AddTask(){
    // 現時刻~1年後後のイベントのみを取得
    String tz   = Time.TIMEZONE_UTC;
    Time time   = new Time( tz );
    time.setToNow();
    time.allDay = false;
    int begin   = Time.getJulianDay( time.toMillis( true ), 0 );
    time.year   += 1;
    int end     = Time.getJulianDay( time.toMillis( true ), 0 );

    Uri content_by_day_uri = CalendarContract.Instances.CONTENT_BY_DAY_URI;
    String[] instance_projection = new String[] {
        Instances._ID,
        Instances.EVENT_ID,
        Instances.TITLE,
    //  Instances.BEGIN,
    //  Instances.END,
    };
    String sort_order =
        Instances.BEGIN + " ASC, " + Instances.END + " DESC, "
            + Instances.TITLE + " ASC";
    
    Uri baseUri = buildQueryUri( begin, end, content_by_day_uri );
    
    m_Cursor = new CursorLoader(
        this, baseUri,
        instance_projection, null,
        null, sort_order
    );
    m_Cursor.registerListener( 0, this );
    m_Cursor.startLoading();
}

@Override
public void onLoadComplete( Loader loader, Cursor c ){
    Log.d( getClass().getSimpleName(), "onLoadFinished called." );
    ContentResolver cr = getContentResolver();
    
    if( c.moveToFirst()) do{
        if( 削除するイベント ){
            cr.delete(
                ContentUris.withAppendedId(Events.CONTENT_URI, c.getInt( iIdxEventID )),
                null, null
            );
        }
    }while( c.moveToNext());
    
    // イベントをカレンダーに追加
    // Tasks は ContentValues の配列
    for( ContentValues Task : Tasks ){
        cr.insert( CalendarContract.Events.CONTENT_URI, Task );
    }
    
    if (m_Cursor != null) {
        m_Cursor.unregisterListener(this);
        m_Cursor.cancelLoad();
        m_Cursor.stopLoading();
    }
}

private Uri buildQueryUri( int start, int end, Uri content_by_day_uri ){
    StringBuilder path = new StringBuilder();
    path.append( start );
    path.append( '/' );
    path.append( end );
    Uri uri = Uri.withAppendedPath( content_by_day_uri, path.toString());
    return uri;
}

Aipo と Google カレンダー連携 (4)

あけおめ.

=== これまでのお話 ===
・Aipo に入れたスケジュールを Google カレンダーに反映したかった (一方通行)
・Aipo にスケジュールを入れた時に投げられるメールをトリガに G カレンダーの API をキックして G カレンダーにスケジュールを追加するスクリプトを組んだ
・某所のセキュリティポリシーが変更され,G カレンダー API の URL がブロックされたっぽい←いまここ
=== ここまで ===

なんかもうイタチごっこの様相を呈してきた.
しかたがないので DD-WRT でサーバを立ち上げ,某所からの HTTP or SMTP アクセスをトリガにスケジュール追加するようなものを考えてみたが,残念ながら DD-WRT 用のまともなクロスコンパイル環境を構築できていない.(ちょっと複雑なものを DD-WRT で走らすとすぐに core dump する(;´д⊂))

なのでプラン B で,Aipo のスケジュール追加通知メールを GMail に転送し,Android 上でそのメールをトリガに G カレンダーに予定を追加するアプリを開発するプロジェクトを開始.
似たような既存アプリはもうありそうな気がするけど,Aipo はイベントの時間を修正するだけでメールをバンバン投げてくるので,そのままだと G カレンダーに大量にイベントが登録されてしまう.なので,同一のイベント名がある場合は古いイベントは削除するようにするのがミソで (スクリプトではこうやっていた),ここまでやってくれるアプリは多分無い.

フローとしてはこんな感じ.
1. 某所の Aipo のスケジュール追加メールを Android の GMail アカウントに転送
  以下,Android 端末での動作
2. GMail 受信インテントを受け取ってアプリが起動
3. IMAP で GMail の受信トレイを走査し,Aipo メールを取得
4. Aipo メールの内容に従ってカレンダーにイベントを追加する

とりあえずやっつけでアプリアイコンだけ作った.
Aipo のアイコンを Google のプロダクトっぽく色変えただけwww