2011年3月10日木曜日

ListViewをカスタマイズする

ListViewをカスタマイズする

ここが参考になりすぎて、自分で書くことがないのですが、とりあえずメモがわりとして。
実際に動かして表示したコードは、またいつか気が向いたら。

TwitterのTimelineを表示するのはほとんどこれでできちゃいました。
本当は、過去分とかはキャッシュしたりしてsqliteとかに放りこみたいのですが、
スポットでTimeline表示するだけならこれで十分かと。

Twitter Clientとかでよくある、30min agoとか何分前とか。"elapsed time"

これもライブラリがありました。
prettytimeだって。

Convert elapsed timestamps, like, “in 3 minutes!”
elapsed time = 経過時間 というらしい。(恥

ocpsoft-pretty-time-1.0.6.jarを使用しました。

Dateにフォーマットかけて
TextViewにセットするだけです。

PrettyTime p = new PrettyTime();
String prettyTime = p.format(*DateClassのインスタンス*);
TextView minutetext = (TextView)view.findViewById(R.id.minutetext);
minutetext.setText(prettyTime);

簡単簡単。

Twitter4j でプロフィールのイメージを表示

先の投稿、Twitter4jでxAuth認証 で作成した、TwitterWrapperを使用しています。

Activityの中で。

TwitterWrapper twa = new TwitterWrapper();
        // TwitterWrapperに用意してあるmethodで、SharedPreferencesに保存してあるTwitterの
        // oAuth Token と oAuth Token Secretを使って、アカウントをログイン状態にしています。
        twitter = twa.getAuthorizedTwitter(this);
        
        // SharedPreferencesに保存しておいた、設定を取得
        SharedPreferences pref = 
            getSharedPreferences(Values.TWITTER_PREFERENCE_NAME, Activity.MODE_PRIVATE);
        // SharedPreferencesに保存しておいた、ScreenNameを取得
        String screenName = pref.getString(Values.TWITTER_ID_KEY,"NONE");
        
        try {
            // ScreenNameをつかって、Userのデータを取得。
            User user = twitter.showUser(screenName);
            // ScreenNameをつかって、UserのProfile画像のURLを取得。
            URL imageURL = user.getProfileImageURL();
            Bitmap profile = null;
            // これは、適当にViewのLayoutファイルと紐付けしてください。
            ImageView imgv = (ImageView)findViewById(R.id.userIcon);
            try {
                // BitmapFactory.decodeStreamでビットマップを作成。
                profile = BitmapFactory.decodeStream(
                    imageURL.openConnection().getInputStream());
                if ( profile != null ) {
                    // ちゃんと画像が取得できた時だけ、ImageViewにせってい
                    imgv.setImageBitmap(profile);
                }
            } catch (IOException e) {
                // 取れなかったら無視、レイアウトファイルに
                // デフォルト画像のsrcを設定しておくとそれが出ます。
            }
            
        } catch (TwitterException e) {
            e.printStackTrace();
        }

2011年3月9日水曜日

Twitter4jでxAuth認証

実際にActivityに書いて認証を呼び出すコードを。

めんどくさいので、TwitterWrapperというクラスを作りました。
設定値は、Valuesというクラスに入れてあります。(手抜き

ステップは4つ
1)twitter4jのjarファイルをビルドパスに追加。
2)設定用のValuesクラスを用意。
3)TwitterWrapperを用意。
4)Activityにメソッドを記述。

1)twitter4jのjarファイルをビルドパスに追加。
これは適当にどこか見てみてください。
こことか。JavaでTwitterをOAuth認証して使えるTwitter4jとは

Versionは、twitter4j-android-2.2.1-SNAPSHOT.zip を使用しました。
他必要なライブラリは、oauth-signpost で、signpost-commonshttp4-1.2.1.1.jar とsignpost-core-1.2.1.1.jarです。

2)設定用のValuesクラスを用意。
設定用のValuesクラス
public class Values {

 public static final String TWITTER_SCREEN_NAME = "じぶんのアカウント";
 
 public static final String TWITTER_CONSUMER_KEY = "自分のConsumerKey";
 public static final String TWITTER_CONSUMER_SECRET = "自分のConsumerSecret";
 public static final String TWITTER_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token";
 public static final String TWITTER_AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize";
 public static final String TWITTER_CALLBACK_URL = "http://てけとうに自分のサイト/";
 public static final String TWITTER_PREFERENCE_NAME = "おすきに";
 
 public static final String TWITTER_ID_KEY = "screen_name";
 public static final String TWITTER_TOKEN_KEY = "token";
 public static final String TWITTER_TOKEN_SECRET_KEY = "tokenSecret";
 
 public static final String TWITTER_ACCESS_TOKEN_KEY = "accessToken";
 
}

3)TwitterWrapperを用意。


インポート文
import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.Tweet;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationBuilder;
import twitter4j.http.AccessToken;
import twitter4j.http.OAuthAuthorization;
import java.util.List;
import android.app.Activity;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.EditText;

// リソースファイルがわり。
import jp.mediba.android.util.Values;

クラス
public class TwitterWrapper {
    Twitter twitter;
 
    public TwitterWrapper() {
        // do nothing
    }

    public TwitterWrapper(Twitter twitter) {
        this.twitter = twitter;
    }
 
    public Twitter getTwitter() {
        return twitter;
    }

    public void setTwitter(Twitter twitter) {
        this.twitter = twitter;
    }
 
    /**
     * Usage: java twitter4j.examples.search.SearchTweets [query]
     */
    public List<tweet> searchTweet() {
        String str = Values.TWITTER_HASHTAG;
        Twitter twitter = new TwitterFactory().getInstance();
        try {
            Query query = new Query(str);
            query.setRpp(30);
            QueryResult result = twitter.search(query);
            List<tweet> tweets = result.getTweets();
            return tweets;
        } catch (TwitterException te) {
            te.printStackTrace();
            Log.w("TWT", "Failed to search tweets: " + te.getMessage());
        }
        return null;
    }
    
    public void authorization(Activity act) throws TwitterException {
     
        ConfigurationBuilder builder =
                new ConfigurationBuilder();
        builder.setOAuthConsumerKey(Values.TWITTER_CONSUMER_KEY);
        builder.setOAuthConsumerSecret(Values.TWITTER_CONSUMER_SECRET);
        TwitterFactory factory = new TwitterFactory(builder.build());
        String screen_name = 
                ((EditText)act.findViewById(R.id.txtScreenName)).getText().toString();
        String password = 
                ((EditText)act.findViewById(R.id.txtPassword)).getText().toString();
        Twitter twitter = factory.getInstance(screen_name,password);
        AccessToken accessToken = null;
        try {
            accessToken = twitter.getOAuthAccessToken();
            String token = accessToken.getToken();
            String tokenSecret = accessToken.getTokenSecret();
            SharedPreferences pref = 
                act.getSharedPreferences(
                    Values.TWITTER_PREFERENCE_NAME,
                    Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = pref.edit();
            editor.putString(Values.TWITTER_TOKEN_KEY,token);
            editor.putString(Values.TWITTER_TOKEN_SECRET_KEY,tokenSecret);
            editor.putString(Values.TWITTER_ID_KEY,screen_name);
            editor.commit();
        } catch (TwitterException e) {
            e.printStackTrace();
            throw e;
        }
  
    }

    
    public Twitter getAuthorizedTwitter(Activity act) {
        SharedPreferences settings = 
            act.getSharedPreferences(Values.TWITTER_PREFERENCE_NAME, 0);
        String token = settings.getString(Values.TWITTER_TOKEN_KEY,"NONE");
        String tokenSecret = settings.getString(Values.TWITTER_TOKEN_SECRET_KEY,"NONE");
     
        Twitter twitter = new TwitterFactory().getInstance();
        if(twitter.getAuthorization().isEnabled()){
            return twitter;
        } else {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.setOAuthConsumerKey(Values.TWITTER_CONSUMER_KEY);
            builder.setOAuthConsumerSecret(Values.TWITTER_CONSUMER_SECRET);
            builder.setOAuthAccessToken(token);
            builder.setOAuthAccessTokenSecret(tokenSecret);
            Configuration configuration = builder.build();

            TwitterFactory factory = new TwitterFactory();
            try {
                twitter = factory.getInstance(new OAuthAuthorization(configuration));
            } catch (java.lang.StringIndexOutOfBoundsException e) {
                return null;
            }
         
            if(twitter.getAuthorization().isEnabled()){
                return twitter;
            } else {
                return null;
            }
        }
    }
}

4)Activityにメソッドを記述。

Activityに以下のメソッドを書きます。

インポート文
import twitter4j.TwitterException;
import jp.mediba.android.util.TwitterWrapper;

Activityに以下のメソッドを書き、ボタンに結びつけます。

public void pushAuth(View v) {
        TwitterWrapper tw = new TwitterWrapper();
        AlertDialog.Builder abuilder = new AlertDialog.Builder(this);
        try {
         // ActivityそのものをTwitterWrapperに渡しています。
   tw.authorization(this);
   // 設定が完了したら、ダイアログを出す。
   abuilder.setMessage( R.string.dialog_api_twitter_setting_done_title )
          .setCancelable(false)
          .setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int id) {
               dialog.cancel();
               // ダイアログのOKをおしたら、別画面に遷移。
               goTweet();
              }
          });
   
  } catch (TwitterException e) {
   e.printStackTrace();
   // なんかエラーが起きたら、ダイアログを出す。
   abuilder.setMessage( R.string.error_api_twitter_login )
         .setCancelable(false)
         .setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface dialog, int id) {
              dialog.cancel();
             }
         });
  
  }
  AlertDialog alert = abuilder.create();
  alert.show();
    }
    
    // 遷移したいActivityをセットして、画面遷移するロジックは外出ししておく。
    public void goTweet() {
     Intent intent = new Intent(this, ActTweet.class);
     startActivity(intent);
     finish();
    }
    

Twitter xAuth申請で疲れた…。

Androidにて、Twitter連携アプリを作成し、xAuthに対応させてみました。
その際、xAuthの申請で、非常に時間がかかったので、まとめ。

最初、アプリケーション名と、情報だけ書いて、開発中ですとメール送ったら、
これでできない?「http://dev.twitter.com/pages/oauth_single_token
とお返事が帰ってきました。
先に、oAuthのTokenとTokenSecretをとっておいて、アプリに埋め込んじゃえばoAuthで投稿できるよという意味らしいです。



しかしこれでは、アプリでユーザーがログインして、そのアカウントで投稿するってのができません。

xAuthの許諾は真面目に審査してるみたいです。
開発目的での許諾は行わないとのメールももらいました。

ということで、xAuthの許可を得るのに必要な準備事項をまとめておきます。

1)oAuthアプリケーションの登録。
Webでは下記の場所から登録できます。
「設定」>「連携アプリ」>「連携アプリの追加と設定は『こちら』から変更できます。」
2)アプリのログイン画面とTweet画面、TLの画面をキャプチャしてPDFか何かで、説明文をまとめたもの。
Twitterのチケットサーバーは添付ファイルに対応していないようなので、PDFや画像はどこか公開できるサーバーに配置しておきましょう。


この状態で、以下のメールをapi@twitter.comにおくります。
(英文が変なのは、気にしないでください。www )

---
Hello.

I'm a developer of "アプリケーション名".

"アプリケーション名" is a android application, for tweeting some messages.
I want to use this application for a multi users' twitter client.
I do not want to use android.webkit.WebView because of Userbility.
I send this application screen capture and explain the reason of using xAuth.
URL:{スクリーンキャプチャのURLとか。}
Please apply this app to use xAuth.

Application Name: {アプリケーション名}
App Source Code : (now developping)
My account : @{ツイッターのスクリーンネーム}
oAuth Clients : http://twitter.com/oauth_clients/details/{oAuth申請のページのID}

Thank you.
---


これで一発で通るはず。

あとは、Twitterにログインした状態で、
のステータスを確認していましょう。

ちなみに、なんでWebViewを使わないか、ってところの文章は、「日本人アンドロイドに慣れてないの。」としていましたが、これさすがに汎用的に使われると如何かと思うので、文章を「because of Userbility.」に変えてあります。

申請の際に仕様した、PDFも少々変えてありますが、添付しておきます。
(多分ちゃんとしたスクリーンキャプチャのほうが、いいはず。)

2011年2月22日火曜日

au IS04 (REGZA Phone) がADBでデバイス認識されない(MAC OS10.6.6)

タイトルの通りですが、MAC OSXでのAndroid開発環境では、通常は端末をデバッグモードでUSB接続した場合、とくにドライバのインストール作業などを行わずに使用することができました。

(過去に問題なく接続できた端末)
・HTC HT03A
・HTC Desire
・SonyEricsson EXPERIA
・SHARP IS01
・SHARP IS03

今回、TOSHIBA IS04 で実機検証する機会があり、今までと同じようにUSB接続してみたところADB上で認識されません。

WEB上で色々と調べた結果、以下の方法で解決したのでまとめておきます。

1.  .androidディレクトリ(だいたいはユーザディレクトリの直下にあります)
にadb_usb.iniというファイルがあるか確認

2. なければ adb_usb.ini の作成
こちらのサイトを参考にさせていただきました。
http://d.hatena.ne.jp/rmiya/20091109

3. adb_usb.ini にIS04のベンダーID
0x0930
を追加

4. ADBを再起動

端末を接続し、 adb devices コマンド、またはEclipeなどのUI上で端末が表示されれば成功です。


ちなみに、Windows向けにはドライバが配布されているようです。

2011年1月28日金曜日

Android開発 - 画面の縦横を検知して回転するボタンを作る(3)

Android開発 - 画面の縦横を検知して回転するボタンを作る(1)
Android開発 - 画面の縦横を検知して回転するボタンを作る(2)

Javaのコードは2種類
・Main.java (メインのアクティビティクラス)
・AnimationController.java (回転アニメーションの制御)
になります。


AnimationController.java

import android.hardware.SensorEvent;
import android.view.animation.RotateAnimation;
import android.widget.ImageButton;


public class AnimationController {

boolean yoko = false;
float touchY;
long ANIM_ROTATE_SPEED = 200;

public void rotateCube(SensorEvent event, ImageButton[] img) {


if (!yoko && event.values[2] > 60 && event.values[2] < 80) {

for (ImageButton target: img){
RotateAnimation rotate = new RotateAnimation(0, 90, target.getWidth()/2, target.getHeight()/2);
rotate.setDuration(ANIM_ROTATE_SPEED);
rotate.setFillAfter(true);
target.startAnimation(rotate);
}

yoko = true;
}


if (yoko && event.values[2] > -10 && event.values[2] < 10) {

for (ImageButton target: img){
RotateAnimation rotate = new RotateAnimation(90, 0, target.getWidth()/2, target.getHeight()/2);
rotate.setDuration(ANIM_ROTATE_SPEED);
rotate.setFillAfter(true);
target.startAnimation(rotate);
}

yoko = false;
}
}
}



Main.java



import java.util.List;


import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.RotateAnimation;
import android.widget.ImageButton;


public class Main extends Activity implements SensorEventListener, OnClickListener{

AnimationController animationController = new AnimationController(); 

SensorManager mSensorManager = null;
private boolean mRegisteredSensor;
float touchY;
float moveY;
boolean yoko = false;
RotateAnimation rotateAnimation;


ImageButton button01;
ImageButton button02;
ImageButton button03;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mRegisteredSensor = false;


button01 = (ImageButton)findViewById(R.id.button01);
button02 = (ImageButton)findViewById(R.id.button02);
button03 = (ImageButton)findViewById(R.id.button03);
    }


@Override
protected void onResume() {

List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
if (sensors.size() > 0) {
Sensor sensor = sensors.get(0);
mRegisteredSensor = mSensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTEST);
}
super.onResume();
}


@Override
protected void onPause() {

if (mRegisteredSensor) {
mSensorManager.unregisterListener(this);
mRegisteredSensor = false;
}
super.onPause();
}


@Override
protected void onStop() {

mSensorManager.unregisterListener(this);
super.onStop();
}


@Override
public void onClick(View v) {


switch( v.getId() ){
case R.id.button01:
//ボタンタッチ時の処理
break;
case R.id.button02:
//ボタンタッチ時の処理
break;
case R.id.button03:
//ボタンタッチ時の処理
break;
}
}


@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}


@Override
public void onSensorChanged(SensorEvent event) {

if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {


ImageButton[] img = {button01,button02,button03};
animationController.rotateCube(event, img);
}
}
}



Main.java の onSensorChanged → Sensor.TYPE_ORIENTATIONで傾きを検知し、SensorEvent と 対象となるボタンを AnimationController に渡しています。

センサーイベントの event.values[2] でY軸の傾きの値が取れます。
event.values[2] = 60〜80 でボタンを横向きに、
event.values[2] = -10〜10 でボタンを縦向きにアニメーションさせていますが、このあたりの設定は改善の余地がありそうです。



センサーイベントの実装は、以下のサイトを参考にさせていただきました。
http://www.adakoda.com/android/000182.html

2011年1月25日火曜日

Android開発 - 画面の縦横を検知して回転するボタンを作る(2)

Android開発 - 画面の縦横を検知して回転するボタンを作る(1)


まずはManifestファイルから

■AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="jp.mediba.android.RotateCube"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" 
                 android:label="@string/app_name"
                 android:debuggable="true" >
        <activity android:name=".Main"
                  android:label="@string/app_name"
                  android:screenOrientation="portrait">
            <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="4" />
</manifest> 



Activityタグ内で android:screenOrientation="portrait" を指定し、画面自体は縦表示で固定します。


続いてレイアウトファイル

■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"
    >
<ImageButton
android:id="@+id/button01"
android:src="@drawable/icon"
android:layout_width="wrap_content"
 android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@null"
android:layout_marginTop="60dp"
></ImageButton>
<ImageButton
android:id="@+id/button02"
android:src="@drawable/icon"
android:layout_width="wrap_content"
 android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@null"
android:layout_marginTop="60dp"
></ImageButton>
<ImageButton
android:id="@+id/button03"
android:src="@drawable/icon"
android:layout_width="wrap_content"
 android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@null"
android:layout_marginTop="60dp"
></ImageButton>
</LinearLayout>


サンプルではアプリランチャー的なイメージで ImageButton を使いますが、ImageViewやTextView等でも作成可能です。


次回は実際にアニメーションを行うコードを作っていきます。

Android開発 - 画面の縦横を検知して回転するボタンを作る(3)

Android開発 - 画面の縦横を検知して回転するボタンを作る(1)

Androidアプリの開発で、主にUI関係の話をしていると、
「iPhoneぽく」「iPhoneみたいに・・・」といった要望がしばしば出てきます。

直感的でわかりやすく、且つ格好良いインターフェースという点においてはAndroidよりもiOSに分があるのが現状のようです。


今回は、「画面の縦横切替をアニメーションで多少なりとも格好良く見せる」チャレンジをしてみます。
通常、下のような画面の場合


端末を横向きにすると、



このような画面に切り替わります。

これを
・レイアウトそのものは縦画面で固定
・プログラム内で端末の動きを検知しアイコン画像がアニメーションで90度回転
・アニメーション後は以下のような表示に
してみたいと思います。


続きは次回。

Android開発 - 画面の縦横を検知して回転するボタンを作る(2)