marunomaruno-memo

marunomaruno-memo

[Android] クリックしたタイムスタンプを保存して、メールで送信する

2012年06月24日 | Android
[Android] クリックしたタイムスタンプを保存して、メールで送信する
================================================================================

ちょっと必要があって、標記のアプリを作った。
メールの送信先のアドレスも固定(リソースで定義)。

タイムスタンプは long 型なので、それらの値を List<Long> で保持したいところ。でも、
onSaveInstanceState()、onRestoreInstanceState() でインスタンス間の受け渡しを行う
にしても、残念ながら Bundle クラスには、putLongArrayList() というメソッドはない。
しょうがないので、long 型のタイムスタンプを上位 4 バイトと下位 4 バイトに分けて、
上位 4 バイトは long 型、下位 4 バイトは int 型のリスト(正確には Integer 型のリ
スト)に保存することにした。そうすることで、putIntegerArrayList() メソッドが使え
る。

しかし、putLongArrayList() がないのは、int が整数の標準の型なのでしょうがないと
して、putIntegerArray() がないというのは、納得がいかない気がする。というよりも、
putIntegerCollection() というのがあってもよさそうだと思う。


■ アクティビティ

□ TimestampLoggerActivity.java
---
package jp.marunomaruno.android.timestamplogger;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

/**
 * クリックしたタイムスタンプを保存して、メールで送信する。 
 * メールの送信先のアドレスも固定。
 *
 * @author marunomaruno
 * @version 1.0, 2012-06-20
 */
public class TimestampLoggerActivity extends Activity {
    private static final String TEMP_FILE_NAME = "data.dat";
    private static final long TIMESTAMP_HEIGHT_BYTES_MASK = 0xFFFFFFFF00000000L;
                                  // long型の上位4バイトを取得するマスク // (1)
    private static final String TIMESTAMP_LIST = "timestampList";
    private static final String TIMESTAMP_HEIGHT_BYTES = "timestampHightBytes";
    private ArrayList<Integer> timestampList; 
                                 // タイムスタンプの下位4バイト分のリスト // (2)
    private long timestampHightBytes = 0; // タイムスタンプの上位4バイト // (3)

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        timestampList = new ArrayList<Integer>();
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        timestampHightBytes = savedInstanceState
                .getLong(TIMESTAMP_HEIGHT_BYTES);
        timestampList = savedInstanceState.getIntegerArrayList(TIMESTAMP_LIST);

        System.out.println("onRestoreInstanceState: " + timestampList);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putLong(TIMESTAMP_HEIGHT_BYTES, timestampHightBytes);
        outState.putIntegerArrayList(TIMESTAMP_LIST, timestampList);
        System.out.println("onSaveInstanceState: " + timestampList);
    }

    @Override
    protected void onPause() {
        super.onPause();
        try {
            write();

        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("onPause: " + timestampList);
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        try {
            read();

        } catch (FileNotFoundException e) {
            assert true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("onRestart: " + timestampList);
    }

    /**
     * タイムスタンプを取得するボタンのクリック・ハンドラー
     *
     * @param view
     */
    public void onStampButtonClick(View view) {
        // リストの最初のタイムスタンプの上位4バイトを保持する
        if (timestampHightBytes == 0) {
            timestampHightBytes = System.currentTimeMillis()
                    & TIMESTAMP_HEIGHT_BYTES_MASK; // (4)
        }

        // タイムスタンプの下位4バイトをリストに追加する
        timestampList.add((int) System.currentTimeMillis()); // (5)
        TextView stampView = (TextView) findViewById(R.id.stampView);
        stampView.setText(toTimestampStrings());
    }

    /**
     * タイムスタンプのリストをクリアするボタンのクリック・ハンドラー
     *
     * @param view
     */
    public void onClearButtonClick(View view) {
        timestampList.clear();
        TextView stampView = (TextView) findViewById(R.id.stampView);
        stampView.setText("");
    }

    /**
     * タイムスタンプを送信するボタンのクリック・ハンドラー
     *
     * @param view
     */
    public void onEmailButtonClick(View view) {
        if (timestampList.size() == 0) {
            TextView stampView = (TextView) findViewById(R.id.stampView);
            stampView.setText(R.string.noTimestamp);
            return;
        }

        Uri uri = Uri.parse("mailto:" + getString(R.string.emailAddress)); // (6)
        Intent intent = new Intent(Intent.ACTION_SENDTO, uri); // (7)
        intent.putExtra(Intent.EXTRA_SUBJECT, String.format(
                "timestamp %1$tF %1$tT",
                (timestampHightBytes | timestampList.get(0)))); // (8)
        intent.putExtra(Intent.EXTRA_TEXT, toTimestampStrings()); // (9)
        startActivity(intent); // (10)
    }

    /**
     * タイムスタンプのリストを文字列化する。 
     * タイムスタンプは、JISの日時の文字列(yyyy-mm-dd hh:mm:ss)であらわし、
     * これをリストとして並べたもの。
     *
     * @return タイムスタンプのリストの文字列
     */
    private String toTimestampStrings() {
        StringBuilder sb = new StringBuilder();
        for (int timestamp : timestampList) {
            sb.append(String.format("[%1$tF %1$tT]%n",
                    (timestampHightBytes | timestamp)));
        }
        return sb.toString();
    }

    /**
     * データをファイルに一時保存のため書き出す。
     *
     * @throws IOException
     */
    public void write() throws IOException {
        DataOutputStream out = new DataOutputStream(openFileOutput(TEMP_FILE_NAME,
                MODE_PRIVATE));

        out.writeLong(timestampHightBytes);
        for (int timestamp : timestampList) {
            out.writeInt(timestamp);
        }

        out.close();
    }

    /**
     * 一時保存されたデータをファイルから読み出す。
     *
     * @throws IOException
     */
    public void read() throws IOException {
        DataInputStream in = new DataInputStream(openFileInput(TEMP_FILE_NAME));

        timestampList.clear();

        try {
            timestampHightBytes = in.readLong();
            while (true) {
                timestampList.add(in.readInt());
            }

        } catch (EOFException e) { // (10)
            assert true;
        }

        in.close();
    }

}
---

(1) long型の上位4バイトを取得するマスク

long 型のタイムスタンプを上位 4 バイトと下位 4 バイトに分けるためのマスク。

    private static final long TIMESTAMP_HEIGHT_BYTES_MASK = 0xFFFFFFFF00000000L; // (1)


(2) タイムスタンプの下位4バイト分のリスト

    private ArrayList<Integer> timestampList;  // (2)


(3) タイムスタンプの上位4バイト

    private long timestampHightBytes = 0;  // (3)


(4) リストの最初のタイムスタンプの上位 4 バイトを保持する

TIMESTAMP_HEIGHT_BYTES_MASK と AND を取ることで、上位 4 バイト分を確保する。

    timestampHightBytes = System.currentTimeMillis()
                    & TIMESTAMP_HEIGHT_BYTES_MASK; // (4)


(5) タイムスタンプの下位4バイトをリストに追加する

下位 4 バイトは、int 型にキャストすればよい。

    timestampList.add((int) System.currentTimeMillis()); // (5)


(6)-(10) タイムスタンプをメールで送信する

メールのあて先アドレスを Uri インスタンスとして組み立てる。

    Uri uri = Uri.parse("mailto:" + getString(R.string.emailAddress)); // (6)

メールを送るためのインテント・インスタンスを生成する

    Intent intent = new Intent(Intent.ACTION_SENDTO, uri); // (7)

メールの件名を設定する。このとき、件名に最初のタイムスタンプを入れるので、タイム
スタンプを組み立ても行う。
上位 4 バイトと下位 4 バイトに分かれているものを、OR することで、ふたたび long 
型にする。

    intent.putExtra(Intent.EXTRA_SUBJECT, String.format(
                "timestamp %1$tF %1$tT",
                (timestampHightBytes | timestampList.get(0)))); // (8)

本文に、タイムスタンプを文字列化して並べる。

    intent.putExtra(Intent.EXTRA_TEXT, toTimestampStrings()); // (9)


メール送信のアクティビティを起動する。

    startActivity(intent); // (10)


(11) EOF の検知

DataInputStream の readInt() メソッドを使っているので、EOF は EOFException を使
って検知する。

    } catch (EOFException e) { // (11)


■ レイアウト

タイムスタンプの表示は、今回のわたしの使い方としては、せいぜい 10 件程度なので、
ListView などを使わずに、単純に TextView を使っている。

□ res/values/strings.xml
---
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/stamp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onStampButtonClick"
            android:text="@string/stampButtonLabel"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClearButtonClick"
            android:text="@string/clearButtonLabel"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Button
            android:id="@+id/email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onEmailButtonClick"
            android:text="@string/emailButtonLabel"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    </LinearLayout>

    <TextView
        android:id="@+id/stampView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="" />

</LinearLayout>
---


■ リソース

【emailアドレス】部分に、タイムスタンプを送信したいあて先を指定する。


□ res/values/strings.xml
---
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="hello">Hello World, TimestampLoggerActivity!</string>
    <string name="app_name">TimestampLogger</string>
    <string name="stampButtonLabel">クリック</string>
    <string name="clearButtonLabel">クリア</string>
    <string name="emailButtonLabel">email</string>
    <string name="noTimestamp">タイムスタンプがありません。メール送信はしません。</string>
    <string name="emailAddress">【emailアドレス】</string>

</resources>
---

                                                                            以上


最新の画像もっと見る

コメントを投稿