2013年11月30日土曜日

[Androidアニメーション] 5. Drawable Animation

AndroidのAPIににおける2Dアニメーションの種類は主に3種類あります。

Property Animation

Android3.0(API level 11)からサポートされ、どんなオブジェクトにもアニメーションを行うことができる
View Animation
Viewsに回転、移動、拡大縮小、透明度変換などのアニメーションが行える
Drawable Animation
パラパラ漫画のようにフレームを連続再生することができる
ここでは、Developer AndroidのDrawable Animationの中身をかいつまんで、Drawable Animationについて紹介します。

Drawable Animationとは

Drawable Animationは、Drawable リソースを使って、連続的に表示をすることによってアニメーションを作ることが出来ます。フィルムのような役割をし、順番に沿って、連続した異なった画像を表示して行く従来のアニメーションです。AnimationDrawableクラスは 
Drawable Animationのためのクラスになります。

プログラムの中で、アニメーションのフレームを定義し、AnimationDrawableクラスのAPIを使って、アニメーションを構成するフレームをリスト化したXMLファイルによって簡単に行うことが出来ます。このXMLファイルはAndroidプロジェクトのres/drawable/に置きます。この場合、アニメーションの各フレームに順番と継続時間を指定します。

XMLファイルには、ルートノードとして、<animation-list>要素と連続したフレーム<item>ノードを指定します。XMLファイルの例は以下の通りです。

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>
このアニメーションは、3フレームのみです。listのandroid:oneshotの属性をtrueにセットすることによって、1回で停止し、最後にラストのフレームを表示します。falseをセットするとアニメーションは繰り返します。プロジェクトのres/drawable/ディレクトリに保存されたrocket_thrust.xmlを、Viewに背景画像として指定することが出来ます。ここでのActivityの例は、ImageViewにアニメーションを加え、スクリーンにタッチした時に、アニメーションが始まるプログラムです。
AnimationDrawable rocketAnimation;
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
  rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
  rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}
public boolean onTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    rocketAnimation.start();
    return true;
  }
  return super.onTouchEvent(event);
}
AnimationDrawableで呼ばれるstart()は、onCreate()の中では呼ぶことができません。AnimationDrawableがウィンドウ上にまだ配置されていないからです。もし、アニメーションをすぐに始めたい場合、interactionを要求することなしに、Windowにフォーカスされた時に呼ばれるonWindowFocusChanged()から呼ぶことが出来ます。

XMLを使用しないサンプルプログラム

XMLファイルを使用しないサンプルプログラムは以下の通りです。Activityの中のみでアニメーションを指定することが出来ます。ここでは3フレームを使った250msごとにフレームが変化するアニメーションを説明しています。

public class DrawableAnimation extends Activity{
 
 AnimationDrawable mAnimationDrawable;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
     setContentView(R.layout.drawable_animation);
     //Drawable Animation: Changing Images ----------------------------------------------
     //Create AnimationDrawable
     mAnimationDrawable = new AnimationDrawable();
     //Set frames
     Drawable[] mFrame = new Drawable[3];
     mFrame[0] = getResources().getDrawable( R.drawable.img1);
     mFrame[1] = getResources().getDrawable( R.drawable.img2);
     mFrame[2] = getResources().getDrawable( R.drawable.img3);
    
     //Add frames to AnimationDrawable
     for(int i = 0; i < mFrame.length; i++){
      mAnimationDrawable.addFrame( mFrame[i],  250);
     }
     ImageView imageView = (ImageView) findViewById(R.id.imageView1);
     //Set the animation to background drawable
     imageView.setBackground(mAnimationDrawable);
     //Animation starts
     imageView.post(new Runnable(){
         public void run(){
          mAnimationDrawable.setOneShot(false);
          mAnimationDrawable.start();
        
         }
     });          
 }

まず最初に11行目でAnimationDrawableのコンストラクタをセットしています。
13行目では、フレームにDrawableリソースをセットします。各画像は、res/drawableの中に置きます。ここでは3フレームのアニメーションを行っています。
20行目では、各フレームをAnimationDrawableにセットしています。ここでのパラメータは以下の通りです。
addFrame(Drawable frame, int duration)
  • Drawable frame: フレームとしてのDrawableリソース(画像)
  • int duration: このフレームの継続時間

24行目では、ImageViewの背景画像として、アニメーションをセットしています。
onCreate()の中では、まだリソースがセットされていないので、直接onCreate()の中でアニメーションを始めることはできません。そこで、26行目のようにImageView.postを使用します。これは、ImageViewに背景画像がセットされたら呼ばれるので、ここで、AnimationDrawable.start()を指定します。

2013年11月27日水曜日

[Androidアニメーション] 4. View Animation

AndroidのAPIににおける2Dアニメーションの種類は主に3種類あります。

Property Animation

Android3.0(API level 11)からサポートされ、どんなオブジェクトにもアニメーションを行うことができる
View Animation
Viewsに回転、移動、拡大縮小、透明度変換などのアニメーションが行える
Drawable Animation
パラパラ漫画のようにフレームを連続再生することができる

ここでは、Developer AndroidのView Animationの中身をかいつまんで、View Animationについて紹介します。

View Animationとは 

View Animationでは、View上にTween Animationを表示することが出来ます。Tween Animationはスタートポイント、エンドポイント、サイズ、回転や共通の縦横比などのアニメーション情報を計算します。

Tween AnimationはViewオブジェクトのコンテンツ上で、位置、サイズ、回転や透明度などの単純な変換を行うことが出来ます。例えば、TextViewオブジェクトにおいて、テキストの移動、回転、拡大縮小などができます。もし、TextViewに背景画像が設定してあれば、背景画像はテキストと一緒に変換されます。

アニメーションの手順はXMLかコードで書くことができます。ここではコードでのアニメーションの実行の仕方を紹介します。

View Animationはandroid.view.animation パッケージの中にあるクラスを使うことが出来ます。Viewに使えるアニメーションは4つあります。
  • Scale Animation: サイズの変更
  • Translate Animation: Viewの移動
  • Alpha Animation:透明度の設定
  • Rotate Animation:与えられた角度だけ回転
各アニメーションは、android.view.animationsの中にそれぞれクラスを持っています。すべてのアニメーションクラスは、抽象Animationクラスを拡張したものです。図1はこれらのクラスの階層を示しています。
図1ではAnimationSetというクラスがありますが、このクラスは、アニメーションのグループをつくることができ、同時にいくつかのアニメーションを行ったり、連続してアニメーションを行ったりする時などに使うことができます。
図1: View Animationのクラス階層

サンプルプログラム

サンプルプログラムでは、各アニメーションクラスの簡単な例をViewAnimationクラスとして書いています。それぞれのアニメーションにおいて、繰り返しモードsetRepeatMode()の設定で、逆戻りAnimation.REVERSEを指定しています。


public class ViewAnimation extends Activity {

 ImageView setImageView;
 AnimationSet animationSet;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.view_animation);
     
        //Scale Animation Sample---------------------------------------------------
  ImageView scaleImageView = (ImageView) findViewById(R.id.imageView1);
  ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.5f, 1.0f, 0.5f, 0, 0);
  scaleAnimation.setStartOffset(1000);
  scaleAnimation.setDuration(2000);
  scaleAnimation.setRepeatCount(Animation.INFINITE);
  scaleAnimation.setRepeatMode(Animation.REVERSE);
  scaleImageView.startAnimation(scaleAnimation);
  
  //Translate Animation Sample----------------------------------------------
  ImageView transImageView = (ImageView) findViewById(R.id.imageView2);
  TranslateAnimation transAnimation = new TranslateAnimation(0, 50, 0, 20);
  transAnimation.setDuration(3000);
  transAnimation.setRepeatCount(Animation.INFINITE);
  transAnimation.setRepeatMode(Animation.REVERSE);
  transImageView.startAnimation(transAnimation);
  
  //Alpha Animation Sample-------------------------------------------------
  ImageView alphaImageView = (ImageView) findViewById(R.id.imageView3);
  AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
  alphaAnimation.setDuration(3000);
  alphaAnimation.setRepeatCount(Animation.INFINITE);
  alphaAnimation.setRepeatMode(Animation.REVERSE);
  alphaImageView.startAnimation(alphaAnimation);
  
  //Set Animation Sample---------------------------------------------------
  setImageView = (ImageView) findViewById(R.id.imageView5);
  animationSet = new AnimationSet(false);
  //Animation1
  ScaleAnimation scaleAnimation1 = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f);
  scaleAnimation1.setDuration(3000);
  //Animation2
  AlphaAnimation alphaAnimation1 = new AlphaAnimation(0.0f, 1.0f);
  alphaAnimation1.setDuration(3000);
  
  animationSet.addAnimation(scaleAnimation1);
  animationSet.addAnimation(alphaAnimation1);
  setImageView.startAnimation(animationSet);
  
  //AnimationListener
  animationSet.setAnimationListener(new Animation.AnimationListener() {  
        
        public void onAnimationStart(Animation animation) { 
        
        }  
        
        public void onAnimationRepeat(Animation animation) {
      
        }  
        
        public void onAnimationEnd(Animation animation) {  
         setImageView.startAnimation(animationSet);
        }  
      }); 
 
 }
 
 @Override
  public void onWindowFocusChanged(boolean hasFocus) {
   super.onWindowFocusChanged(hasFocus);
   //Rotate Animation Sample ---------------------------------------------------
   ImageView rotateImageView = (ImageView) findViewById(R.id.imageView4);
  int centerX = (int)rotateImageView.getWidth()/2;
  int centerY = (int)rotateImageView.getHeight()/2;
  RotateAnimation rotateAnimation = new RotateAnimation(0, 20, centerX,centerY);
  rotateAnimation.setDuration(3000);
  rotateAnimation.setRepeatCount(Animation.INFINITE);
  rotateAnimation.setRepeatMode(Animation.REVERSE);
  rotateImageView.startAnimation(rotateAnimation);
  }
}

アニメーションの設定

アニメーションの設定は、どのクラスでも同じような設定を行うことが出来ます。例としてScaleAnimationを上げて説明します。
ImageView scaleImageView = (ImageView) findViewById(R.id.imageView1);
ScaleAnimation scaleAnimation = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, 0, 0);
scaleAnimation.setStartOffset(1000);
scaleAnimation.setDuration(3000);
scaleAnimation.setRepeatCount(Animation.INFINITE);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleImageView.startAnimation(scaleAnimation);

13行目でアニメーションのコンストラクタを指定します。14行目の遅延時間setStartOffset(ms)、15行目のアニメーションの継続時間setDuration(ms)、16行目の繰り返し情報setRepeatCount()、17行目の繰り返しモードsetRepeatMode()などを指定して、最後に18行目のようにアニメーションを行いたいViewのstartAnimation()にアニメーションをセットすることで、アニメーションが始まります。
setRepeatCountにAnimation.INFINITEを指定すると無限にアニメーションを繰り返します。setRepeatMode()では、単純な繰り返しAnimation.RESTARTか逆戻りAnimation.REVERSEを設定することが出来ます。


Scale Animation

拡大縮小を行うには、ScaleAnimationを使用します。13行目のScaleAnimationのコンストラクタのパラメータは以下の通りです。サンプルプログラムでは、0.5倍から1倍に拡大しています。
ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY);
  • float fromX: アニメーション開始のXスケール
  • float toX: アニメーション終了のXスケール
  • float fromY: アニメーション開始のYスケール
  • float toY: アニメーション終了のYスケール
  • float pivotX 原点となるX座標位置
  • float pivotY 原点となるY座標位置


Translate Animation

移動を行うには、TranslateAnimationを使用します。22行目のTranslateAnimationのコンストラクタのパラメータは以下の通りです。サンプルプログラムでは、元の位置(0, 0)からX座標に50ピクセル、Y座標に20ピクセル移動するアニメーションを指定しています。
TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta);
  • float fromXDelta: アニメーション開始時のView内のX座標
  • float toXDelta: アニメーション終了時のView内のX座標
  • float fromYDelta: アニメーション開始時のView内のY座標
  • float toYDelta: アニメーション終了時のView内のY座標


Alpha Animation

透明度変化には、AlphaAnimationを使用します。30行目のAlphaAnimationのコンストラクタのパラメータは以下の通りです。サンプルプログラムでは、不透明から透明へ変化しています。ここでのパラメータの値は、0.0〜1.0を指定します。0.0は完全に透明、1.0は完全に不透明を示します。
AlphaAnimation (float fromAlpha, float toAlpha);
  • float fromAlpha: アニメーション開始時の透明度を指定
  • float toAlpha: アニメーション終了時の透明度を指定


Rotate Animation

回転には、RotateAnimationを使用します。75行目のRorateAnimationのコンストラクタのパラメータは以下の通りです。サンプルプログラムでは、20度の回転を指定しています。回転の中心座標をViewの中心に指定しています。
RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY);
  • float fromDegrees: アニメーション開始時の回転角度
  • float toDegrees: アニメーション終了時の回転角度
  • float pivotX:回転の中心X座標
  • float pivotY: 回転の中心Y座標
サンプルプログラムでは、RotateAnimationをOnWindowFocusChangedの中で指定しています。これは、OnCreate内では早過ぎてViewのgetWidth(), getHeight()の値を取ることができず0が帰ってきてしまうからです。OnWindowFocusChanged内で呼ぶことで、値を得ることが出来ます。


Animation Set

複数のアニメーションを同時に行う場合には、AnimationSetを使用します。サンプルプログラムでは、scaleAnimation1とAlphaAnimation1のアニメーションをセットしています。AnimationSetでは、setRepeatCountが効きません。繰り返しを行いたい場合は、AnimationListenerを使用して、アニメーションの終了時にアニメーションの開始を指定することができます。
AnimationSet(boolean shareInterpolator);
  • boolean shareInterpolator: Interpolatorを共有するかどうかを指定


AnimationListener

51行目のAnimationListenerでは、アニメーションの開始、リピート、終了を監視することができます。ただし、AnimationSetではonAnimationRepeatは動作しません。

2013年11月16日土曜日

[Androidアニメーション] 3. Property Animation のプログラム

Android APIには2DAnimationが3種類用意されています。Property Animation, View Animation,Drawable Animationです。これらの違いの説明は1. Android Animationの種類紹介しています。
ここでは主にObject Animatorを使ったProperty Animationのサンプルプログラムを紹介します。Property Aniamtionの詳細な説明は2. Property Animationの説明で紹介しています。

  1. Android Animationの種類
  2. Property Animationの説明
  3. Property Animationのプログラム
  4. View Animation の説明とプログラム
  5. Drawable Animationの説明とプログラム


Property Animationのサンプルプログラム

サンプルではオブジェクトをButton 1とButton2とし、プロパティをButton 1ではテキストカラー、Button 2ではx方向の移動距離とするアニメーションを行っています。



public class SamplePropertyAnimation extends Activity {
 
 private static final int RED = 0xffFF8080;
 private static final int BLUE = 0xff8080FF;
 Button button1;
 Button button2;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.propertyanimation_main);
  
  //Property Animation: Changing TextColor
  button1 = (Button) findViewById(R.id.btn1);
  ObjectAnimator colorAnimator = ObjectAnimator.ofInt(button1,"textColor", RED, BLUE);
  colorAnimator.setDuration(3000);
  colorAnimator.setEvaluator(new ArgbEvaluator());
  colorAnimator.setRepeatCount(ValueAnimator.INFINITE);
  colorAnimator.setRepeatMode(ValueAnimator.REVERSE);
  colorAnimator.start();
     
  //Property Animation: Translation
  button2 = (Button) findViewById(R.id.btn2);
  ObjectAnimator transAnimator = ObjectAnimator.ofFloat(button2, "x", 0f, 100f);
  transAnimator.setDuration(5000);
  transAnimator.setRepeatCount(2);
  transAnimator.setInterpolator(new BounceInterpolator());
  transAnimator.addListener(new AnimatorListenerAdapter(){
   @Override
   public void onAnimationEnd(Animator animation){
    //Write processing after animating
    button2.setVisibility(View.GONE);
   } 
  });
  transAnimator.start();
  
 }

}
16行目でObjectAnimatorにアニメーションしたいオブジェクトとプロパティを設定します。
ObjectAnimator colorAnimator = ObjectAnimator.ofInt(button1,"textColor", RED, BLUE);
1.ターゲットとなるオブジェクト:ここではButtonを設定。
2.プロパティの名前(String):ここでは"textColor"を設定。
3.開始のプロパティの値
4.終了時のプロパティの値
17行目でアニメーションの継続時間をmsで設定します。デフォルトでは300msが設定されています。 

18行目でEvaluatorを設定します。Evaluatorsは与えられたプロパティ値をどのように計算するか指定します。ここでは色の変化をアニメーションするので、ArgbEvaluator()を設定しています。Evaluatorの種類は以下の通りです。
  • IntEvaluator: int プロパティの値を計算。
  • FloatEvaluator:  floatプロパティの値を計算。
  • ArgbEvaluator:  16進数で表されるカラープロパティの値を計算。
  • TypeEvaluator: カスタマイズのエバリュエータを作ることが出来るインターフェース。もし、int, float, colorではないオブジェクトプロパティにアニメーションをしたい場合、オブジェクトプロパティの値をどのように計算をするか指定することができます。

21行目でアニメーションをスタートします。

28行目ではアニメーションの速度を示すInterpolatorを設定しています。Interpolatorはアニメーションで指定された値がタイム機能としてどのように計算されるか定義することができます。例えば、全体のアニメーションを通して、連続的に起こるアニメーションを指定するか、またはアニメーションの始まりと終わりで加速するようなの不連続的なアニメーションを指定するかなど選択できます。android.view.animationに含まれるInterpolatorは以下の種類があります。
  • AccelerateDecelerateinterpolator: 最初最後がゆっくりになり、真ん中で加速する。 
  • AccelerateInterpolator: ゆっくりで始まり、徐々に加速して行く。 
  • AnticipateInterpolator: 開始時に逆方向にためる。 
  • AnticipateOvershootInterpolator:  開始時に逆方向にため、終了時にはみ出す。 
  • BounceInterpolator: 終了時にバウンドする。 
  • AnticipateOvershootInterpolator: 開始時に逆方向にため、終了時にはみ出す。 
  • CycleInterpolator: 指定された回数アニメーションを繰り返す。 
  • DecelerateInterpolator: 開始時に素早く徐々に速度を落とす。 
  • LinearInterpolator: 一定に変化させる。 
  • OvershootInterpolator: 終了時にはみ出す。
  • TimeInterpolator: カスタマー実装を行う。
29行目ではAnimationListenersを設定しています。これは、アニメーションが行われている間のイベントを監視することができます。例では、アニメーション終了時にボタンを消去しています。Listenersでは以下のイベントをキャッチすることができます。
  • onAnimationStart() アニメーション開始時に呼ばれる。
  • onAnimationEnd() アニメーション終了時に呼ばれる。
  • onAnimationRepeat() アニメーション繰り返し時に呼ばれる。
  • onAnimationCancel() キャンセル時に呼ばれる。このとき、onAnimationEnd()も同時に呼ばれる。


2013年11月15日金曜日

[Androidアニメーション] 2. Property Animationの説明

AndroidのAPIににおける2Dアニメーションの種類は主に3種類あります。

Property Animation
Android3.0(API level 11)からサポートされ、どんなオブジェクトにもアニメーションを行うことができる
View Animation
Viewsに回転、移動、拡大縮小、透明度変換などのアニメーションが行える
Drawable Animation
パラパラ漫画のようにフレームを連続再生することができる

ここでは、Developer AndroidのProperty Animationの中身をかいつまんで、Property Animationについて紹介します。サンプルプログラムについては、Property Animationのサンプルプログラムの説明で紹介しています。

Property Animationとは

Android3.0(API level 11)からサポートされ、Viewに限らず、どんなオブジェクトのプロパティにも指定した時間間隔で、アニメーションを行うことができます。ここでいうプロパティとはアニメーションに必要な設定値を指しています(例えば、色の変化値、透明度など)。このシステムは拡張性があり、カスタムタイプのプロパティにも適しています。Property Animationには以下のようなアニメーション機能を定義することができます。
継続時間(Duration): アニメーションの継続時間を指定することが出来ます。デフォルトは300msです。

補間時間(Time interpolation): アニメーションの現在の経過時間の機能として、プロパティの値がどのように計算されるか指定することが出来ます。
繰り返し(Repeat count and behavior): アニメーションの繰り返し回数や振る舞いなどを指定することが出来ます。

アニメーションのセット機能(Animator sets): 複数のアニメーションをグループ化して表示することができます。連続回数や、遅延などを指定することができます。
フレーム更新の遅延(Frame refresh delay): フレーム更新の時間を指定することが出来ます。デフォルトの設定は10msごとですが、アプリケーションのスピードに依存します。

Property Animationの動き

アニメーションがどのように行われるか簡単な例を紹介します。図1はプロパティxを持つオブジェクトを示しています。ここでプロパティxは画面のx方向への位置を表しているとします。アニメーションの継続時間を40msに設定し、移動距離を40pixelsとします。すると10 msごと、フレームが更新され10pixelsごとx方向にオブジェクトが移動します。この例では、補間時間の設定に、一定のスピードで物体が動くlinear interpolationを指定しています。

図1.Linear Interpolationの例

またスピードが変化するnon-linear interpolationも指定することが出来ます。図2はアニメーションの開始時に加速をし、中間からまた徐々に最後まで加速をするというアニメーションを示しています。オブジェクトは40msに40pixels移動しますが、速度が変化しています。


図2.non-linear Interpolationの例

Property Animationが上のようなアニメーションをどのように計算しているか説明していきます。図3はメインクラスと他のクラスの関係を示しています。


図3.アニメーションの計算の仕方

ValueAnimatorオブジェクトは継続時間やプロパティの現在の値などのアニメーションのタイミングを監視しています。ValueAnimatorはアニメーションの補間時間を定義しているTimeInterpolator、プロパティの値の計算方法を定義するTypeEvaluatorをカプセル化しています。例えば、図2ではTimeInterpolatorはAccelerateDecelerateInterpolatorをTypeEvaluatorにはIntEvaluatorが設定されています。

アニメーションを開始するには、ValueAnimatorを作り、アニメーションしたいプロパティの最初と最後の値を設定します。start()を呼ぶとアニメーションが始まります。アニメーションの間、ValueAnimatorはアニメーションの継続時間とどれくらい経過したかで、0から1の間でその割合を計算します。経過割合はアニメーションがどれくらい経過したかを0が0%を1が100%で示しています。例えば図1では、t=10での経過割合は全体の時間が40msなので、0.25になります。

ValueAnimatorが経過割合を計算しているとき、補間割合を計算するために、現在セットされているTimeInterpolatorが呼ばれます。補間割合はセットされた時間補間を考慮して新しい割合を時間補間にセットします。例えば図2でじゃ、アニメーションはゆっくり加速するので、t=10のとき、時間補間よりも小さい補間割合0.15となります。図1では補間割合は時間補間と同じように一定となります。

補間割合が計算されるときValueAnimatorはアニメーションの補間割合、始まりの値、終わりの値をもとに、プロパティの値を計算するために、適切なTypeEvaluatorを呼びます。例えば図2では補間割り肺はt=10msの時に0.15なので、この時間でのプロパティ値は0.15x(40 - 0)=6となります。

Property AnimationとView Animationの違いView AnimationシステムはViewオブジェクトにのみアニメーションを行うことが出来ます。もし、Viewオブジェクト以外にアニメーションを行いたい場合は、自信のコードを実行しなければいけません.View Animation システムは背景色の変更などはできず、Viewの回転や拡大縮小などアニメーションが限られています。さらにView Animationのデメリットは、実際のViewではなく表示されたViewのみを修正します。例えば、スクリーン上にボタンが動くアニメーションを行った場合、現在ある位置でボタンをクリックしても反応しません。自信でこれを修正する必要があります。

Property Animation システムはこれらの制約が取り除かれ、どんなオブジェクトのどんなプロパティにもアニメーションを行うことができ、オブジェクト自身も修正されます。Property Animationシステムはまたアニメーション実行においてさらにロバストになっています。ハイレベルで、animatorにアニメーションをしたいプロパティ(例えば色、位置やサイズなど)を指定をして複数のanimatorの同期や補間などのアニメーションのアスペクトを定義することができます。

View Animationシステムは短いセットアップ時間と少ないコードで設定できます。もしView Animationで自信のアニメーションが行えるのであればProperty Animationを使う必要はありません。異なった状況において両方のアニメーションシステムを使うことも可能です。

APIの概要

Property AnimationシステムのAPIはandroid.animationで定義されています。View Animationシステムではandroid.view.animationの中で、多くの補間機能が既に定義されていますが、Property Animationシステムでも同じようにこれらの機能を使うことが出来ます。以下では、Property Animationシステムの主な構成要素を示しています。

Animator クラスはアニメーションを構成する上で基本的なメソッドを定義したスーパークラスです。Animatorクラスは抽象クラスと定義されているため、直接インスタンス化することは出来ないので、通常Animatorクラスのサブクラスを使用します。サブクラスは主に3つ、ValueAnimator, ObjectAnimator, AnimatorSetがあります。主に、オブジェクトの属性などを設定するのには、ObjectAnimatorクラスを使います。

ValueAnimator

ValueAnimatorは、Property Animationのメインとなるタイミングエンジンです。プロパティ値を計算し、各アニメーションの詳細、繰り返し情報やイベントのアップデートを受け取るリスナーを含んでいます。アニメーションは、主に2つの部分に分けられます。アニメーション値の計算とアニメーションされるオブジェクトとプロパティにそれらの値を設定することです。ValueAnimatorでは、オブジェクトとプロパティに値を設定することができないので、自信のロジックでアニメーションをしたいオブジェクトに値をアップデートしていく必要があります。

ObjectAnimator

ValueAnimationのサブクラスで、ターゲットオブジェクトを設定することが出来ます。このクラスはアニメーションの値を計算する時に,それに応じてプロパティををアップデートします。ターゲットとなるオブジェクトにアニメーション値を設定できるので、簡単にアニメーションを行うことが出来ます。しかし、OjbectAnimationは現在のターゲットオブジェクトに特定のアクセッサーの方法を要求する場合に制限出てくるので、その時にはValueAnimationを直接使います。

AnimationSet

アニメーションをグループ化することができます。アニメーションの同時表示や連続表示、また指定した遅延時間後などを設定することが出来ます。

[Androidアニメーション] 1. アニメーションの種類

Androidフレームワークには、Android 3.0からサポートされているProperty Animationと従来からサポートされているView Animationがあります。Property AnimationはView Animationよりも柔軟性が高く多くの機能を持っているので、使いやすいと言えます。これらの2つのシステムに加えてDrawable Animationがあります。Drawable Animationではリソースをロードし、ぱらぱら漫画のように、連続したフレームで画像を表示することが出来ます。

Property Animation

Android3.0(API level 11)からサポートされ、Viewsに限らず、どんなオブジェクトのプロパティにも指定した時間間隔で、アニメーションを行うことができます。ここでいうプロパティとはアニメーションに必要な設定値を指しています(例えば、色の変化値、透明度など)。このシステムは拡張性があり、カスタムタイプのプロパティにも適しています。
サンプルプログラムの説明

View Animation

View Animationは、従来のシステムで、Viewsにのみアニメーションを行うことが出来ます。設定が簡単で、Viewsの移動、拡大縮小、透過度の変化など、基本的な機能を備えています。
サンプルプログラムの説明

Drawable Animation

Drawable Animationは、Drawable リソースを次から次へと表示することが出来きます。パラパラ漫画のような連続的なアニメーションを行うことが出来ます。
サンプルプログラムの説明




サンプルプログラムは、各アニメーションを最も簡単な例で示しています。詳細は各アニメーションの紹介ページ(Property Animation, View Animation, Drawable Animation)で説明しています。参考にしてください。


public class SampleAnimation extends Activity {
 
 private static final int RED = 0xffFF8080;
 private static final int BLUE = 0xff8080FF;
 
 AnimationDrawable mAnimationDrawable;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  
  //Property Animation: Changing TextColor-------------------------------------
  Button btnColor = (Button) findViewById(R.id.btncolor);
  //Define an object and property in ObjectAnimator 
  ObjectAnimator animColor = ObjectAnimator.ofInt(btnColor,"textColor", RED, BLUE);
  //Set duration time (ms)
  animColor.setDuration(3000);
  //Set Evaluator, how to calculate the specified values.
  animColor.setEvaluator(new ArgbEvaluator());
  //Set repeat count
  animColor.setRepeatCount(ValueAnimator.INFINITE);
  //Set repeat mode
  animColor.setRepeatMode(ValueAnimator.REVERSE);
  //Animation starts
  animColor.start();
  
  //View Animation: Changing Transparency--------------------------------------
  Button btnAlpha = (Button) findViewById(R.id.btnalpa);
  //Set a value of transparency
  AlphaAnimation alp1 = new AlphaAnimation(1.0f, 0.0f);
  //Set duration time (ms)
  alp1.setDuration(3000);
  //Set repeat count
  alp1.setRepeatCount(Animation.INFINITE);
  //Animation starts
  btnAlpha.startAnimation(alp1);
     
  //Drawable Animation: Changing Images ----------------------------------------------
  //Create AnimationDrawable
  mAnimationDrawable = new AnimationDrawable();
  //Set frames
  Drawable[] mFrame = new Drawable[2];
  mFrame[0] = getResources().getDrawable( R.drawable.imgcat1);
  mFrame[1] = getResources().getDrawable( R.drawable.imgcat2);
  //Add frames to AnimationDrawable
  for(int i = 0; i < mFrame.length; i++){
   mAnimationDrawable.addFrame( mFrame[i],  200);
  }
  ImageView imageView = (ImageView) findViewById(R.id.image);
  //Set the animation to background drawable
  imageView.setBackgroundDrawable(mAnimationDrawable);
  //Animation starts
  imageView.post(new Runnable(){
      public void run(){
       mAnimationDrawable.setOneShot(false);
    mAnimationDrawable.start();
    
      }
  });
  
 }

}

2013年10月8日火曜日

EditTextにおける文字制限の設定(入力候補の非表示など)

EditTextで英数字のみや、数字のみを入力したい場合に、文字制限を行うことができます。EditTextのインスタンスのsetInputTypeでオプションを設定します。
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.main);
        
        EditText editText = (EditText)findViewById(R.id.edittext);
        //Not to display any dictionary-based candidates
        editText.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

    }
EditTextに文字を入力する際に、辞書機能が働き、入力候補が表示されてしまうのを非表示にする設定なども用意されています。

InputTypeの設定オプションは以下の通りです。

  • TYPE_CLASS_DATETIME 日付と時刻
  • TYPE_CLASS_NUMBER 数値
  • TYPE_CLASS_PHONE 電話番号
  • TYPE_CLASS_TEXT 文字列
  • TYPE_TEXT_FRAG_AUTO_COMPLETE オートコンプリート機能
  • TYPE_TEXT_FLAG_AUTO_CORRECT 文字列の自動修正機能
  • TYPE_TEXT_FLAG_CAP_CHARACTERS すべて大文字
  • TYPE_TEXT_FLAG_CAP_SENTENCES センテンスの先頭が大文字
  • TYPE_TEXT_FLAG_CAP_WORDS 単語の先頭が大文字
  • TYPE_TEXT_FLAG_NO_SUGGESTIONS 入力候補の非表示

2013年10月4日金曜日

TimerとHandlerの連携

Androidはシングルスレッドのため、GUI操作はmainスレッドで行われています。Timerを使用する場合には、別スレッドを儲け、Handlerを使用して、mainスレッドにアクセスする必要があります。
以下Timerのサンプルプログラムで説明をしていきます。スタートボタンを押すと、カウントが始まり、ストップボタンを押すとカウントが止まります。

public class TimerExample extends Activity implements OnClickListener{
 TextView timeTextView;
 Button btnStart;
 Button btnStop;
 private Handler mHandler;
 private Timer mTimer;
 private int count = 0;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.timer_example);
  //Setting TextView
  timeTextView = (TextView) findViewById(R.id.time);
  //Setting Button
  btnStart = (Button) findViewById(R.id.btnstart);
  btnStart.setOnClickListener(this);
  btnStop = (Button) findViewById(R.id.btnstop);
  btnStop.setOnClickListener(this);
 }
 
 @Override
 public void onClick(View v) {
  if(v == btnStart){
   if(mTimer != null) return;
   count = 0;
   mTimer = new Timer();
   mHandler = new Handler();
   mTimer.schedule(new TimerTask() {
             public void run() {
                 mHandler.post(new Runnable() {
                     public void run() {
                      ++count;
                      timeTextView.setText(String.valueOf(count));
                     }
                 });
             }
         },0, 1000);
  }
  
  if(v == btnStop){
   if(mTimer == null) return;
   mTimer.cancel();
   mTimer = null;
  }
 }
}

Timer
Timerのメソッドでは、遅延や何秒間隔で繰り返すかなどを設定することができます。

1. Timerのインスタンス
まず最初にTimerのインスタンスを生成します。
mTimer = new Timer();

2. Timerのスケジュール設定
TimerTask()のインスタンスを生成し、遅延や何秒間隔で繰り返すか設定します。
mTimer.schedule(new TimerTask() {
     ...
},0, 1000);



  • TimerTask task: タイマースケジュールのためのタスク
  • long Delay: 最初の実行を行うまでの遅延時間(ms)
  • Period: 繰り返し実行の時間間隔(ms)

  • 3. Timerのキャンセル
    Timerとスケジュールされているタスクのキャンセル。現在実行中のタスクはキャンセルされません。
    mTimer.cancel();

    TimerTask
    決められた時間間隔でタスクを実行します。
    new TimerTask() {
        public void run() {
        ...
        }
    }

    Handler
    Handlerによって、別スレッドからメインスレッドに通信をすることができます。ここではTimerが別スレッドになります。Timerからシグナルが来た時に、Runnable内のコードがメインスレッドとして実行されます。

    2013年7月4日木曜日

    SoundPoolとMediaPlayerの使い分け

    Androidで音を再生するにはSoundPoolとMediaPlayerの2つの方法があります。

    1. SoundPoolとMediaPlayerの違い

    SoundPool
    SoundPoolは、再生時間が短い効果音などを扱うときに最適です。オーディオファイルがメモリに展開されている状態で再生を行うので、遅延がほとんどありません。また、複数の音声を同時に再生することができます。ただし、長いオーディをファイルを扱った場合、ファイルサイズが100kbを超えてしまうと、OutOfMemoryExceptionが発生してしまいます。

    MediaPlayer
    MediaPlayerは、BGMなど長い曲を再生するときに最適です。オーディオファイルは、毎回呼ばれるたびにメモリにロードされるので、メモリを節約することができます。しかし、再生を行う際に若干の遅延が生じてしまいます。MediaPlayerはオーディオだけでなくビデオも再生することができます。アプリのresourcesだけでなくSDカードにあるオーディオファイルやHTTPサーバに置かれたMP3ファイルを再生することができます。

    2. SoundPoolの使い方

    SoundExampleというActivityクラスを持つ以下のプログラムで説明していきます。ボタンを押した時に音声が鳴るというプログラムです。
    public class SoundExample extends Activity implements OnClickListener{
     
     private SoundPool soundPool;
     private int[] soundRes = {R.raw.sample1, R.raw.sample2, R.raw.sample3};
     private int[] soundID;
     private Button btnSPPlay;
     private int index = 0;
     
     @Override
     protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.sound_example);
      //Setting Play Button
      btnSPPlay = (Button) findViewById(R.id.btnplay);
      btnSPPlay.setOnClickListener(this);
     }
     
     @Override
        protected void onResume() {
            super.onResume();
            //Setting SoundPool
      soundPool = new SoundPool(soundRes.length, AudioManager.STREAM_MUSIC, 0);
      soundID = new int[soundRes.length];
      for(int i = 0; i < soundRes.length; i++){
       //Loading sound files
       soundID[i] = soundPool.load(this, soundRes[i], 1);
      }
     }
     
     @Override
        protected void onPause() {
            super.onPause();
            //Releasing SoundPool
            soundPool.release();
     }
     
     @Override
     public void onClick(View v) {
      if(soundRes.length <= index) index = 0;
      if(v == btnSPPlay){
       //Playing Sound
        soundPool.play(soundID[index], 1.0F, 1.0F, 0, 0, 1.0F);
        ++index;
      }
     }
    }
    
    onResume()でSoundPoolを初期化しファイルをload()し、効果音を鳴らしたいイベントが発生をしたときにplay()をします。最後にonPause()でrelease()させます。SoundPoolはloadしたときにデータをメモリに展開します。よって、毎回アプリを起動させた時にファイルをloadし、終了したときにメモリを解放させたほうがメモリ効率がよくなります。

    1. SoundPoolのインスタンス
    まず最初にSoundPoolのインスタンスを生成します。
    new SoundPool(soundRes.length, AudioManager.STREAM_MUSIC, 0);
    • maxStreams: 扱うファイルの最大数
    • streamType: Streamのタイプ(STREAM_MUSIC)
    • srcQuality:サンプリングレートのクオリティ(現在使用不可、デフォルト 0)
    2. Load()
    次に音声ファイルをloadしていきます。ファイルは/res/raw/に置きます。今回は3つのオーディオファイル(sample1.ogg, sample2.ogg, sample3.ogg)を用意しました。
    SoundPool.load(this, soundRes[i], 1);
    • context: コンテキスト
    • resId: オーディオファイルのリソースID
    • priority: 優先度サンプルレートのクオリティ。(現在使用不可、デフォルト 1)
    3. play()
    音声を鳴らしたいイベントにplay()を設定します。複数のオーディオファイルを扱うには、soundIDで識別します。
    soundPool.play(soundID[index], 1.0F, 1.0F, 0, 0, 1.0F);
    • soundID … load()で返された soundID
    • leftVolume … 左の音量 (0.0~1.0)
    • rightVolume … 右の音量 (0.0~1.0)
    • priority … ストリームの優先度 (0: 優先度最低)
    • loop … ループ回数 (0: ループ無し、-1: 無限ループ)
    • rate … 再生レート (0.5~2.0 1.0が標準)
    4. release()
    最後onPause()でSoundPoolをrelease()します。ファイルをメモリから解放します。
    soundPool.release();

    Android 4.3ではSoundPoolのloopが効かないバグの報告があります。

    3. MediaPlayerの使い方
    SoundMPExampleというActivityクラスを持つ以下のプログラムで説明していきます。スタートボタンで音声が流れ、Pauseボタンで停止をします。

    public class MediaPlayerExample extends Activity implements OnClickListener{
     private MediaPlayer mediaPlayer;
     private Button btnPlay, btnPause;
     
     @Override
     protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.sound_example);
      
      //From Resource
      mediaPlayer = MediaPlayer.create(this, R.raw.sample);
      
      //Setting Button
      btnPlay = (Button) findViewById(R.id.btnplay);
      btnPlay.setOnClickListener(this);
      btnPlay = (Button) findViewById(R.id.btnpause);
      btnPlay.setOnClickListener(this);
     }
    
     @Override
     public void onClick(View v) {
      if(v == btnPlay){
       //Start playing a sound
       mediaPlayer.start();
      }
      if(v == btnPause){
       //Pause playing a sound
       mediaPlayer.pause();
      }
     }
     
     @Override
        protected void onPause() {
            super.onPause();
            //Pause playing a sound
            mediaPlayer.pause();
     }
     
     @Override
        protected void onDestroy() {
            super.onDestroy();
            //Releasing SoundPool
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
          
     }
    }
    
    onCreate()でMadiaPlayerを初期化します。play()で再生、pause()で停止します。

    1. MadiaPlayerの初期化
    リソースからの場合
    オーディオファイルは/res/raw/以下に置きます。ここでは/res/raw/sample.mp3としています。
    MediaPlayer.create(this, R.raw.sample);
    • Context: コンテキスト
    • resId: リソースID
    ローカル/htmlからの場合
    ローカルファイルを扱う場合には、setDataSource()を使用します。setDataSource()の後にprepare()が必要になります。
    ファイルパスを使用する場合
    String url = Environment.getExternalStorageDirectory() + "/sample.mp3";
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(url);
    mediaPlayer.prepare();
    
    Uriを使用する場合
    Uri mUri = Uri.parse(Environment.getExternalStorageDirectory() + "/sample.mp3");
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(getApplicationContext(), mUri);
    mediaPlayer.prepare();
    
    2. start()
    start()で再生されます。

    3. pause()
    pause()で停止します。stop()でも停止しますが、stop()を使用した時には後に再生する前にprepare()が必要になります。



    2013年3月9日土曜日

    Android 4.2 開発者向けオプション(Developer Settings)の表示方法

    テスト用にNexus 7を購入したのですが、いざ開発をしようとしたときに開発画面が何処を探してもありません。Android 4.2 JellyBean 搭載の機種では、開発者向けオプションを有効にする必要があります。一般ユーザには必要ない機能なのでGoogleは初期状態で非表示にしたようです。
    1. 設定(Settings)-> タブレット/端末情報(About tablet/phone)
    2. 一番下にビルド番号(Build number)があるので、ここを7回タップする。
    3. 設定に戻るとタブレット/端末情報の上に開発者向けオプションが表示される。

    参考: http://www.androidcentral.com/how-enable-developer-settings-android-42

    2012年12月28日金曜日

    アプリ内課金バージョン3 (In-App Billing Version 3)

    ここではAndroid DevelopersのIn-app Billing Version 3のページを訳してみました。バージョン 3では、管理商品("managed products")のみをサポートしています。管理商品では、Google Playがユーザのアイテム所有の有無を全て管理してくれます。また、消費商品("consumable products")が扱えるようになりました。消費商品とは、ゲーム内の通貨など、アイテムが消費されるたび、再度購入ができるようになります。

    In-app Billing Version 3
    In-app Billing version 3 APIではアプリ内課金を簡単にあなたのアプリに統合することが出来ます。このバージョンは、改良された同期購入フロー、消費商品の所有の記録を簡単に行えるAPIやアプリ内購入データのローカルキャッシュなどの特徴を含んでいます。

    Product Types
    Google Play Developer Consoleを使って、商品タイプ、SKU、値段、説明文など、商品を定義することができます。詳細は、Administering In-app Billingを見てください。Version 3 APIは管理商品 ("managed products") のみサポートしています。

    Managed In-app Products
    アプリ内管理商品(managed in-app products) は、Google Playによって管理され記録された所有情報を持つアイテムです。ユーザが管理アイテムを購入したとき、Google Playは各ユーザごと、各アイテムの購入情報を保存します。特定ユーザが購入したアイテムの状態を復元するために、いつでもGoogle Playにクエリを送ることができます。ユーザがアプリをアンインストールしたり、ディバイスを変更したとしても、この情報はGoogle Play サーバ上に保存してあります。

    Version 3 APIを使用する場合、アプリの中で管理アイテムを消費することができます。何度も購入することができるアイテム (例えばゲーム内の通貨、燃料や魔力など) の消費を実装することができます。いったん消費アイテムが購入されると、Google Playに消費リクエストを送ることでそのアイテムが消費されるまで、再び購入することは出来ません。商品の消費に関しての詳細は、Consuming Itemsを見てください。

    Purchasing Items
    Version 3 APIの購入の流れは以下の通りです。

    1. 使っているアプリ内課金 API のバージョンがサポートされているか確認するために、アプリはGoogle PlayにisBillingSupportedリクエストを送る。
    2. アプリがスタートしたときやユーザがログインしたとき、何のアイテムをユーザが所有しているか確定するために、Google Playでチェックすることを推奨します。ユーザのアプリ内商品の検索には、getPurchasesリクエストを送ります。リクエストが成功したら、Google Playは購入アイテムの商品IDのリスト、個々の購入アイテムの詳細リスト、購入のためのサインのリストを含んだBundleを返します。
    3. 通常、購入できる商品をユーザを知らせたいと思います。Google Playで定義した商品の詳細を検索するために、getSkuDetailsリクエストを送ることができます。検索リクエストには、商品IDのリストを明記する必要があります。リクエストが成功したら、Google Playは商品の値段、タイトル、詳細、購入タイプを含んだBundleを返します。
    4. 商品がユーザによって所有されていなかったら、購入プロセスを始めることができます。購入リクエストを始めるには、購入するアイテムの商品IDを他のパラメータと一緒にを明記した getBuyIntent リクエストを送ります。Developer Consoleで新しい商品を作るとき、商品IDを記録する必要があります。
    a. Google Playは購入のためのチェックアウトUIをスタートするのに使うPendingIntentを含んだBundleを返します。 b. アプリはstartIntentSenderForResutメソッドを呼ぶことによってpending intentを立ち上げます。 c. チェックアウトフローが終了したとき (ユーザがアイテムを購入もしくは購入をキャンセルしたとき)、Google PlayはonActivityResultメソッドにレスポンスIntentを送ります。onActivityResutの結果コードは購入が行われたかキャンセルされたかどうかを示す結果コードを持っています。レスポンスIntentは購入されたアイテムについての情報を含んでいます。この購入取引を特定するために、レスポンス Intentはgoogle Playによって発行されたpurchaseToken Stringを含んでいます。このIntentはprivate developer key にサインした購入サインも含んでいます。
    Version 3 APIのコールについて更に学びたい場合、In-app Billing Referenceをみてください。

    Consuming Items
    商品のユーザの所有状態を監視するために消費メカニズム 使用することができます。


    バージョン3では、全てのアプリ内商品は管理されています。これは全てのアイテム購入のユーザの所有がGoogle Playによって保持されていることを意味します。そしてアプリ側は必要なときにユーザの購入情報を検索することができます。ユーザがアイテムの購入に成功したとき、その購入情報はGoogle Playに記録されます。一旦アイテムが購入されると、それは所有された("owned") 状態と見なされます。"owned"状態のアイテムはGoogle Play から購入することはできません。 Google Playが再び購入を可能にする前に、"owned"アイテムの消費リクエストを送らなければいけません。アイテムを消費するということは、非所有("unwoned")状態に戻り、前の購入データを破棄するということです。

    ユーザの所有している商品リストを検索するには、アプリ側はgetPurchasesコールをGoogle Playに送ることができます。アプリ側はconsumePurchaseコールを送ることによって消費リクエストを作ることができます。リクエストの中で、購入時にGoogle Playから手に入れたアイテムの固有のpurchaseToken Stringを明確にしなければいけません。消費の登録に成功したら、Google Playはステータスコードを返します。

    Non-consumable and consumable items

    アプリ内商品を消費アイテムにするか非消費アイテムにするかは、あなた自身で決められます。

    Non-consumable items
    通常、アプリ内で一度だけ購入することができ、永続してベネフィットを提供するアイテムなので消費を実装をする必要はありません。一度購入したら、アイテムはユーザのGoogleアカウントに関連づけられます。非消費アイテムの例としてはプレミアムアップグレードやレベルパックなどです。

    Consumable items
    何度も購入することができるアイテムで消費を実装することができます。通常これらのアイテムは一時的な効果を提供します。例えば、ユーザのゲーム内でのキャラクターがライフポイントを得るためや、ゴールドコインを手に入れるなどです。アプリの中で購入したアイテムのベネフィットや効果を施すことは、商品をプロビジョニングするといいます。どのように商品がユーザに提供されるのか、開発者は、それを管理し記録する責任があります。
    重要:アプリの中で消費アイテムをプロビジョニングする前に、Google Playに消費リクエストを送り、消費が記録されたことを指し示すレスポンスを受け取らなければいけません。

    Managing consumable purchases in your application
    消費アイテムを購入するための基本的な流れは以下のとおりです。

    1. getBuyIntentコールで購入フローを始めます。
    2. 購入が完全に成功したかどうかを示すBundleをGoogle Playから受け取ります。
    3. 購入が成功したら、consumePurchaseコールを作ることによって、購入を消費することができます。
    4. 消費が成功したかどうかを示す返答コードをGoogle Playから得ます。
    5. もし消費が成功したら、アプリの中で商品をプロビジョンします。
    その後、ユーザがアプリをスタートアップもしくはログインするときには、ユーザが未処理の消費アイテムを所有しているかどうか確認しなければいけません。その場合、それらのアイテムの消費とプロビジョンが出来ているかを確認してください。ここではアプリの中で消費アイテムを実装するための推奨されたアプリのスタートアップの流れです。

    1. ユーザが所有しているアイテムを検索するためにgetPurchases リクエストを送ります。
    2. 何か消費アイテムがあったら、consumePurchaseを呼ぶことによってアイテムを消費します。消費アイテムの購入オーダが完了していて、しかし、消費リクエストを送る機会を持つ前に、ストップしてしまったりアクセスが切られてしまう場合があるので、このプロセスは必要です。
    3. 消費が完了したかどうかを示すレスポンスコードをGoogle Playから得ます。
    4. 消費が成功したら、アプリの中の商品をプレビジョンします。
    Local Caching
    Google playクライアントが、ディバイス上にin-appBilling情報をキャッシュすることができるので、更に頻繁にこれらの情報を検索するのに、例えば、getPurchasesコールを通して、Version3 APIを使うことができます。前のバージョンのAPIとは違い、Version 3 APIのコールは、Google Playに繋ぐネットワークコネクションの代わりにAPIの返答時間を十分にスピードアプしたキャッシュ検索を通して提供されます。

    2012年12月22日土曜日

    アプリ内課金概要 (In-app Billing Overview for Android)

    Android DevelopersのIn-appBilling Overviewのページを訳しました。
    アプリ内課金のバージョン2と3の違いとそれぞれの商品タイプについて説明されています。今後バージョン3での開発を勧めています。

    アプリ内課金概要 (In-app Billing Overview)
    ここでは、アプリ内にアプリ内課金(In-app Billing)を加えるために理解しておくべき、基本的なアプリ内課金の概念と特徴について説明します。

    In-app Billing API

    アプリはディバイスにインストールされているGoogle PlayアプリにあるAPIを使ってアプリ内課金サービスにアクセスします。Google PlayアプリはアプリとGoogle Playサーバ間の課金リクエスト(billing requests)とレスポンス(billing responses)を送ります。実際に、アプリはGoogle Playサーバと直接コミュニケーションを取ることはありません。代わりにアプリは課金リクエストをプロセス間通信によってGoogle Playアプリに送り、Google Playアプリからレスポンスを受け取ります。アプリはGoogle Play サーバ間のネットワーク通信の管理は一切しません。

    アプリ内課金はGoogle Playで公開されたアプリにのみ実装することができます。アプリ内購入リクエスト( in-app purchase requests)を完了するために、Google PlayアプリはGoogle Playサーバにネットワークを通してアクセスする必要があります。


    現在、Google Playはアプリ内課金APIに関して2つのバージョンを提供しています。どちらのバージョンを使うかはMigration Considerationsを参照してください。


    Version 3 (Recommended)

    • Google Playからのプロダクト詳細のリクエスト、プロダクトのオーダー、ユーザのプロダクト所有に基づいたアイテムの復元などを簡単に行うため、簡素化されたAPIを通してリクエストは送られる。
    • オーダー情報は購入完了をディバイスに同期的に伝える。
    • 全ての購入は管理("managed")される。(Google Playはアプリ内プロダクトのユーザの所有を管理している。)ユーザはアプリ内アイテムのコピーを複数所有することはできない。いずれの時点でも1つのコピーだけ所有することができる。
    • 購入されたアイテムは消費することができる。消費された場合、そのアイテムは非所有状態("unowned")に戻り、Google Playから再び購入することができる。

    Version 2

    • リクエストは単体APIインタフェース(sendBillingRequest)を通して送られる。
    • Google Playからのレスポンスは非同期で、Broadcast Intentの形で行われる。
    • 消費モデルは提供されない。自身で実装しなければいけません。
    • 非管理アプリ内購入アイテム (unmanaged)を管理アプリ内購入アイテム (managed) と同様サポートしている。
    どちらのバージョンも幅広いAndroid端末に互換性があります。アプリ内課金Version 3はGoogle Play ストアーの最新バージョンが搭載されているAndroid 2.2以上を搭載の機種をサポートしています。Version 2も同じように互換性があります。詳細はVersion Notesを見てください。

    In-app Products

    アプリ内プロダクトはアプリ内からユーザに販売するために提供するデジタル商品です。デジタル商品の例は、ゲーム内の通貨、ユーザの経験を強化するアプリのアップグレード、新しいコンテンツなどが含まれます。

    デジタルコンテンツを売ることだけにアプリ内課金を使うことができます。物理的商品、個人的サービス、物理的な配送を要求すものを売るためにアプリ内課金を使用することはできません。有料アプリとは違い、ユーザがアプリ内プロダクトを一度購入すると払い戻し画面は表示されません。


    Google Playはコンテンツ配信の形で提供はしません。開発者にはアプリの中で売るデジタルコンテンツを配信する責任があります。アプリ内プロダクトは1つのアプリのみ明確に関連づけられています。たとえ同じ開発元であっても、他のアプリで発行されたアプリ内プロダクトを購入することはできません。


    Product types

    アプリの収益化の仕方に柔軟性を持たせるために、アプリ内課金は異なる商品タイプをサポートしています。全ての場合において、Google Play Developer Consoleを使って製品を定義することができます。

    管理商品(managed)、非管理商品(unmanaged)、定期購買商品(subscriptions)の商品タイプをアプリ内課金で指定することができます。管理("managed")とは
    Google Play がユーザのアカウントを元にアプリ内商品の所有情報を管理し記録することを示しています。一方非管理("unmanaged")とは、開発者自身で所有情報を管理しなければいけません。

    APIバージョンの違いによる製品のタイプのサポートについて更に知りたい場合、以下の資料を見てください。Version2, Version3


    Google Play Developer Console

    Developer Consoleではアプリ内課金を実装したアプリを公開し、そのアプリから購入できる様々な商品を管理することが出来ます。

    1度のみの購入や定期購入の自動更新といったアイテムを含む、
    アプリに関連したデジタル商品のリストを作ることができます。各アイテムに対し、ユニーク商品ID (SKU),商品タイプ、値段、説明、商品の購入履歴をGoogle Playが管理をすべきかなどの情報を定義することができます。

    また非公開のテストアプリにアクセス許可を持たせたテストアカウントを作ることができます。


    商品や商品リストの設定に関してDeveloper Consoleの使い方を学ぶには、Administering in_app Billingを見て下さい。


    Google Play Purchase Flow

    Google Playはアプリ購入に使われるバックエンド精算サービスを使用するので、ユーザにとって一貫性のある親しみやすい購入の流れになっています。

    重要:Google Play上でアプリ内課金サービスを使用する
    にはGoogle Checkout Merchant のアカウントを持たなければいけません。

    購入を開始するために、アプリは特有のアプリ内商品の課金リクエストを送ります。google Playは支払い形式のリクエストや認証、
    ファイナンシャル取引の処理などを含む取引に関する精算の詳細をすべて管理します。

    精算処理が完了すると、Google Playは注文番号、注文日時、そして支払いなどの購入の詳細をアプリに送ります。アプリはファイナンシャル取引を管理する必要はありません。それはGoogle Playによって提供されます。


    The Sample Applications

    アプリにアプリ内課金を統合する手助けとして、Android SDKはアプリ内で商品を売るための方法をデモした、2つのサンプルアプリを提供しています。

    TrivialDrive sample for the Version 3 API

    このサンプルはドライブゲームで商品購入を実装し、アプリ内課金 Version 3 APIの使い方を示しています。このアプリはアプリ内課金のレクエストの送り方、Google Playからの同期したレスポンスの管理の仕方をデモしています。また、APIを使ったアイテムの消費の記録の仕方なども紹介しています。Version 3のサンプルはアプリ内課金処理の方法と同様に自動署名認証の仕方も含まれています。

    Dungeons sample for the version 2 API

    このサンプルはアドベンチャーゲームのための商品と定期購買を販売するためのアプリない課金 Version 2 APIの使い方をデモしています。またデータベース、ユーザインタフェースそしてアプリ内課金の実装に使える基本的ロジックを含んでいます。

    重要:アプリを公開する前にアプリのコードを難読化することを進めます。詳細情報は

    Security and Designを見てください。

    Migration Considerations

    アプリ内課金を含んだアプリを新しく作る際やVersion 2 やそれよりも前のAPIからVersion 3のAPIにアプリ内課金の実装を移行する際に考慮するべきことは以下のとおりです。

    Google PlayはVersion2とVersion3 APIどちらも同時にサポートを続ける予定です。自身のペースでVersion3に移行してください。アプリ内課金 Version 2のサポート状態の変更はGoogle Play チームから前もってお知らせします。

    必要性によってどちらのAPIのバージョンを使えばいいか以下のテーブルを参照してください。
    Table 1. Selecting the In-app Billing API Version for Your Project

    Choose Version 3 if ...
    • You want to sell in-app products only (and not subscriptions)
    • You need synchronous order confirmations when purchases complete
    • You need to synchronously restore a user's current purchases
    Choose Version 2 if ...

    • You want to sell subscriptions in your app


    もし、アプリ内で商品を売っているアプリを既に公開している場合以下に注意してください。


    • Developer Consoleで前持って定義した管理アイテム("managed items")はVersion 3で使用できます。
    • Version 3 APIを使ってアイテムの購入リクエストを行う場合、既存アプリで非管理アイテム("unmanaged items")と定義されているものは管理アイテム("managed items")として扱われます。このアイテムのためにDeveloper Consoleに新しいプロダクトを作る必要はありません。このアイテムの購入のために同じプロダクトIDを使用することができます。Version2 もしくはその前のAIPを使ってその購入リクエストを作る場合、非管理アイテム("unmanaged items")としてそのまま扱われます。

    2012年11月20日火曜日

    Facebook SDK for Android v3.0 を試す

    Getting Started with the Facebook SDK for Android v3.0

    Android用Facebook SDKはFacebookプラットフォームを最も簡単にAndroidアプリに統合することができます。SDKはFacebookのLogin認証、Facebook APIへのread/write、またピッカーUI(pickers)やダイアログのようなUI部品をサポートしています。
    Android用Facebook SDKの開発を始めるため、以下のステップに沿って行いましょう。このガイドラインで使用しているスクリーンショットはApple OS X 環境のものですが、Windowsへのインストールのステップも殆ど同じ手順で行うことができます。手順が異なる部分はハイライトされています。

    1. 必要なものをインストール(省略)

    Eclipse(少なくともAndroidの開発に必要な開発環境はEclipse Classic), Android SDK, ADT Pluginが必要です。
    Facebook SDKを使用するために、ここではAndroid 2.2(API 8)のインストールが必要です。

    2. Android用のFacebook SDKとFacebook APKのインストール(省略)

    Installationパッケージをダウンロードし展開。エミュレータにFacebookアプリをインストール。

    Facebookのネイティブアプリ(公式アプリ)がインストールされている場合、Facebook SDKは認証をサポートするのにネイティブアプリを使用します。実機上では、このテストを行うのに、Google Playから無料でこのアプリをインストールすることができます。

    インストールされたFacebookアプリのAPKはSDKのbinフォルダーにある、FBAndroid-1.9.9.apkとなっています。

    (エミューレータへのインストールは省略)


    3. EclipseにFacebook SDKをインポート

    サンプルプログラムと一緒に、ワークスペースにlibraryプロジェクトとしてFacebook SDKを追加。
    InstallationフォルダはSDKプロジェクトであるfacebookディレクトリに含まれています。サンプルプロジェクトはsamplesフォルダに含まれています。

    空のワークスペースに全てのプロジェクトを一気にインポートすることができます。このときワークスペースに古いfacebookプロジェクトがないことを確認してください。もし古いものが存在していると、古いFacebook SDKを参照してしまう可能性があるので、古いものを削除するかワークスペースを変更してください。

    新しいFacebook SDKと一緒にSDK Libraryプロジェクトとサンプルをインポートするには、Eclipseの 'File' > 'Import' で 'General' / 'Existing Projects into Workspace'を選択してください。
    SDKフォルダのルートであるfacebook-android-ask-3.0.1.bを選択します。全てのサンプルプログラムが表示されるので、'FacebookSDK'が含まれていることを確認してください。
    'Copy projects into workspace'オプションを外すことで、隣接するSDKに対し各サンプルプロジェクトは正しい参照を保持します。これはEclipseSDK installationの中にプロジェクトのコピーではなくリンクを張ることを意味します。

    (もし将来SKD in-placeをアップデートし、結果的にワークスペースのLibraryプロジェクトをアップデートするときにこのプションが役立ちます。ですが将来SDKサンプルを編集するとなると、オリジナルバージョンに影響しないようにコピーを後に再インポートしなければいけなくなります。)

    'Finish' をクリックします。もしサンプルプロジェクトにエラーが表示されたら、Eclipseの 'Project' > 'Clearn' で全てのプロジェクトの状態をリフレッシュしてください。



    EclipseコンパイラのレベルがAndroidによって要求されたものに一致しない可能性があります。もし、'Android requires compiler compliance level 5.0 or 6.0'というエラーが出たら、'Java Compiler' のプロジェクトプロパティがv1.6になっているか確認してください。


    これでサンプルアプリを動かすことができます。いくつかのサンプルを動かすためにFacebookAndroid key hashを準備する必要があります。手順は次のステップで紹介します。


    4. サンプルプログラムを動かす(省略)


    サンプルプロジェクトを試し、Facebook プロファイルにAndroid key hashを入力。


    各サンプルプロジェクトはSDKの重要な部分とソーシャルアプリの構築のためのテクニックを示しています。

    基本サンプル
    • ProfilePictureSample
    • FriendPickerSample
    • PlacePickerSample

    機能サンプル
    • SessionLoginSample
    • JustRequestSample
    • BooleanOGSample
    • SwitchUserSample

    完成サンプル
    • HelloFacebookSample
    • Scrumptious
    • Hackbook
    これらのサンプルは前のステップでインポートしたワークスペースに置きます。各サンプルが隣接する'FacebookSDK'ライブラリを正しく参照している必要があります。

    サンプルプロジェクトを選択し、Eclipseの'Run'を実行します。初めて行う際には、以下のダイアログが表示されます。

    'Android Application'を選択するとAndroid エミュレータがスタートし、選択したサンプルが表示されます。

    ProfilePictureSampleのようなとても単純なアプリは即座に実行されます。もし、認証を含んだサンプルを実行したい場合、更にFacebookにAndroid key hashを提供するための設定を行う必要があります。

    簡単に言うと作成した各Androidアプリは署名が必要であり、後に説明しますが、認証のためのセキュリティチェックとしてFacebookに各アプリのkey hashを登録する必要があります。SDKサンプルではこのチェックを回避して簡単に実行するために、Facebook Developerプロファイルにアプリのkey hashを入力することができます。

    まずパソコン上でkey hashを生成するために、Javaのkeytool utilityを実行します。(Androidのdebug keysotreではなくコンソールのパスにします。)


    OS Xでの実装:
    keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64
    Windowsでの実装:
    keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\debug.keystore | openssl sha1 -binary | openssl base64
    パスワードを聞かれるので、ここではandroidとします。30文字くらいのkey hashを得ることができます。(もしパスワードを聞かれなかったら何かが間違っているので、debug.keystoreが上記のパスに存在しているか確認してください。)


    公開するアプリでは、debug.keystoreではなく自分のアプリの署名に使用したkey hash(xxx.keystore)を使用します。(xxxはaliasを示す) パスワードは自分で設定したxxx.keystoreのパスワードを入力する必要があります。 
    keytool -exportcert -alias xxx -keystore /keystoreまでのパス/xxx.keystore | openssl sha1 -binary | openssl base64

    最後にFacebook Developer siteに行きます。Loginされているか確認し、画面右上にあるメニューから'Setting'を選択します。


    画面左の'Developer Settings'に行きます。もし、developerの登録をしていなければ登録を要求されます。

    プロファイルにKey hashを登録し保存します。

     
    (複数のパソコンで開発を行っている場合は複数のkey hashを登録することができます。)
    これで、問題なくエミュレータで全ての基本的な認証サンプルプログラムを動かすことができるはずです。


    5. Facebookアプリを作る


    新しいFacebookアプリを作り、そのIDとAndroid key hashの関連づけ。

    Facebook Developers サイトのApp Dashboardに戻ります。'Create New App'をクリックし、基本情報としてアプリ名やnamespaceを入力します。


    一端作成すると、dasyuboradのトップにアプリのIDが表示されます。後にプロジェクトファイルにこのIDを加える必要があります。




    既に存在しているアプリのIDももちろん使えることが出来ます。

    いずれにしても、アプリにAndroid key hash を関連づける必要があります。'Edit App'をクリックし、dashboardの下の方にある'Native Android App'セクションを開きます。keytoolアプリで前のステップの最後に手に入れたkey hashを加えます。



    変更を保存します。

    このdashboradに戻り、アプリのパッケージ名と新しく作成したAndroidプロジェクトのmain activity classを登録する必要があります。

    6. Facebook Loginの新しいAndroidプロジェクトを作る




    Facebook SDKにリンクした新しいプロジェクトの作成。Facebook Loginとユーザ名の取得のデモ。

    この最後のステップで、新しいプロジェクトを作り、Facebook Loginのデモに必要な最低限のコードを加えます。

    新しいAndroid project の作成
    Eclipseに新しいAndroid アプリのプロジェクトを作成するには、File>New>Project…で'Android Application Project'を選択します。
     
    Facebookのアプリを登録したときに、使用したアプリ名とNamespaceを使う必要はありませんが、ここでは統一するために用います。アプリにアイコンを選択することができます。
     
    次に、アプリのmain activityを作成します。この例では、基本的なユーザインタフェースを持つシンプルなBlankActivityを選択します。
    最後に、Activityの名前を入力します。レイアウト名がactivity_mainとなるデフォルトのMainActicityとして保存します。


    Finishをクリックし、新しいプロジェクトがmain activity classと一緒に作られます。


    パッケージ名とActicityFacebookに登録
    この時点で、Facebook DeveloperサイトのApp Dashboardに戻り、Androidの設定部分Android アプリのパッケージとActivity名を入力します。またFacebook LoginEnabledにします。




    SDKプロジェクトのリンクとFacebook アプリIDの設定

    Eclipseに戻り、SDK libraryプロジェクトと新しいアプリをリンクさせます。プロジェクトのPropertiesを開きAndroidを選びます。ダイアログの下の方にある'Add'を選択し、FacebookSDKプロジェクトをworkspaceから選択します。

    プロジェクトの中にFacebook app IDを加えるには、プロジェクトのres/valuesフォルダのstrings.xmlを開きます。前のステップで手に入れたIDをString型のapp_idに加える必要があります。

    (string.xmlファイルを見てみると、ドキュメントの中に
    <string name="app_id">497906953566757</string>
    という内容が書き込まれているはずです。)

    続いてプロジェクトのルーツの中のAndroidManifest.xmlを開きます。android.permission.INTERNET:という'uses permission'を加えます。


    また、'Application' タブでは、先ほど設定したstring型app_idを持った com.facebook.sdk.ApplicationIDと呼ばれるmeta-data itemを加える。

    最後にアプリにcom.facebook.LoginActivityという名前で新しいActivityを加えます。

    (ソースファイルを見てみると、以下のようなXMLに新しいコードが加わっているはずです。)
    <uses-permission android:name="android.permission.INTERNET"/> 
    <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/app_id"/>
    <activity android:name="com.facebook.LoginActivity">


    最低限実行可能なソーシャルアプリ
    最初にこのプロジェクトを作成すると、res/layout にある activity_main.xmlで単純なユーザインタフェースが作成されます。そのファイルを開き、設計画面の真ん中でtext labelを選択し、@+id/welcom のIDを付けます。


    レイアウトのソースコードを見ると以下のようになっているはずです。
    ...
    <TextView
        android:id="@+id/welcome"
        android:layout_width="wrap_content"
        ...
    この参照によって、ユーザがFacebookにLoginしたときに、ユーザ名を含んだwelcomeテキストがアップデートされるようにします。srcフォルダのMainActivity.javaを最後に開きます。

    まず、MainActivityの親クラスをActivityからFacebookActivityに変更し、import android.app.Activitycom.facebook.FacebookActivityに変えます。

    import android.app.Activity;
    import com.facebook.FacebookActivity;
    
    public class MainActivity extends Activity {
    public class MainActivity extends FacebookActivity {
    ...
    次にonCreateメソッドの最後に、Facebook loginを開始するためのthis.openSession()を呼びます。
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      this.openSession();
    }
    一端ユーザがLoginすると、activityのonSessionStateChangeメソッドが呼ばれます。 ユーザ名のフェッチやwelcome メッセージのアップデートにはこのメソッドを使うことができます。以下メソッドを加えます。
    @Override
    protected void onSessionStateChange(SessionState state, Exception exception) {
      // user has either logged in or not ...
    }
    このコールバックの中で、オープンセッションを作成するためにユーザがLoginしているかチェックし、ユーザの基本情報をフェッチするためのFacebook Graph API の /me endpointのコールを作る必要があります。onCompletedと呼ばれるコールバックを持つRequest.newMeRequest(Session, Callback)でリクエストを作ることによってこれを行います。
    この中で、ユーザ名と一緒にTextViewのラベル(先ほどwelcome IDとしたもの)を置き換えます。設定したリクエストを実行することによってこのメソッドを完成させます。

    @Override
    protected void onSessionStateChange(SessionState state, Exception exception) {
      // user has either logged in or not ...
      if (state.isOpened()) {
        // make request to the /me API
        Request request = Request.newMeRequest(
          this.getSession(),
          new Request.GraphUserCallback() {
            // callback after Graph API response with user object
            @Override
            public void onCompleted(GraphUser user, Response response) {
              if (user != null) {
                TextView welcome = (TextView) findViewById(R.id.welcome);
                welcome.setText("Hello " + user.getName() + "!");
              }
            }
          }
        );
        Request.executeBatchAsync(request);
      }
    }
    この抜粋はFacebook SDK(TextView widget classを含む)から追加した多くのクラスを使うので追加のimport宣言が必要になります。

    import com.facebook.GraphUser;
    import com.facebook.Request;
    import com.facebook.Response;
    import com.facebook.SessionState;
    import android.widget.TextView;
    全てのファイルを保存して実行します。アプリはすぐにFacebook Login flowを始めます。( もしエミュレータにAPKがインストールされていればFacebook アプリを通して行われます。もしされていなければウェブダイアログを通して行われます。) Loginが成功するとユーザ名が表示されます。






    これでソーシャルAndroidアプリを構築することができました。Facebook SDKを更に深く理解し、追求する準備が整いました。
    最後にMainActivity.javaの完成コードは以下のとおりです。


    package com.firstandroidapp;
    
    import android.os.Bundle;
    import android.view.Menu;
    import android.widget.TextView;
    import com.facebook.FacebookActivity;
    import com.facebook.GraphUser;
    import com.facebook.Request;
    import com.facebook.Response;
    import com.facebook.SessionState;
    
    public class MainActivity extends FacebookActivity {
    
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          this.openSession();
      }
    
      @Override
      protected void onSessionStateChange(SessionState state, Exception exception) {
        // user has either logged in or not ...
        if (state.isOpened()) {
        // make request to the /me API
          Request request = Request.newMeRequest(this.getSession(), new Request.GraphUserCallback() {
            // callback after Graph API response with user object
            @Override
            public void onCompleted(GraphUser user, Response response) {
              if (user != null) {
                TextView welcome = (TextView) findViewById(R.id.welcome);
                welcome.setText("Hello " + user.getName() + "!");
              }
            }
          });
          Request.executeBatchAsync(request);
        }
      }
    
    }