ラベル android の投稿を表示しています。 すべての投稿を表示
ラベル android の投稿を表示しています。 すべての投稿を表示

2016年1月2日土曜日

android:Googleトレンドから急上昇ワードを取得する

androidアプリ内でGoogleトレンドの急上昇ワードを扱う。
  • Googleトレンドへのアクセスは、AsyncTaskLoaderを使用して非同期で通信する。
  • Googleトレンドから取得したXMLのパースはXmlPullParserで解析する。
  • 取得したワードはリストビュー(ListView)で表示する。

MainActivity


package net.kuttya.googletrendaccesssample;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Xml;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import org.xmlpull.v1.XmlPullParser;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<String> {
    private final String GOOGLETREND_URL = "http://www.google.co.jp/trends/hottrends/atom/hourly";
    private final String ENCODING = "UTF-8";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Googleトレンドからワードを取得(非同期)
        getSupportLoaderManager().initLoader(0, null, this);

    }

    @Override
    public Loader<String> onCreateLoader(int id, Bundle args) {
        // GoogleトレンドにアクセスするAsyncTaskLoaderを返す
        return new HttpAccessingTaskLoader(this, GOOGLETREND_URL);
    }

    @Override
    public void onLoadFinished(Loader<String> loader, String data) {
        // Googleトレンドから取得したXMLから、CDATAの部分を抽出
        String htmlData = getHtmlData(data);

        // CDATAからワードを抽出
        List<String> words = getListData(htmlData);

        // リストビューのアダプタを作成、Googleトレンドのワードを設定
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, words);

        // リストビューにアダプタを設定
        ListView view = (ListView)findViewById(R.id.listView);
        view.setAdapter(adapter);
    }

    @Override
    public void onLoaderReset(Loader<String> loader) {
    }

    /**
     * Googleトレンドから取得したXMLデータから、CDATAの部分を抽出する
     * @param data 解析するXMLデータ
     * @return CDATA内のHTMLデータ
     */
    private String getHtmlData(String data) {
        String htmlData = "";
        try {
            // Xmlパーサで解析
            XmlPullParser xmlPullParser = Xml.newPullParser();
            xmlPullParser.setInput(new ByteArrayInputStream(data.getBytes(ENCODING)), ENCODING);
            int eventType;
            String tagName;
            while((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
                if(eventType == XmlPullParser.START_TAG) {
                    tagName = xmlPullParser.getName();

                    // contentタグの中身を抽出する
                    if(tagName.equals("content")) {
                        htmlData = xmlPullParser.nextText();
                        break;
                    }
                }
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }

        return htmlData;
    }

    /**
     * CDATA内のHTMLからワードを抽出する
     * @param htmlData 解析するHTMLデータ
     * @return 抽出したワードのリスト
     */
    private List<String> getListData(String htmlData) {
        List<String> listData = new ArrayList<String>();

        try {
            // HTMLでもXmlパーサで解析可能
            XmlPullParser xmlPullParser = Xml.newPullParser();
            xmlPullParser.setInput(new ByteArrayInputStream(htmlData.getBytes(ENCODING)), ENCODING);

            // content内の構造は下記
            // <li><span><a>データ</a></span></li>
            int eventType;
            String tagName;
            String word;
            boolean inLi = false;
            boolean inSpan = false;
            while((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
                switch(eventType) {
                    case XmlPullParser.START_TAG:
                        tagName = xmlPullParser.getName();
                        if(tagName.equals("li")) {
                            inLi = true;
                            break;
                        }
                        if(inLi != true) {
                            break;
                        }

                        // <Li>タグ内部のみ以下を実施

                        if(tagName.equals("span")) {
                            inSpan = true;
                            break;
                        }
                        if(inSpan != true) {
                            break;
                        }

                        // <span>タグ内部のみ以下を実施

                        if(tagName.equals("a")) {
                            // ワードを抽出
                            word = xmlPullParser.nextText();
                            listData.add(word);
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        tagName = xmlPullParser.getName();
                        if(tagName.equals("li")) {
                            inLi = false;
                        }
                        else if(tagName.equals("span")) {
                            inSpan = false;
                        }
                        break;
                }
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }

        return listData;
    }
}



AsyncTaskLoader


package net.kuttya.googletrendaccesssample;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

/**
 * Created by junichi on 2016/01/02.
 */
public class HttpAccessingTaskLoader extends AsyncTaskLoader<String> {
    private String mUrl;

    public HttpAccessingTaskLoader(Context context, String url) {
        super(context);
        mUrl = url;
    }

    @Override
    public String loadInBackground() {
        String result = "";

        try {
            // URL
            URL url = new URL(mUrl);

            // 接続
            URLConnection connection = url.openConnection();

            // 結果を文字列で返す
            BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder sb = new StringBuilder();
            String line;
            while((line = br.readLine()) != null) {
                sb.append(line);
            }
            result = sb.toString();
        }
        catch(Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    protected void onStartLoading() {
        forceLoad();
    }
}




レイアウト


リストビューだけ。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="net.kuttya.googletrendaccesssample.MainActivity">

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"/>
</RelativeLayout>



AndroidManifest.xml


パーミッションの追加を忘れずに。
<?xml version="1.0" encoding="utf-8"?>
<manifest package="net.kuttya.googletrendaccesssample"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

</manifest>


実行結果




2014年2月8日土曜日

androidからHttpClientでHTTP-Post通信をする

androidでは、HTTP通信をするための方法として、下記のふたつがある。

  • HttpURLConnection (java.net)
  • HttpClient (org.apache.http)

ここでは、HttpClientを使用する方法を紹介する。

HttpURLConnectionとHttpClientの違いについては、下記のサイトが参考になった。
Y.A.M の 雑記帳: Android Apache HTTP Client と HttpURLConnection どっちを使うべき?

HttpURLConnectionを使用する方法は下記を参照。
androidからAsyncTaskLoaderでHTTP-Post通信をする

 

Http通信処理を実現するためには、Androidアプリで下記の3つの部品を実装する。

  • UI処理
  • 通信データ
  • バックグラウンド処理

Androidアプリのマニフェストファイルにインターネット接続のパーミッションを追加すること。

また、サーバ側でも何らかの応答を返す処理を実装する必要がある。

UI処理

アプリケーション実行時に呼び出されるアクティビティ。
ここで通信データを生成し、バックグラウンド処理に渡す。

package net.kuttya.httppostsample2;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;

public class MainActivity extends FragmentActivity implements LoaderCallbacks {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// サーバにデータ送信(初期化)
TransData sendData = new TransData();
Bundle args = new Bundle();
sendData.data = "test";
args.putSerializable("data", sendData);
getSupportLoaderManager().initLoader(0, args, this);
}

public Loader onCreateLoader(int id, Bundle args) {
if( null != args ) {
TransData sendData = (TransData) args.getSerializable("data");

return new HttpAccesser(this, sendData);
}
return null;
}

public void onLoadFinished(Loader loader, TransData recvData) {
// AsyncTaskLoaderの処理が終了したら呼び出される
if(null == recvData) {
return;
}
else {

}
}

public void onLoaderReset(Loader arg0) {
// AsyncTaskLoaderが破棄されるときに呼び出される
}
}

通信データ


実際に使用する通信データはメッセージ種別など様々な情報が含まれていると思うが、ここでは簡略化して文字列のみを送受信することにする。

package net.kuttya.httppostsample2;

import java.io.Serializable;

public class TransData implements Serializable {
private static final long serialVersionUID = 1L;

public String data;
}

バックグラウンド処理


ここでHTTP-Post通信を実行する。

package net.kuttya.httppostsample2;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

public class HttpAccesser extends AsyncTaskLoader {
private final static String SERVER_URL = "http://www.sample.com/index.php";
private final static String ENCODE = "UTF-8";

private TransData mSendData = null; // 送信データ
private DefaultHttpClient mHttpClient = null; // HTTPクライアント
private HttpPost mPost = null; // POST通信

public HttpAccesser(Context context, TransData sendData) {
super(context);

this.mSendData = sendData;
}

public TransData loadInBackground() {
// ここに非同期処理で実施したい処理を記載する
TransData recvData = null;

recvData = send(mSendData);

return recvData;
}

public void onCanceled(Boolean data) {
// キャンセル処理
}

protected void onStartLoading() {
// 開始処理
forceLoad();
}

public TransData send(TransData sendData) {
boolean ret = false;
TransData recvData = null;
List params = new ArrayList();
HttpResponse response = null;
HttpEntity resEntity = null;
int resStatus = 0;

// 接続初期化
ret = initConnection();
if(false == ret) {
return null;
}

try {
// 送信データ生成
params.add(new BasicNameValuePair("data", sendData.data));
// UTF-8にエンコードする
mPost.setEntity(new UrlEncodedFormEntity(params, ENCODE));

// 送信
response = mHttpClient.execute(mPost);

// リクエスト結果を取得
resStatus = response.getStatusLine().getStatusCode();

if(200 == resStatus) {
// リクエスト成功
resEntity = response.getEntity();
recvData = new TransData();
recvData.data = EntityUtils.toString(resEntity, ENCODE);
}
} catch(Exception e) {
e.printStackTrace();
recvData = null;
} finally {
try {
if(mHttpClient != null) {
mHttpClient.getConnectionManager().shutdown();
}
} catch(Exception e) {
recvData = null;
}
}

return recvData;
}

private boolean initConnection() {
mHttpClient = new DefaultHttpClient();
mPost = new HttpPost(SERVER_URL);

return true;
}
}

マニフェストファイルにパーミッション追加


下記のパーミッションを追加する。


2014年2月4日火曜日

androidからAsyncTaskLoaderでHTTP-Post通信をする

Android 3.0からUIスレッドではインターネットへの接続ができなくなった。

そこで、AsyncTaskLoaderを使って、Android 4.0.3でHTTP-Post通信を実行してみる。
HTTP通信には、HttpURLConnectionクラスを使用する。

処理を実現するためには、Androidアプリで下記の3つの部品を実装する。

  • UI処理
  • 通信データ
  • バックグラウンド処理

Androidアプリのマニフェストファイルにインターネット接続のパーミッションを追加すること。

また、サーバ側でも何らかの応答を返す処理を実装する必要がある。

UI処理

アプリケーション実行時に呼び出されるアクティビティ。
ここで通信データを生成し、バックグラウンド処理に渡す。

package net.kuttya.httppostsample2;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;

public class MainActivity extends FragmentActivity implements LoaderCallbacks {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// サーバにデータ送信(初期化)
TransData sendData = new TransData();
Bundle args = new Bundle();
sendData.data = "test";
args.putSerializable("data", sendData);
getSupportLoaderManager().initLoader(0, args, this);
}

public Loader onCreateLoader(int id, Bundle args) {
if( null != args ) {
TransData sendData = (TransData) args.getSerializable("data");

return new HttpAccesser(this, sendData);
}
return null;
}

public void onLoadFinished(Loader loader, TransData recvData) {
// AsyncTaskLoaderの処理が終了したら呼び出される
if(null == recvData) {
return;
}
else {

}
}

public void onLoaderReset(Loader arg0) {
// AsyncTaskLoaderが破棄されるときに呼び出される
}
}

通信データ


実際に使用する通信データはメッセージ種別など様々な情報が含まれていると思うが、ここでは簡略化して文字列のみを送受信することにする。

package net.kuttya.httppostsample2;

import java.io.Serializable;

public class TransData implements Serializable {
private static final long serialVersionUID = 1L;

public String data;
}

バックグラウンド処理


ここでHTTP-Post通信を実行する。

package net.kuttya.httppostsample2;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

public class HttpAccesser extends AsyncTaskLoader {
private final static String SERVER_URL = "http://www.sample.com/index.php";
private final static String ENCODE = "UTF-8";

private TransData mSendData = null; // 送信データ
private HttpURLConnection mHttpConnection = null; // HTTP通信

public HttpAccesser(Context context, TransData sendData) {
super(context);

this.mSendData = sendData;
}

public TransData loadInBackground() {
// ここに非同期処理で実施したい処理を記載する
TransData recvData = null;

recvData = send(mSendData);

return recvData;
}

public void onCanceled(Boolean data) {
// キャンセル処理
}

protected void onStartLoading() {
// 開始処理
forceLoad();
}

public TransData send(TransData sendData) {
OutputStream out = null; // HTTPリクエスト送信用ストリーム
InputStream in = null; // HTTPレスポンス取得用ストリーム
BufferedReader reader = null; // レスポンスデータ出力用バッファ
int retByte = 0;
boolean ret = false;
byte[] sendMessage = null;
byte[] recvMessage = null;
TransData recvData = null;

// 接続初期化
ret = initConnection();
if(false == ret) {
return null;
}

try {
// 送信データ生成
sendMessage = new byte[255];
System.arraycopy(mSendData.data.getBytes(), 0, sendMessage, 0, mSendData.data.length());

// 接続
mHttpConnection.connect();

// データを出力
out = new BufferedOutputStream(mHttpConnection.getOutputStream());
out.write(sendMessage);
out.flush();

// レスポンスを取得
in = new BufferedInputStream(mHttpConnection.getInputStream());
recvMessage = new byte[1024];
retByte = in.read(recvMessage);
if(0 > retByte) {
return null;
}

// 受信データ解析
recvData = new TransData();
ret = analyzeRecvData(recvMessage, recvData);
if(false == ret) {
return null;
}
} catch(Exception e) {
e.printStackTrace();
recvData = null;
} finally {
try {
if(reader != null) {
reader.close();
}
if(in != null) {
in.close();
}
if(out != null) {
out.close();
}
if(mHttpConnection != null) {
mHttpConnection.disconnect();
}
} catch(Exception e) {
recvData = null;
}
}

return recvData;
}

private boolean initConnection() {
// URL指定
URL url;
try {
url = new URL(SERVER_URL);

// HttpURLConnectionインスタンス作成
mHttpConnection = (HttpURLConnection)url.openConnection();

// POST設定
mHttpConnection.setRequestMethod("POST");

// HTTPヘッダの「Content-Type」を「application/octet-stream」に設定
mHttpConnection.setRequestProperty("Content-Type","application/octet-stream");

// URL 接続を使用して入出力を行う
mHttpConnection.setDoInput(true);
mHttpConnection.setDoOutput(true);

// キャッシュは使用しない
mHttpConnection.setUseCaches(false);
} catch (Exception e) {
e.printStackTrace();
return false;
}
finally {
if(mHttpConnection != null) {
mHttpConnection.disconnect();
}
}
return true;
}

private boolean analyzeRecvData(byte[] recvMessage, TransData recvData) {
try {
if(null == recvData)
{
return false;
}

recvData.data = new String(recvMessage, ENCODE);
}
catch(Exception e) {
e.printStackTrace();
return false;
}

return true;
}

}

マニフェストファイルにパーミッション追加


下記のパーミッションを追加する。



サーバ処理


サーバ側はPHPで実装する。
HTTPリクエストを取得し、受信したデータを送り返す。

<?php
// 受信データ格納
$data = file_get_contents("php://input");

echo "POST DATA RECEIVED. data[".$data."]"

?>

2014年2月3日月曜日

HTTP通信でNetworkOnMainThreadException発生

昔作成したコードを動かしてみたら、NetworkOnMainThreadExceptionが発生した。
昔は問題なく動作していたにも関わらず、だ。

原因は、Android 3.0からインターネット通信はUIスレッドでは行えなくなっている様子。

AsyncTaskか、AsyncTaskLoaderを使用しなくてはならないらしい。
Android4.0以降であれば、AsyncTaskLoaderがよいと思われる。

一時的に、下記のコードを埋め込んでおけば、例外を抑制できる。
埋め込む場所は、UIスレッドのonCreateメソッド開始直後、super.onCreateメソッド実行前。

  1. StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());  

2013年8月13日火曜日

android:複数のアクティビティでデータを共有する

アクティビティ間でのデータのやりとりはインテントを使用すれば可能であるが、
そのデータが共有データである場合、画面が遷移するたびにインテントにput、getを繰り返すのは効率がよくない。
そこで、すべてのアクティビティから参照できるよう、Singletonでデータを保持する方法を試してみる。

  • 共有するデータクラスをSingletonで作成する。
  • 共有データが必要なアクティビティにて、getInstanceでインスタンスを取得する。

共有するデータクラスは以下のように定義する。
package net.kuttya.singletonsample;

public class SampleData {
    // 共有するデータクラス

    // 自クラスをインスタンスに持つ
    private static SampleData data = null;

    // メンバ変数
    private int dataInt = 0;

    public static SampleData getInstance() {
        if(data == null) {
            // インスタンスがnullの場合のみ生成する
            data = new SampleData();
        }
        return data;
    }

    private SampleData() {
        // コンストラクタをprivateにすることで、
        // インスタンスを新しく生成できなくする
    }

    public void setDataInt(int i) {
        this.dataInt = i;
    }

    public int getDataInt() {
        return this.dataInt;
    }
}

呼び出し元のアクティビティでは、次の動作を実装する。
package net.kuttya.singletonsample;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button b = (Button)findViewById(R.id.button1);
        b.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                // 共有するデータを設定
                SampleData data = SampleData.getInstance();
                data.setDataInt(12345);

                // インテント生成
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setClassName("net.kuttya.singletonsample", "net.kuttya.singletonsample.CallActivity");

                // アクティビティ呼び出し
                startActivity(intent);
            }
        });
    }
}

呼び出し先のアクティビティでは、次の動作を実装する。
package net.kuttya.singletonsample;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class CallActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_call);

        // 共有データを取得
        SampleData data = SampleData.getInstance();

        // 表示
        TextView textView1;
        textView1 = (TextView) findViewById(R.id.textView1);
        textView1.setText(Integer.toString(data.getDataInt()));
    }
}

ただし、データの共有がうまくいかないケースもある。
例えば、アクティビティ単位でプロセスを分けた場合、
Singletonであったとしても、それぞれのプロセスでインスタンスが生成されてしまう可能性がある。
他にも、共有するデータにキャッシュ情報が含まれていたりすると、
キャッシュがなにかのきっかけでクリアされてしまった場合、
共有データないのキャッシュ情報には参照先が存在しなくなる。

保持するデータには注意が必要である。

にほんブログ村 IT技術ブログ Androidアプリ開発へ

2013年8月12日月曜日

android:Parcelableインタフェースを使用したアクティビティ間のデータ受け渡し

androidでは、プロセス間通信が頻繁に行われる。
特に、アクティビティ間でデータをやりとりする場合にはインテントを利用する。
インテントでインスタンスをやりとりする場合は、一工夫必要である。

インスタンスはintなどの型とはことなり、なんでもインテントにputすることはできない。
インテントにインスタンスを受け渡すためには、Parcelableインタフェースを実装する必要がある。

Parcelableインタフェースを利用するには、以下の手順が必要となる。
  • インテントに受け渡すクラスにParcelableを実装する。
  • インテントに受け渡すクラスでCREATORメンバ変数を定義する。
  • 受け渡し元のアクティビティにおいて、インテントにはParcelableを実装したクラスのインスタンスをputする。
  • 受け渡し先のアクティビティにおいて、同様にインテントにはParcelableを実装したクラスのインスタンスにgetする。
受け渡しするデータクラスは以下のように定義する。
package net.kuttya.parcelablesample;

import android.os.Parcel;
import android.os.Parcelable;

public class SampleData implements Parcelable {
    // インテントで受け渡しするデータクラス

    public String sampleString;
    public int sampleInt;
    public int[] sampleIntArray;

    public int describeContents() {
     // ファイルディスクリプタを返す(Parcelableインタフェースの実装メソッド)
        // ファイルディスクリプタを持たない場合は0を返す
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
        // シリアライズ処理(Parcelableインタフェースの実装メソッド)

        dest.writeString(sampleString);
        dest.writeInt(sampleInt);
        dest.writeIntArray(sampleIntArray);
    }

    public static final Parcelable.Creator CREATOR =
            new Parcelable.Creator() {

                public SampleData createFromParcel(Parcel source) {
                    // デシリアライズ処理
                    // writeToParcelメソッドでシリアライズした順番と同じ順番でメンバ変数をデシリアライズすること

                    // インスタンス生成
                    SampleData data = new SampleData();

                    // インスタンスに受け取るデータを格納する
                    data.sampleString = source.readString();
                    data.sampleInt = source.readInt();

                    // 配列の場合は、引数に格納先を指定する
                    data.sampleIntArray = new int[data.sampleInt];
                    source.readIntArray(data.sampleIntArray);

                    return data;
                }

                public SampleData[] newArray(int size) {
                    // 配列を受け渡す処理

                    return new SampleData[size];
                }
            };
}

受け渡し元のアクティビティでは、次の動作を実装する。
package net.kuttya.parcelablesample;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button b = (Button)findViewById(R.id.button1);
        b.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                // シリアライズするインスタンスを生成
                SampleData data = new SampleData();
                data.sampleString = "abcde";
                data.sampleInt = 3;
                data.sampleIntArray = new int[data.sampleInt];
                data.sampleIntArray[0] = 123;
                data.sampleIntArray[1] = 456;
                data.sampleIntArray[2] = 789;

                // インテント生成
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setClassName("net.kuttya.parcelablesample", "net.kuttya.parcelablesample.CallActivity");

                // データをシリアライズ
                intent.putExtra("data", data);

                // アクティビティ呼び出し
                startActivity(intent);
            }
        });
    }
}

受け渡し先のアクティビティでは、次の動作を実装する。
package net.kuttya.parcelablesample;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

public class CallActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_call);

        // インテント取得
        Intent intent = getIntent();

        //データをデシリアライズして受け取る
        SampleData data = intent.getParcelableExtra("data");

        // 表示
        TextView textView1,textView2,textView3;
        textView1 = (TextView) findViewById(R.id.textView1);
        textView2 = (TextView) findViewById(R.id.textView2);
        textView3 = (TextView) findViewById(R.id.textView3);
        textView1.setText(data.sampleString);
        textView2.setText(Integer.toString(data.sampleInt));
        textView3.setText(Integer.toString(data.sampleIntArray[0]));
    }
}


にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年12月31日月曜日

android:プリファレンスでデータを保存する

プリファレンスを使うことによって、android端末内にデータを保存できる。

プリミティブな型でデータを保存することは少ないかと思うが、
  • 保存できる型は、
     boolean
     float
     int
     long
     String
  • データの格納先は「/data/data/パッケージ名/shared_prefs/ファイル名.xml」
  • SharedPreferencesのインスタンスはgetSharedPreferencesで取得する。
  • getSharedPreferencesメソッドの第1引数は、ファイル名。
     下記のサンプルの場合、「sample.xml」がファイル名となる。
  • getSharedPreferencesメソッドの第2引数は、プリファレンスへのアクセスモード。
     MODE_PRIVATE:他のアプリケーションからはアクセスできない。
     MODE_WORLD_READABLE:他のアプリケーションからの読み込みを許可する。
     MODE_WORLD_WRITEABLE:他のアプリケーションからの書き込みを許可する。
  • プリファレンスへの読み書きはSharedPreferences.Editorを使う。

package net.kuttya.sharedpreferencessample;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初期化
        SharedPreferences preferences = getSharedPreferences("sample", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();

        // 書き込み
        // 第1引数はキー、第2引数は書き込む値
        editor.putBoolean("boolkey", true);
        editor.putFloat("floatkey", 3.5f);
        editor.putInt("intkey", 7);
        editor.putLong("longkey", 9);
        editor.putString("Stringkey", "文字列");
        editor.commit();

        // 読み込み
        // 第1引数はキー、第2引数はデフォルト値
        boolean b = preferences.getBoolean("boolkey", false);
        float f = preferences.getFloat("floatkey", 1.1f);
        int i = preferences.getInt("intkey", 1);
        long l = preferences.getLong("longkey", 5);
        String str = preferences.getString("Stringkey", "");
    }
}


にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年12月30日日曜日

android:JSONを使う(Twitter-APIの検索結果をパースする)

JSON形式のデータをandroidで扱う。

JSON形式のデータは、WebAPIから取得することにする。
WebAPIは何でもいいのだが、今回はTwitter-APIを使うことにする。

次のサイトにAPIの使い方が書かれているため、参考にしてほしい。
https://dev.twitter.com/docs/api/1/get/search

  • Uri.Builderで、WebAPIのリクエストURLを作成する。
  • HttpURLConnectionで、WebAPIに対してリクエストを投げる。
  • WebAPIから受け取ったレスポンス(JSON形式データ)を、BufferedInputStream、ByteArrayOutputStreamを使用して文字列データに変換する。
  • JSONObject、JSONArrayを使用して、JSONデータをパースする。

package net.kuttya.jsonsample;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.json.JSONArray;
import org.json.JSONObject;

import android.net.Uri;
import android.os.Bundle;
import android.os.StrictMode;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class JsonSampleActivity extends Activity {
    private EditText editSearchWord;
    private TextView viewSearchResult;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        // おまじない
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editSearchWord = (EditText)findViewById(R.id.edittext_searchWord);
        viewSearchResult = (TextView)findViewById(R.id.textview_serchResult);
    }

    // 検索ボタン押下処理
    public void onClickSearch(View view) {

        // 検索ワードのチェック
        String searchWord = editSearchWord.getText().toString();
        if(searchWord.length() < 1) {
            viewSearchResult.setText("検索する文字列を指定してください。");
            return;
        }

        // Twitter-APIのURLを生成
        Uri.Builder uriBuilder = new Uri.Builder();
        uriBuilder.scheme("http");
        uriBuilder.authority("search.twitter.com");
        uriBuilder.path("search.json");
        uriBuilder.appendQueryParameter("lang", "ja");
        uriBuilder.appendQueryParameter("rpp", "20");
        uriBuilder.appendQueryParameter("q", searchWord);
        String uriStr = uriBuilder.toString();

        try {
            // コネクション生成
            HttpURLConnection connection = null;
            URL url = new URL(uriStr);
            connection = (HttpURLConnection)url.openConnection();
            connection.setRequestMethod("GET");

            // リクエスト送信
            connection.connect();

            // レスポンスコードチェック
            if(connection.getResponseCode() != 200) {
                viewSearchResult.setText("検索失敗!!");
                return;
            }

            // レスポンス文字列取得
            BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream());
            ByteArrayOutputStream responseArray = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];

            int length;
            while((length = inputStream.read(buff)) != -1) {
                if(length > 0) {
                    responseArray.write(buff, 0, length);
                }
            }

            // JSONをパース
            StringBuilder viewStrBuilder = new StringBuilder();
            JSONObject jsonObj = new JSONObject(new String(responseArray.toByteArray()));
            JSONArray result = jsonObj.getJSONArray("results");
            for(int i = 0; i < result.length(); i++) {
                JSONObject tweet = result.getJSONObject(i);
                viewStrBuilder.append(tweet.getString("created_at") + "\n");        // つぶやき日時
                viewStrBuilder.append(tweet.getString("from_user_name") + "\n");    // 投稿者
                viewStrBuilder.append(tweet.getString("text") + "\n");                // 投稿内容
                viewStrBuilder.append("----------\n");
            }

            // 表示
            viewSearchResult.setText(viewStrBuilder.toString());
        } catch(Exception e) {
            viewSearchResult.setText(e.getMessage());
        }
    }
}


マニフェストファイルにインターネットを使う旨を登録する。
    

検索結果を取得したときのキャプチャ。






















にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年7月4日水曜日

android:通話ログを取得する

発着信ログにアクセスするには、ContentResolverクラスのqueryメソッドにを使用する。
queryメソッドには引数が5つあり、それぞれ以下を指定する。

1(アクセスするデータ):CallLog.Calls.CONTENT_URI
2(取得するデータ):null
3(データの制限):null
4(第3引数の追加情報):null
5(データの順番):CallLog.Calls.DEFAULT_SORT_ORDER

第2引数に「null」を指定することで、すべてのデータを取得できる。

package net.kuttya.calllog;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.CallLog;
import android.widget.TextView;

public class CallLogSampleActivity extends Activity {
    TextView textView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // テキストビュー
        textView = (TextView)findViewById(R.id.callLogText);
        textView.setText("");

        ContentResolver contentResolver = getContentResolver();
        Cursor cursor = contentResolver.query(CallLog.Calls.CONTENT_URI, null, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);

        if(cursor.moveToFirst()) {
            do {
                // 通話先
                String name = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
                if(name == null) {
                    name = "登録なし";
                }

                // 電話番号
                String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));

                // 日時
                Date datetime = new Date(cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE)));
                SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd (EEE) HH:mm:ss");
                String datetimeStr = format.format(datetime);

                // 通話種別
                String calltype = cursor.getString(cursor.getColumnIndex(CallLog.Calls.TYPE));
                if(calltype.equals("1")) {
                    calltype = "着信";
                }
                else if(calltype.equals("2")) {
                    calltype = "発信";
                }
                else if(calltype.equals("3")) {
                    calltype = "不在着信";
                }

                // 出力
                textView.append(datetimeStr + ":" + calltype + "\n" + name + "(" + number + ")\n");
                textView.append("----------\n");
            } while(cursor.moveToNext());
        }
    }
}

マニフェストファイルにパーミッションを追加する。


ログ情報が多いため、テキストビューをスクロールさせる。


    
        
    




にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年7月3日火曜日

android:外部電源の接続/切断を検知

android端末に、電源コードやUSBケーブルを接続/切断したことを検知することができる。

  • 接続を検知するためには、ブロードキャストレシーバで「ACTION_POWER_CONNECTED」のインテントアクションを受信する。
  • インテントアクションを受信するためには、マニフェストファイルに同名のインテントフィルタを登録する。
  • 切断は「ACTION_POWER_DISCONNECTED」
  • アクティビティは必ずしも用意しなくてよい。

package net.kuttya.powerconnect;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class PowerConnectReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
  if(intent.getAction().equals("android.intent.action.ACTION_POWER_CONNECTED")) {
   // 外部電源接続のインテントアクションを検知
   Toast.makeText(context, "外部電源接続", Toast.LENGTH_SHORT).show();
  }

  if(intent.getAction().equals("android.intent.action.ACTION_POWER_DISCONNECTED")) {
   // 外部電源切断のインテントアクションを検知
   Toast.makeText(context, "外部電源切断", Toast.LENGTH_SHORT).show();
  }
 }
}

マニフェストファイルにインテントフィルタを登録する。

    
    
        
            
                
                
            
        
    


USBケーブルを接続したときのキャプチャ。






















にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年6月24日日曜日

android:Facebook Android SDK(3:友人一覧を取得)

Facebook Android SDKを使用してGraph APIにアクセスすると、Facebook上の友人一覧を取得することができる。
友人一覧以外にも、投稿内容やアルバムの取得にもGraph APIを使用する。

  • Graph APIへのアクセスにはAsyncFacebookRunnerクラスのrequestメソッドを使用する。
  • 友人情報を取得するためのリクエストは「me/friends」。
  • requestメソッドの応答はJSON形式となる。

認証については、こちらも参照のこと。

package net.kuttya.facebooksample;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.facebook.android.AsyncFacebookRunner;
import com.facebook.android.AsyncFacebookRunner.RequestListener;
import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.FacebookError;

public class FacebookSdkSampleActivity extends Activity {
    private final String API_KEY = "登録したAPP ID"; // Facebookに登録したAppID

 private Facebook facebook;
 private AsyncFacebookRunner runner;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    // ログインボタン押下処理
    public void clickEventLogin(View v) {
        // 認証実施
     if(facebook == null) {
      facebook = new Facebook(API_KEY);
     }
        facebook.authorize(this, new DialogListener() {

            public void onComplete(Bundle values) {
             // 認証成功

          runner = new AsyncFacebookRunner(facebook);

          // 「me/friends」とリクエストすることで、友人情報を取得する
          runner.request("me", new RequestListener(){

     public void onComplete(String response, Object state) {
      // リクエスト成功
      // responseを処理する
      // responseはJSON形式となっている
     }

     public void onIOException(IOException e, Object state) {
      // ネットワークエラー
               Toast.makeText(FacebookSdkSampleActivity.this, "リクエスト失敗\n(ネットワークエラー)", Toast.LENGTH_SHORT).show();
     }

     public void onFileNotFoundException(FileNotFoundException e, Object state) {
      // 存在しないリソースへのアクセスが発生
               Toast.makeText(FacebookSdkSampleActivity.this, "リクエスト失敗\n(存在しないリソースへのアクセスが発生)", Toast.LENGTH_SHORT).show();
     }

     public void onMalformedURLException(MalformedURLException e, Object state) {
      // リクエストが無効
               Toast.makeText(FacebookSdkSampleActivity.this, "リクエスト失敗\n(リクエストが無効)", Toast.LENGTH_SHORT).show();
     }

     public void onFacebookError(FacebookError e, Object state) {
      // Facebook側でのエラー
               Toast.makeText(FacebookSdkSampleActivity.this, "リクエスト失敗\n(Facebook側エラー)", Toast.LENGTH_SHORT).show();
     }
          });
            }

            public void onFacebookError(FacebookError e) {
             // Facebook側エラー
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン失敗\n(Facebook側エラー)", Toast.LENGTH_SHORT).show();
            }

            public void onError(DialogError e) {
             // 端末側エラー
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン失敗\n(端末側エラー)", Toast.LENGTH_SHORT).show();
            }

            public void onCancel() {
             // キャンセル
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン失敗\n(キャンセル)", Toast.LENGTH_SHORT).show();
            }

        });
    }

    // ログアウトボタン押下処理
    public void clickEventLogout(View v) {
     try {
      if(facebook != null) {
    facebook.logout(this);

    facebook = null;

          // ログアウト成功
          Toast.makeText(FacebookSdkSampleActivity.this, "ログアウト成功", Toast.LENGTH_SHORT).show();
      }
  } catch (MalformedURLException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }

    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);

     facebook.authorizeCallback(requestCode, resultCode, data);
    }
}

マニフェストファイルに下記のパーミッションを追加する。


レイアウトとして、main.xmlにログイン用ボタン、ログアウト用ボタンを設置する。 ボタン押下で、クリックイベントを発生させる。

    

にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年6月12日火曜日

android:Facebook Android SDK(2:ログインとログアウト)

Facebook Android SDKを使用して、ログインとログアウトするサンプルを示す。

認証については、こちらも参照のこと。

package net.kuttya.facebooksample;

import java.io.IOException;
import java.net.MalformedURLException;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.facebook.android.AsyncFacebookRunner;
import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.FacebookError;

public class FacebookSdkSampleActivity extends Activity {
    private final String API_KEY = "登録したAppID"; // Facebookに登録したAppID

 private Facebook facebook;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    // ログインボタン押下処理
    public void clickEventLogin(View v) {
        // 認証実施
     if(facebook == null) {
      facebook = new Facebook(API_KEY);
     }
        facebook.authorize(this, new DialogListener() {

            public void onComplete(Bundle values) {
             // 認証成功
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン成功", Toast.LENGTH_SHORT).show();
            }

            public void onFacebookError(FacebookError e) {
             // Facebook側エラー
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン失敗\n(Facebook側エラー)", Toast.LENGTH_SHORT).show();
            }

            public void onError(DialogError e) {
             // 端末側エラー
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン失敗\n(端末側エラー)", Toast.LENGTH_SHORT).show();
            }

            public void onCancel() {
             // キャンセル
             Toast.makeText(FacebookSdkSampleActivity.this, "ログイン失敗\n(キャンセル)", Toast.LENGTH_SHORT).show();
            }

        });
    }

    // ログアウトボタン押下処理
    public void clickEventLogout(View v) {
     try {
      if(facebook != null) {
    facebook.logout(this);

    facebook = null;

          // ログアウト成功
          Toast.makeText(FacebookSdkSampleActivity.this, "ログアウト成功", Toast.LENGTH_SHORT).show();
      }
  } catch (MalformedURLException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }

    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);

     facebook.authorizeCallback(requestCode, resultCode, data);
    }
}

マニフェストファイルに下記のパーミッションを追加する。


レイアウトとして、main.xmlにログイン用ボタン、ログアウト用ボタンを設置する。 ボタン押下で、クリックイベントを発生させる。

    

にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年6月2日土曜日

android:Facebook Android SDK(1:アプリに組み込む)

AndroidアプリからFacebookの機能を使用するための方法として、Facebook Android SDKを使用する方法がある。

手順は以下。
  • Facebook Android SDKの入手
  • Facebook Developersに登録
  • Facebookにアプリ登録
  • アプリ作成

ここでは、Facebookにアプリ登録を済ませていることを前提とする。

プロジェクト作成

新規にプロジェクトを作成した後、Facebook Android SDKをプロジェクトに取り込む。
ダウンロードしたFacebook Android SDK内のfacebook/src、facebook/resを、作成したプロジェクトのフォルダにコピーする。

facebook_android_sdk
└facebook
├res
└src

取り込んだソースファイルのうち、FbDialog.javaにてコンパイルエラーが発生するが、リソースを作成したプロジェクトのパッケージに修正すると解消される。
Drawable crossDrawable = getContext().getResources().getDrawable(R.drawable.close);

下記のように修正する。
Drawable crossDrawable = getContext().getResources().getDrawable(net.kuttya.facebooksample.R.drawable.close);

認証画面呼び出し

作成したプロジェクトのアクティビティにて認証画面を呼び出す。

package net.kuttya.facebooksample;

import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.FacebookError;
import com.facebook.android.Facebook.DialogListener;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class FacebookSdkSampleActivity extends Activity {
    private final String API_KEY = "登録したAppID"; // Facebookに登録したAppID

 Facebook facebook = new Facebook(API_KEY);
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 認証実施
        facebook.authorize(this, new DialogListener() {

            public void onComplete(Bundle values) {
             // ログイン完了
            }

            public void onFacebookError(FacebookError e) {
             // Facebook側エラー
            }

            public void onError(DialogError e) {
             // 端末側エラー
            }

            public void onCancel() {
             // キャンセル
            }

        });
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);

     facebook.authorizeCallback(requestCode, resultCode, data);
    }
}

Facebookへ接続するため、INTERNETのパーミッションを追加する。



    
    

    
        
            
                

                
            
        
    




アプリを起動すると、Facebookのアプリ認証画面を表示する。




にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年1月26日木曜日

android:暗号化に使用するための鍵生成

android端末で、インターネット経由の通信をしたりファイルにパスワードなどを保存しておく場合、通信内容や個人情報の保護のためにデータを暗号化することが必須となる。
実際にはAESなどの方式で暗号化することになるが、暗号化/復号化で使用する鍵はどのように生成すべきか、android端末の視点から考えてみた。

暗号化鍵の生成

android端末に限らず、暗号化鍵の生成方法で候補に挙がるのはだいたい次の3パターン。
  • ソースコード内に定数として定義
  • 乱数により生成
  • 端末内の情報からパスワードを生成

それぞれの脆弱な点は以下。
  • ソースコード内に定数として定義
  • リバースエンジニアリングによりソースコードが読み取られると、簡単にパスワードが判明してしまう。 ソースコードに鍵を定義しているため、他の端末でも同じ鍵を使用する。 androidアプリなどの配布するタイプのアプリケーションでは、リバースエンジニアリングの危険性からこの方法は使用すべきではない。
  • 乱数により生成
  • アプリケーションの実行ごとにパスワードが変更されるため、次にアプリケーションを起動したときには暗号化しているデータを復号化することができない。
  • 端末内の情報からパスワードを生成
  • 使用する端末情報によっては、パーミッションが必要になり、ユーザに不要な警戒心を抱かせる。

セキュリティの面から、端末内の情報を使用すべきだろう。
パーミッションが必要な理由はインストール画面で説明するなどし、警戒を薄れさせることが必要。
にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年1月15日日曜日

android:バーコードの読み取り

androidアプリでバーコードを読み取るために、ZXingライブラリを使用することもできるが、
QRコードスキャナーというアプリを経由してバーコードを読み取ることもできる。
QRコードスキャナーの呼び出しにはインテントを使用する。

QRコードスキャナーの呼び出し

  • QRコードスキャナーをインテントで呼び出す。
  • QRコードスキャナーが端末にインストールされていなければ、AndroidMarketを起動する。

package net.kuttya.barcodescansample;

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

public class BarcodeScanSampleActivity extends Activity {
    // インテントリクエストコード
    private static final int REQUEST_SCAN = 0;

    private static String resultStr;
    private TextView textResult;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 読み取り結果表示領域
        textResult = (TextView)findViewById(R.id.result);
    }

    public void clickEventScan(View v) {
        // バーコードスキャナの呼び出し
        callBarcodeScanner();
    }

    private void callBarcodeScanner() {
        Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");

        try {
            startActivityForResult(intentScan, REQUEST_SCAN);
        }
        catch(ActivityNotFoundException e) {
            // バーコードスキャナが端末にインストールされていなければ、
            // AndroidMarketを起動する。
            Uri uri = Uri.parse("market://details?id=com.google.zxing.client.android");

            Intent intentMarket = new Intent(Intent.ACTION_VIEW,uri);

            try {
                startActivity(intentMarket);
            }
            catch(ActivityNotFoundException ee) {
            }
        }
    }

    @Override
    protected void onActivityResult(int request, int result, Intent intent) {
        switch(request) {
            case REQUEST_SCAN:
                if(result == Activity.RESULT_OK) {
                    resultStr = intent.getStringExtra("SCAN_RESULT");
                    textResult.setText(resultStr);
                }
                break;
            default:
                break;
        }
    }
}

  • 直接関係しないがレイアウトは次の通り

<?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" >

    <Button
        android:id="@+id/scan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="clickEventScan"
        android:text="scan start" />

    <TextView
        android:id="@+id/result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

にほんブログ村 IT技術ブログ Androidアプリ開発へ

android:危険なパーミッションの組み合わせ

androidOSはOSの権限を管理するために、パーミッションという仕組みを用意している。
パーミッションは機能ごとに細かく分けられていて、アプリがなにをするかがわかるようになっている。
たとえば、 端末の電話番号を取得するアプリなら「READ_PHONE_STATE」、インターネットに接続するアプリなら「INTERNET」というパーミッションが必要となる。

パーミッションはアプリがどのような機能をもつかを示しているため、危険なアプリかどうかの判断材料のひとつとなる。
先に示した、電話番号を取得する「READ_PHONE_STATE」と、インターネットに接続する「INTERNET」のふたつのパーミッションをもつアプリの場合、端末の電話番号を使用者にわからないように外部に送信することが可能となる。

危険なパーミッションの組み合わせ

危険なパーミッションの組み合わせには、次のようなものがある。
  • インターネットに接続する「INTERNET」と端末の現在位置を取得する「ACCESS_FINE_LOCATION」
  • インターネットに接続する「INTERNET」と電話帳の内容を読み取る「READ_CONTACTS」
  • インターネットに接続する「INTERNET」とSMSの受信を取得する「READ_SMS」
  • 電話帳の内容を読み取る「READ_CONTACTS」と電話をかけることのできる「CALL_PHONE」
  • 電話帳の内容を読み取る「READ_CONTACTS」とSMSを送信できる「SEND_SMS」

危険なパーミッション組み合わせは他にも多く存在する。
androidOSにどのようなパーミッションがあるのかは、下記のサイトから確認できる。

android developers Manifest permission

アプリを使用する上で注意すべきこと

アプリがどのパーミッションを使用するか(どのような権限を持っているか)知りたい場合は、アプリをインストールした端末で確認することができる。

端末によって多少操作方法が異なるが、だいたい次の操作で確認が可能。
  1. ホーム画面でメニューボタンを押す。
  2. メニューから設定を選ぶ。
  3. 設定のリストから「アプリケーション」を選ぶ。
  4. アプリケーションのリストから「アプリケーションの管理」を選ぶ。
  5. 確認したいアプリを選ぶ。
  6. 「許可」の欄にアクセスする内容が書かれている。

また、アプリのインストール時にもパーミッションについての許可を求められるので、
危険だと判断した場合は、アプリをインストールしないようにすべき。
アプリをインストールする前に、アプリの開発元が信頼できるところなのか、アプリの紹介元は危険がないことを確認しているのかなど、他者がどのように判断したのかを確認する。
それに加え、最終的にはアプリ使用者の自己責任でアプリをインストールするようにしたい。

開発者として注意すべきこと

開発したい、または開発済みのアプリのパーミッションが危険な組み合わせとなる場合は、危険性がないことを表明しなくてはならない。使用者が不安にならないように配慮する必要がある。
androidマーケットのインストール画面で次のことをを記載すれば、多少は使用者の不安を取り除くことができる。
  • アプリで使用するパーミッションの種類を明記
  • アプリが端末情報や個人情報にアクセスする理由
  • 通話、SMS、インターネット接続など、費用が発生する機能を使用する理由
  • 必要以上に端末情報、個人情報を取得していない、不正アクセをしていないことを表明

また、使用者の不安を取り除くことも大切だが、まずは、設計段階でアプリで使用するパーミッションを必要最低限にすることが大前提だろう。

にほんブログ村 IT技術ブログ Androidアプリ開発へ

2012年1月14日土曜日

android:androidで使用できる暗号方式

androidアプリでは様々な暗号方式を使用することができる。

暗号方式の取得

  • java.security.Securityクラスにより取得できる
  • 聞いたことのない暗号方式も取得できる

package net.kuttya.cryptalgorithmprinter;

import java.security.Security;
import java.util.Set;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;


public class CryptAlgorithmPrinterActivity extends Activity {
    private TextView textAlgorithm;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 表示領域
        textAlgorithm = (TextView)findViewById(R.id.text_algorithm);
    }

    public void clickEventPrint(View v) {
        // 使用できる暗号方式を取得

        Set<String> algorithms = Security.getAlgorithms("Cipher");

        textAlgorithm.setText("");
        for(String algorithm:algorithms) {
            textAlgorithm.append(algorithm + "\n");
        }
    }
}

  • 直接関係しないがレイアウトは次の通り

<?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" >

    <Button
        android:id="@+id/print"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
            android:onClick="clickEventPrint"
        android:text="暗号方式表示" />

    <ScrollView
        android:id="@+id/scroll_algorithm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/text_algorithm"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="" />
    </ScrollView>

</LinearLayout>

こんな感じで表示される。
AES、DES、RSAくらいしか聞いたことがない。
まあ、実際に使うのはAESかRSAだと思う。


にほんブログ村 IT技術ブログ Androidアプリ開発へ

2011年12月24日土曜日

android:androidで発信した電話番号を取得する

android端末から発信した電話番号は、ブロードキャストで「android.intent.action.NEW_OUTGOING_CALL」を検知したときに取得できる。

電話発信を検知

  • 電話発信により、「android.intent.action.NEW_OUTGOING_CALL」がブロードキャストされる。
  • BroadcastReceiverクラスのonReceiveメソッドにてブロードキャストを検知する。
  • ブロードキャストされた情報から電話番号を取得する。

package net.kuttya.callreceivesample;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class CallReceiveSample extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {
  // 電話発信を検知
  String actionStr = intent.getAction();

  if(actionStr.equals("android.intent.action.NEW_OUTGOING_CALL")) {
   // 発信した電話番号を取得
   String phoneNumber = intent.getStringExtra("android.intent.extra.PHONE_NUMBER");

   Toast.makeText(context, phoneNumber + "に電話発信中", Toast.LENGTH_LONG).show();
  }
 }
}


  • AndroidManifest.xmlで電話発信へのアクセスを許可しておく。



    

    
        
            
                
            
        
    

    



こんな感じ。

にほんブログ村 IT技術ブログ Androidアプリ開発へ

android:androidからHTTP-POST通信でバイト列を送信する。

android端末からのPOST通信は、HttpURLConnectionクラスを使用することで簡単に実現できる。

クライアント(android)
  •  HttpURLConnectionのインスタンスを生成する。
  • インスタンスを設定する。
  •  BufferedOutputStreamでリクエストを送信する。
  •  BufferedInputStreamでレスポンスを受信する。

package net.kuttya.httppostsample;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class HttpPostSampleActivity extends Activity {
 private final static String SAMPLE_URL = "http://sample.php";

 private Button submitButton;
 private TextView responseText;

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

        // 送信ボタン
        submitButton = (Button)findViewById(R.id.main_submit);

        // レスポンス表示エリア
        responseText = (TextView)findViewById(R.id.main_response);

    }

    /*
     * 送信ボタンクリック
     */
 public void clickEventSubmit(View view) {
  HttpURLConnection http = null;  // HTTP通信
  OutputStream out = null;   // HTTPリクエスト送信用ストリーム
  InputStream in = null;    // HTTPレスポンス取得用ストリーム
  BufferedReader reader = null;  // レスポンスデータ出力用バッファ


  byte[] requestBytes;
  String result = null;

  requestBytes = new byte[5];
  requestBytes[0] = 0x31;
  requestBytes[1] = 0x32;
  requestBytes[2] = 0x33;
  requestBytes[3] = 0x34;
  requestBytes[4] = 0x35;

  try {
   // URL指定
   URL url = new URL(SAMPLE_URL);

   // HttpURLConnectionインスタンス作成
   http = (HttpURLConnection)url.openConnection();

   // POST設定
   http.setRequestMethod("POST");

   // HTTPヘッダの「Content-Type」を「application/octet-stream」に設定
   http.setRequestProperty("Content-Type","application/octet-stream");

   // URL 接続を使用して入出力を行う
   http.setDoInput(true);
   http.setDoOutput(true);

   // キャッシュは使用しない
   http.setUseCaches(false);

   // 接続
   http.connect();

   // データを出力
   out = new BufferedOutputStream(http.getOutputStream());
   out.write(requestBytes);
   out.flush();

   // レスポンスを取得
   in = new BufferedInputStream(http.getInputStream());
   reader = new BufferedReader(new InputStreamReader(in));
   result = reader.readLine();

   // テキストエリアに表示
   responseText.setText(result);


  } catch(Exception e) {
   e.printStackTrace();
  } finally {
   try {
    if(reader != null) {
     reader.close();
    }
    if(in != null) {
     in.close();
    }
    if(out != null) {
     out.close();
    }
    if(http != null) {
     http.disconnect();
    }
   } catch(Exception e) {
   }
  }
 }
}

  •  マニフェストファイルに下記のパーミッションを追加。



サーバ
  • 今回はPHPで作成。
  • HTTPリクエストを取得する。
  • 取得したデータに対する処理を実施。(サンプルは受信データを送り返すだけ)

<?php
// 受信データ格納
$data =  file_get_contents("php://input");

echo "POST DATA RECEIVED. data[".$data."]"

?>

にほんブログ村 IT技術ブログ Androidアプリ開発へ