Twitter4jを使って連携したり投稿したりのメモです。長いです。。。

連携に関しては下記のサイト様のコードを基本的につかわしていただいてます。
http://shogo82148.github.com/blog/2012/11/24/no-more-webview/
いわゆるIntent Filterを利用した感じです。


んじゃ始めます。

①まずは連携するためにTwitterにアプリを登録しましょう。

#Twitter Developers
https://dev.twitter.com/

右上にあるSign inからログインしてください。
特に理由がなければ、普段使っているIDでログインしていいと思います。
※TwitterIDを持っていない場合は別途、取得してください。



ログインをしたら、右上に自分のアイコンがでているのでそこから
My applicationsを選択してください。



Create a new applicationのボタンを押して
アプリケーションを登録しましょう。

Name: アプリ名を登録しましょう。今回はテストなので適当に。※すでに使われている名前は使えないようです。
Description: アプリの説明です。今回はテストですのでここも適当に。
Website:アプリのサイトとか置くといいです。まぁ今回は適当に。
Callback URL:今回は使いません。まぁ念のためWebsiteと同じものを貼っておいてもいいです。

あとは普通にチェックつけたりして作成してください。



アプリが登録されるとアプリの詳細画面にそのままいくと思います。
そしたら「Details」タブのOAuth settingsにある
 ・Consumer key
 ・Consumer secret
この二つを使いますのでメモ帳などに書いておいてください。
$うつ病miwawaの日記帳


次に「Setting」タブにあるApplication Typeを
 ・Read and Write
に変更して更新してください。
$うつ病miwawaの日記帳
初期値だとRead onlyになっているため投稿ができません。

以上でTwitterへのアプリ登録は終了です。


②次にTwitter4jをDLしましょう。

#Twitter4j
http://twitter4j.org/ja/index.html

現時点での最新バージョンが
twitter4j-3.0.3になっていますのでそれをDLしましょう。
※android版は廃止になったようです。

DLをして解凍したらなんかいっぱいあると思いますが
libフォルダの中にある
  ・twitter4j-core-3.0.3.jar
  ・twitter4j-async-3.0.3.jar
の二つだけを使います。


③さてアプリを作りましょう。
今回は下記の形でサンプルを作ってます。
※apiレベル4(Android1.6)で作ってます。
-----------------------------------------------
プロジェクト名 HelloTwitter    *適当でいいっす
Javaファイル  MainActivity.java *アクティビティです
        SaveImage.java   *画像保存処理をいれてます詳しくはこちらへ
レイアウト   main.xml      *まぁこれも初期名称的な感じで
        send_dialog.xml  *Twitter投稿する用のダイアログレイアウトです
-----------------------------------------------

1.まず②でDLして使うといった二つのtwitter4j.jarをlibsに入れて
パスを通しましょう。
下記サイト様の「JARファイルを読み込んで利用する」部分が参考になります。
http://techbooster.jpn.org/andriod/environment/2768/



2.次にAndroidManifestに加筆します。

まずactivityに

android:launchMode="singleTask"

を追加します。
その下のintent-filterにも

 <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<!-- schemeは他のアプリとかぶらないようユニーク名にしましょう。 -->
<data android:scheme="SampleTwitter" />
</intent-filter>

を追加します。

そしてパーミッションに

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

の二つを追加します。
INTERNETは、Twitterと連携したり投稿したりするのに通信が必要なのでつけます。
WRITE_EXTERNAL_STORAGEは、今回アプリ内スクリーンショットを保存して投稿するので保存先のSDカードを利用するのにつけます
なので別に画像使わないよって場合はWRITE_EXTERNAL_STORAGEはいらないです。

上記部分を適応させるとこんな感じになります。
------------------------------------------------------------------------
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<!-- schemeは他のアプリとかぶらないようユニーク名にしましょう。 -->
<data android:scheme="SampleTwitter" />
</intent-filter>

</activity>
</application>

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

------------------------------------------------------------------------

ちなみにschemeは他のアプリとかぶらないようユニーク名にといってますが
端末に同じschemeで作られてるアプリがあるとCallback時に下記のようにどちらのアプリを開けるか聞いてきます。これは残念な感じです。
$うつ病miwawaの日記帳



3.次にレイアウトファイルを用意していきましょう。

#main.xml *単純にログインボタンと投稿ボタンだけです

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button android:text="ログイン"
android:id="@+id/loginbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<Button android:text="投稿"
android:id="@+id/sendbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>


#send_dialog.xml *本文を書くEditTextと画像投稿するかどうかのCheckBoxをつけてます

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText
android:id="@+id/sendtext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

<CheckBox
android:id="@+id/imgcheck"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="画像も投稿する /SDがある場合のみ"
/>
</LinearLayout>


4.最後はJava側です。
説明はコメント部分を参照してください。

#MainActivity.java
※赤字部分を①でメモ帳に保存などしておいた・Consumer key・Consumer secretに書き換えてください。

=======================================================================
import java.io.File;
import twitter4j.AsyncTwitter;
import twitter4j.AsyncTwitterFactory;
import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import twitter4j.TwitterAdapter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.TwitterListener;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import twitter4j.conf.ConfigurationBuilder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

//Manifestに記述したschemeを記述
public final static String CALLBACK = "SampleTwitter://callback/";
//Twitter Developersに登録したアプリのConsumer key,Consumer secretをセット
public final static String CONSUMER_KEY = "Consumer keyをいれる";
public final static String CONSUMER_SECRET = "Consumer secretをいれる";

private RequestToken mRequestToken;
final AsyncTwitterFactory factory = new AsyncTwitterFactory();
final AsyncTwitter twitter = factory.getInstance();


//とりあえずエラーがあったらなんか入れとくよう
int err = 0;
//とりあえずスレッドからトースト表示とかしたいからおいとく
Handler mHandler = new Handler();


//ログイン処理がすんでるかどうか
private boolean availableStatus;
private Button btn;

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

//立ち上げ時に連携処理がすんでるか確認
SharedPreferences pref = getSharedPreferences("Twitter_setting",MODE_PRIVATE);
availableStatus = pref.getBoolean("status", false);

//もしも連携してなかったら連携処理させるか?
if(!availableStatus){
//連携させるならコメントアウトはずす
//TwitterLogin();
}


btn = (Button)findViewById(R.id.loginbutton);
btn.setOnClickListener(btnListener);
btn = (Button)findViewById(R.id.sendbutton);
btn.setOnClickListener(btnListener);
}

//ボタンの動作****************************************************************
private OnClickListener btnListener = new OnClickListener() {
public void onClick(View v) {
switch(v.getId()){
//ログインボタン
case R.id.loginbutton:
TwitterLogin();
break;
//投稿ボタン
case R.id.sendbutton:
//もしも連携してなかったら連携処理にいく
if(!availableStatus){
TwitterLogin();
}else{
send_twitter();
}
break;
default:
break;
}
}
};


//以下Twitter連携処理*****************************************************************
private void TwitterLogin(){
twitter.addListener(listener);
twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

// 認証開始
// Request Token を取得する
twitter.getOAuthRequestTokenAsync(CALLBACK);
}

private final TwitterListener listener = new TwitterAdapter() {
@Override
public void gotOAuthRequestToken(RequestToken token) {
// ブラウザを開く
mRequestToken = token;
final Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse(mRequestToken.getAuthorizationURL()));
startActivity(intent);
}

@Override
public void gotOAuthAccessToken(AccessToken token) {
// ここで永続化するために設定を保存していく
AccessToken accessToken = token;

SharedPreferences pref=getSharedPreferences("Twitter_setting",MODE_PRIVATE);

SharedPreferences.Editor editor=pref.edit();
// トークンとシークレットトークンを保存して連携してるかどうかのステイタスをtrueにする
editor.putString("oauth_token",accessToken.getToken());
editor.putString("oauth_token_secret",accessToken.getTokenSecret());
editor.putBoolean("status",true);

editor.commit();

//ログイン情報をtrueにしとく
availableStatus = pref.getBoolean("status", false);
}
};

@Override
public void onNewIntent(Intent intent) {
// callback してきた
final Uri uri = intent.getData();
if(uri == null) return ;
final String verifier = uri.getQueryParameter("oauth_verifier");
twitter.getOAuthAccessTokenAsync(mRequestToken, verifier);
}

protected void onTwitterAuthSet() {
}
//******************************************************************************



//つぶやき処理(画像あり/今回はアプリのスクリーンショット固定)*********************************************************
public void send_twitter(){

//今回はカスタムダイアログで入力させたりするので用意 *カスタムダイアログはEditTextのみのsend_dialog1.xmlを使用
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
View view = inflater.inflate(R.layout.send_dialog, null);

//今回はEditText内にデフォルトで文字を入れておく
final EditText editText = (EditText)view.findViewById(R.id.sendtext);
editText.setText("TEST投稿だよ☆画像もつけるよ♪");
final CheckBox checkbox = (CheckBox)view.findViewById(R.id.imgcheck);

//ダイアログを作成して表示
new AlertDialog.Builder(MainActivity.this)
.setTitle("twitterにつぶやく") //タイトルに表示する文章
.setIcon(R.drawable.ic_launcher) //タイトルに表示するアイコン
.setView(view) //カスタムダイアログを適応

//ポジティブボタンの動作
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {

//ここに処理中のダイアログを表示させとくといいかも

// スレッド立てて処理する
(new Thread(new Runnable() {
@Override
public void run() {

//なんとなくEditTextに入力した文字をStringに格納
String twStr = editText.getText().toString();

// 連携時に記録した設定ファイルを読み込む。
SharedPreferences pref = getSharedPreferences("Twitter_setting",MODE_PRIVATE);

// 設定ファイルからoauth_tokenとoauth_token_secretを取得してTwitter投稿の準備。
String oauthToken = pref.getString("oauth_token", "");
String oauthTokenSecret = pref.getString("oauth_token_secret", "");

ConfigurationBuilder confbuilder = new ConfigurationBuilder();
confbuilder.setOAuthAccessToken(oauthToken)
.setOAuthAccessTokenSecret(oauthTokenSecret)
.setOAuthConsumerKey(CONSUMER_KEY)
.setOAuthConsumerSecret(CONSUMER_SECRET);

Twitter twitter = new TwitterFactory(confbuilder.build()).getInstance();

//つぶやき開始
if(checkbox.isChecked() ==true && SaveImage.sdcardWriteReady()){
//画像投稿チェックがついてかつSDカードもついてれば文章と画像を送る
try {
//つぶやきする文章をStatusUpdateにセット
final StatusUpdate status = new StatusUpdate(twStr);
//アプリ全体のスクリーンショットを保存する
//使っているsaveBitmapPngについては http://ameblo.jp/miwawa08/entry-11431952775.html を参照
String filepass = SaveImage.saveBitmapPng(MainActivity.this, findViewById(android.R.id.content), 100, false);

//画像を保存したあと帰ってきたファイルのパスをStatusUpdateの.mediaにセットし投稿する画像を決定
status.media(new File(filepass));

//updateStatusでStatusUpdateにセットした内容をつぶやき
twitter.updateStatus(status);

err = 0;
} catch (TwitterException e) {
e.printStackTrace();
if(e.isCausedByNetworkIssue()){
//ネットワークエラーなので1入れる
err = 1;
}
}catch(Exception e){
//画像保存エラーなので2入れる
e.printStackTrace();
err = 2;
}
}else{
//画像投稿チェックがついていなければ文章だけを送る
try {
//つぶやきする文章をStatusUpdateにセット
final StatusUpdate status = new StatusUpdate(twStr);
//updateStatusでStatusUpdateにセットした内容をつぶやき
twitter.updateStatus(status);
err = 0;
} catch (TwitterException e) {
e.printStackTrace();
if(e.isCausedByNetworkIssue()){
//ネットワークエラーなので1入れる
err = 1;
}
}
}


mHandler.post(new Runnable() {
@Override
public void run() {
//エラーなしなら0、1ならネットワークエラー,2なら画像エラーをトースト
if(err == 0){
Toast.makeText(MainActivity.this, "twitterへ投稿しました。", Toast.LENGTH_LONG).show();
}else if(err == 1){
Toast.makeText(MainActivity.this, "ネットーワークの問題です", Toast.LENGTH_LONG).show();
}else if(err == 2){
Toast.makeText(MainActivity.this, "画像の保存ができませんでした", Toast.LENGTH_LONG).show();
}
}
});
}
})).start();
}
})
//ネガティブボタンの動作
.setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//なんか処理するなら書く
}
})
.show();
}

}
=======================================================================

#SaveImage.java
※前に書いたhttp://ameblo.jp/miwawa08/entry-11431952775.htmlと内容は一緒。
画像保存いらないならつかわないっす。

======================================================================
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.view.View;

public class SaveImage {

//SDカード存在確認
public static boolean sdcardWriteReady(){
//SDカードがささってるか確認
String state = Environment.getExternalStorageState();
return (Environment.MEDIA_MOUNTED.equals(state));
}

//画像保存処理
//第一引数はContextを
//第2引数には保存したいViewを
//第3引数には画質を100までの数値で
//第4引数はtrueならJPEG,falseならPNGで
public static String saveBitmapPng(Context context,View view,int quality,boolean formattype) throws IOException {


//ディレクトリがなかったら作成 *今回はTestというディレクトリ
final String SAVE_DIR = "/Test/";
File file = new File(Environment.getExternalStorageDirectory().getPath() + SAVE_DIR);
try{
if(!file.exists()){
file.mkdir();
}
}catch(SecurityException e){
e.printStackTrace();
throw e;
}


//もってきたViewをBitmapに
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);

//ファイル名を作ります
Date mDate = new Date();
SimpleDateFormat fileNameDate = new SimpleDateFormat("yyyyMMdd_HHmmss");
String fileName;
//trueならJPEG,falseならPNG
if(formattype){
fileName = fileNameDate.format(mDate) + ".jpeg";
}else{
fileName = fileNameDate.format(mDate) + ".png";
}
String AttachName = file.getAbsolutePath() + "/" + fileName;

//保存作業をします
try {
FileOutputStream out = new FileOutputStream(AttachName);
//trueならJPEG,falseならPNG
if(formattype){
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
}else{
bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
}
out.flush();
out.close();
} catch(IOException e) {
e.printStackTrace();
throw e;
}

// 画像を登録しましょう
ContentValues values = new ContentValues();
ContentResolver contentResolver = context.getContentResolver();
if(formattype){
values.put(Images.Media.MIME_TYPE, "image/jpeg");
}else{
values.put(Images.Media.MIME_TYPE, "image/png");
}
values.put(Images.Media.TITLE, fileName);
values.put("_data", AttachName);
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

return AttachName;
}

}
========================================================================


以上です。
問題がなければ実行してみましょう。


下記のように投稿できれば成功です。

画像なし
$うつ病miwawaの日記帳

画像あり
$うつ病miwawaの日記帳


お疲れ様でした。
今回もコード内のタブが削除されました、見にくいですね。。。

最後にプロジェクトおいときます。
>ここ