2013年11月28日木曜日

アプリ内課金実装(IInAppBillingService.aidl, IabException)

IInAppBillingServiceのライブラリを実装しました。

前回までのIMarketBillingService.aidlを使っていたのですが、
version3にアップデートしろと言われ仕方なくアップデート。。


アップデートした感想を先にいうと、
全体的に便利になっています。

前回は自分で準備した処理も
既に入っていたりと。。

IInAppBillingServiceを使っている人は今すぐにでも
アップデートしていいレベルだど思います。

また、新機能として、consumeという機能が付きました。
購入した商品を消費するという処理です。
この消費が正常に行われないと、ユーザーは商品を購入できません。
購入した商品はちゃんと、consumeの処理を走らせて、商品を消費させてください。

ではでは、課金機能を新規追加の人のためにも実装方法を説明します。
他のサイトでも書かれていると思いますが、サンプルソースを見ながら、自分のアプリに必要な
ソースをコピって、貼り付けてね。
とそんな感じになるんですが。。。。

■サンプルの取得
Android SDKマネージャーを立ち上げ。
play_billingをインストール

eclipseでplay_billingをインポート

■サンプルソースを見る前に覚えておくといい事
◇商品タイプ
Googleのアプリ内課金で購入できるアイテムには3つのタイプがあります。
(アイテム登録時に分ける必要あり)

・管理対象の商品
・管理対象外の商品
・定期購入

定期購入は何となくわかると思いますが、管理対象の商品と管理対象外の商品違いとは何か??
管理対象のアイテムは1度しか購入できない商品で、
管理対象外のアイテムは何度も購入できる商品の事です。

RPGで言うと、回復アイテムは管理対象外のアイテムで
鍵は管理対象のアイテムです。

何となくわかりますかね??

今回のgoogleのサンプルソースで言うと、
gasが管理対象外の商品
premiumが管理対象の商品
infinite_gasが定期購読
になります。

◇商品購入テストの仕方
パッケージ名がcom.exampleから、始まると購入テストはできません。
googleplayに実際にアプリを上げないとテストはできません。(アプリは非公開で)
また、googleplayにあげるので、本番で使う証明書でBuildするのがお勧めです。
developer consoleのアカウントの詳細でテスト用のアクセス権を設定すれば、テスト購入ができます。

ただ、そのテスト用のアクセス権を設定する際にテスト用に使いたい端末の初期設定時の
Gmailアドレスを設定しないといけないので注意して下さい。下手すると、端末を初期化しないといけなくなります。


■サンプルソースをよく読む
MainActivityがメイン処理です。
課金機能の流れとしては、

消費していないアイテムがあるかの確認

アイテムの購入

アイテムの消費

という風になっております。


■サンプルを見ても自分のアプリに必要なソースがわからない人のために
ちょっと、コメントアウトを噛みくだいて、MainActivitywを記述して見ました。

◇サンプルのサンプル
public class MainActivity extends Activity {
    //デバック用のログタイトル
    static final String TAG = "TrivialDrive";

    //管理されているアイテムを購入しているかのフラグ
    boolean mIsPremium = false;

    //定期購読アイテムを購入しているかのフラグ
    boolean mSubscribedToInfiniteGas = false;

    //developer consoleページ内のアプリ内アイテムで登録されているアイテムID
    //premiumは管理対象のアイテム
    //gasは管理対象外のアイテム
    //infinite_gasは定期購読商品のアイテム
    static final String SKU_PREMIUM      = "premium";
    static final String SKU_GAS          = "gas";
    static final String SKU_INFINITE_GAS = "infinite_gas";

    //onActivityResultで返ってくるときのrequest_code
    static final int RC_REQUEST = 10001;

    //画像配列
    static int[] TANK_RES_IDS = { R.drawable.gas0, R.drawable.gas1, R.drawable.gas2,
                                   R.drawable.gas3, R.drawable.gas4 };

    //管理対象外アイテムの購入可能回数
    static final int TANK_MAX = 4;

    //管理対象外アイテムの購入回数
    int mTank;

    //IabHelperオブジェクト
    IabHelper mHelper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //画面を設定
        setContentView(R.layout.activity_main);

        //ゲーム内容のロード
        loadData();

        //このアプリのライセンス キー 。バイナリに追加する Base64 エンコードの RSA 公開鍵
        String base64EncodedPublicKey = "CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE";

        //base64EncodedPublicKeyの変数をアプリライセンスキーに変更したかの判定
        if (base64EncodedPublicKey.contains("CONSTRUCT_YOUR")) {
            throw new RuntimeException("Please put your app's public key in MainActivity.java. See README.");
        }
     
        //パッケージ名がcom.exampleから、始まっていないかの判定(com.exampleのままだと購入テストはできません)
        if (getPackageName().startsWith("com.example")) {
            throw new RuntimeException("Please change the sample's package name! See README.");
        }

        //IabHelperオブジェクトの生成
        Log.d(TAG, "Creating IAB helper.");
        mHelper = new IabHelper(this, base64EncodedPublicKey);

        //ログ出力フラグをONにする
        mHelper.enableDebugLogging(true);

        //IabHelperのインスタンスの呼び出し
        //IabHelperが使用可能か非同期で通信して、通信完了後に呼ばれる
        Log.d(TAG, "Starting setup.");
        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            public void onIabSetupFinished(IabResult result) {
                Log.d(TAG, "Setup finished.");

                //IabHelperが使用不能なときに通る
                if (!result.isSuccess()) {
                    complain("Problem setting up in-app billing: " + result);
                    return;
                }

                // Have we been disposed of in the meantime? If so, quit.
                if (mHelper == null) return;

                //アイテム購入復元処理を実行(消費していないアイテムがあるかの判定処理が走る)
                Log.d(TAG, "Setup successful. Querying inventory.");
                mHelper.queryInventoryAsync(mGotInventoryListener);
            }
        });
    }

    //消費してしないアイテムを判定完了時に呼ばれる
    IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
            Log.d(TAG, "Query inventory finished.");

            // Have we been disposed of in the meantime? If so, quit.
            if (mHelper == null) return;

            //購入が成功しているかの判定。購入画面から、購入しないで戻ってもここを通る
            if (result.isFailure()) {
                complain("Failed to query inventory: " + result);
                return;
            }

            Log.d(TAG, "Query inventory was successful.");

            /*
             * Check for items we own. Notice that for each purchase, we check
             * the developer payload to see if it's correct! See
             * verifyDeveloperPayload().
             */

            //管理対象のアイテム購入履歴があるかの判定
            Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
            mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
            Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));

            //定期購読アイテム購入履歴があるかの判定
            Purchase infiniteGasPurchase = inventory.getPurchase(SKU_INFINITE_GAS);
            mSubscribedToInfiniteGas = (infiniteGasPurchase != null &&
                    verifyDeveloperPayload(infiniteGasPurchase));
            Log.d(TAG, "User " + (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE")
                        + " infinite gas subscription.");
            if (mSubscribedToInfiniteGas) mTank = TANK_MAX;

            //消費されていない管理対象外のアイテムがあるかの判定
            Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
            if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
                Log.d(TAG, "We have gas. Consuming it.");
                mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);
                return;
            }

            updateUi();
            setWaitScreen(false);
            Log.d(TAG, "Initial inventory query finished; enabling main UI.");
        }
    };

    //管理対象外アイテム購入ボタン押下時
    public void onBuyGasButtonClicked(View arg0) {
        Log.d(TAG, "Buy gas button clicked.");

        //定期購読アイテム購入していたら、処理を抜ける
        if (mSubscribedToInfiniteGas) {
            complain("No need! You're subscribed to infinite gas. Isn't that awesome?");
            return;
        }
     
        //管理対象外アイテムの購入限度を超えていたら、処理を抜ける
        if (mTank >= TANK_MAX) {
            complain("Your tank is full. Drive around a bit!");
            return;
        }

        //Viewを購入画面に変更(これは地味に重要!!仕込むアプリにも是非導入しよう)
        setWaitScreen(true);
        Log.d(TAG, "Launching purchase flow for gas.");

        /* TODO: for security, generate your payload here for verification. See the comments on
         *        verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
         *        an empty string, but on a production app you should carefully generate this. */
        String payload = "";

        //アイテム購入フローが走る
        mHelper.launchPurchaseFlow(this, SKU_GAS, RC_REQUEST,
                mPurchaseFinishedListener, payload);
    }

    //管理対象アイテム購入ボタン押下時
    public void onUpgradeAppButtonClicked(View arg0) {
        Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
        setWaitScreen(true);

        /* TODO: for security, generate your payload here for verification. See the comments on
         *        verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
         *        an empty string, but on a production app you should carefully generate this. */
        String payload = "";

        mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST,
                mPurchaseFinishedListener, payload);
    }

    //定期購読商品を購入する時に流れる
    public void onInfiniteGasButtonClicked(View arg0) {
        if (!mHelper.subscriptionsSupported()) {
            complain("Subscriptions not supported on your device yet. Sorry!");
            return;
        }

        String payload = "";

        setWaitScreen(true);
        Log.d(TAG, "Launching purchase flow for infinite gas subscription.");
        //定期購読用の引数を渡すので注意
        mHelper.launchPurchaseFlow(this, SKU_INFINITE_GAS, IabHelper.ITEM_TYPE_SUBS,
                RC_REQUEST, mPurchaseFinishedListener, payload);
    }


    boolean verifyDeveloperPayload(Purchase p) {
        String payload = p.getDeveloperPayload();


        return true;
    }

    //購入フロー完了時に呼ばれる
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);

            // if we were disposed of in the meantime, quit.
            if (mHelper == null) return;

            if (result.isFailure()) {
                complain("Error purchasing: " + result);
                setWaitScreen(false);
                return;
            }
            if (!verifyDeveloperPayload(purchase)) {
                complain("Error purchasing. Authenticity verification failed.");
                setWaitScreen(false);
                return;
            }

            Log.d(TAG, "Purchase successful.");

            if (purchase.getSku().equals(SKU_GAS)) {
                // bought 1/4 tank of gas. So consume it.
                Log.d(TAG, "Purchase is gas. Starting gas consumption.");
                //購入アイテムの消費フローが走る
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);
            }
            else if (purchase.getSku().equals(SKU_PREMIUM)) {
                // bought the premium upgrade!
                Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
                alert("Thank you for upgrading to premium!");
                mIsPremium = true;
                updateUi();
                setWaitScreen(false);
            }
            else if (purchase.getSku().equals(SKU_INFINITE_GAS)) {
                // bought the infinite gas subscription
                Log.d(TAG, "Infinite gas subscription purchased.");
                alert("Thank you for subscribing to infinite gas!");
                mSubscribedToInfiniteGas = true;
                mTank = TANK_MAX;
                updateUi();
                setWaitScreen(false);
            }
        }
    };

    //購入したアイテムの消費に完了した時に呼ばれる
    IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
        public void onConsumeFinished(Purchase purchase, IabResult result) {
            Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);

            // if we were disposed of in the meantime, quit.
            if (mHelper == null) return;

            // We know this is the "gas" sku because it's the only one we consume,
            // so we don't check which sku was consumed. If you have more than one
            // sku, you probably should check...
            if (result.isSuccess()) {
                // successfully consumed, so we apply the effects of the item in our
                // game world's logic, which in our case means filling the gas tank a bit
                Log.d(TAG, "Consumption successful. Provisioning.");
                mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
                saveData();
                alert("You filled 1/4 tank. Your tank is now " + String.valueOf(mTank) + "/4 full!");
            }
            else {
                complain("Error while consuming: " + result);
            }
            updateUi();
            setWaitScreen(false);
            Log.d(TAG, "End consumption flow.");
        }
    };

    // Drive button clicked. Burn gas!
    public void onDriveButtonClicked(View arg0) {
        Log.d(TAG, "Drive button clicked.");
        if (!mSubscribedToInfiniteGas && mTank <= 0) alert("Oh, no! You are out of gas! Try buying some!");
        else {
            if (!mSubscribedToInfiniteGas) --mTank;
            saveData();
            alert("Vroooom, you drove a few miles.");
            updateUi();
            Log.d(TAG, "Vrooom. Tank is now " + mTank);
        }
    }

    // We're being destroyed. It's important to dispose of the helper here!
    @Override
    public void onDestroy() {
        super.onDestroy();

        // very important:
        Log.d(TAG, "Destroying helper.");
        if (mHelper != null) {
            mHelper.dispose();
            mHelper = null;
        }
    }

    // updates UI to reflect model
    public void updateUi() {
        // update the car color to reflect premium status or lack thereof
        ((ImageView)findViewById(R.id.free_or_premium)).setImageResource(mIsPremium ? R.drawable.premium : R.drawable.free);

        // "Upgrade" button is only visible if the user is not premium
        findViewById(R.id.upgrade_button).setVisibility(mIsPremium ? View.GONE : View.VISIBLE);

        // "Get infinite gas" button is only visible if the user is not subscribed yet
        findViewById(R.id.infinite_gas_button).setVisibility(mSubscribedToInfiniteGas ?
                View.GONE : View.VISIBLE);

        // update gas gauge to reflect tank status
        if (mSubscribedToInfiniteGas) {
            ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(R.drawable.gas_inf);
        }
        else {
            int index = mTank >= TANK_RES_IDS.length ? TANK_RES_IDS.length - 1 : mTank;
            ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(TANK_RES_IDS[index]);
        }
    }

    // Enables or disables the "please wait" screen.
    void setWaitScreen(boolean set) {
        findViewById(R.id.screen_main).setVisibility(set ? View.GONE : View.VISIBLE);
        findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE : View.GONE);
    }

    void complain(String message) {
        Log.e(TAG, "**** TrivialDrive Error: " + message);
        alert("Error: " + message);
    }

    void alert(String message) {
        AlertDialog.Builder bld = new AlertDialog.Builder(this);
        bld.setMessage(message);
        bld.setNeutralButton("OK", null);
        Log.d(TAG, "Showing alert dialog: " + message);
        bld.create().show();
    }

    void saveData() {

        /*
         * WARNING: on a real application, we recommend you save data in a secure way to
         * prevent tampering. For simplicity in this sample, we simply store the data using a
         * SharedPreferences.
         */

        SharedPreferences.Editor spe = getPreferences(MODE_PRIVATE).edit();
        spe.putInt("tank", mTank);
        spe.commit();
        Log.d(TAG, "Saved data: tank = " + String.valueOf(mTank));
    }

    void loadData() {
        SharedPreferences sp = getPreferences(MODE_PRIVATE);
        mTank = sp.getInt("tank", 2);
        Log.d(TAG, "Loaded data: tank = " + String.valueOf(mTank));
    }
}

2013年11月12日火曜日

メニューの表示(onCreateOptionsMenu())

Androidの特徴の一つであるメニューボタン。
iPhoneにはないAndroid特有のボタンです。
iPhoneだと、画面のどこかに設定ボタンとか作らないとダメですが、
Androidだと、このメニューボタンを実装すればいいので、わりと楽です。

メニューボタンの生成

@Override
public boolean onCreateOptionsMenu(Menu menu) {
}

Acticvityを継承していれば使えます。
このメソッドの中でメニューを作っていきます。

Menu.add(int, int, int, String);
          groupid, 識別ID, ボタン配置優先順位, ボタン名

addした数だけ、メニューにボタンが配置されます。
レイアウトは勝手に作ってくれるので、わりと簡単で便利です。

■サンプルソース
package com.example.manifest;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends Activity {
    //どのボタンが押されたかの識別用ID
    private final int Menu1 = Menu.FIRST;
    private final int Menu2 = Menu.FIRST + 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //適当にレイアウトを作成
        LinearLayout oLayout = new LinearLayout(this);
        setContentView(oLayout);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // メニューアイテムを追加します
        menu.add(Menu.NONE, Menu1, Menu.NONE, "メニュー1").setIcon(R.drawable.ic_launcher);
        menu.add(Menu.NONE, Menu2, Menu.NONE, "メニュー2");
        return super.onCreateOptionsMenu(menu);
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case Menu1:
                Toast.makeText(getApplicationContext(), "Menu1", Toast.LENGTH_SHORT).show();
                return true;
            case Menu2:
                Toast.makeText(getApplicationContext(), "Menu2", Toast.LENGTH_SHORT).show();
                return true;
        }
        return false;
    }

    //メニューボタンを押される度に呼ばれる
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        Log.d("メニュー","押された");
        return true;
    }

    //メニューが消える度に呼ばれる  
    @Override
    public void onOptionsMenuClosed (Menu menu) {
        super.onPrepareOptionsMenu(menu);
        Log.d("メニュー","閉じられた");
    }
}

■実行結果




メニュー画面が開くタイミングや閉じるタイミングのOverrideが既にあるのが、とても便利です。
また、サンプルソースで使っている定数ですが、
Menu.NONEが0
Menu.FIRSTが1
です。



2013年11月5日火曜日

Fileオブジェクト(getName(), getParent())

ファイルを扱う場合によく使うオブジェクトです。
フォルダを作ったり、存在しているかの確認等してくれます。

Fileオブジェクトの生成  ファイルのパズを指定
File fFile = new File( "C:\\test\\test.txt");

ファイルのパスはurlでも、ローカルでもどちらでも大丈夫です。

簡単なFileオブジェクトの使い方

■ファイル名の取得
fFile.getName()
→test.txt

■親ディレクトリの取得
fFile.getParent()
→C:\test

■ファイルかの判定
fFile.isFile()
→true or false

仮にFileオブジェクトで指定したファイルの場合には、
ファイルが実在しないと、trueになりません。

■ファイルが存在するのかの判定
fFile.exists();


基本的なところはこんな感じです。

Fileオブジェクト生成時に指定するパスをディレクトリーにした場合
のサンプルです。

■生成
File fParent = new File( fFile.getParent() );

■ディレクトリを作成する
fParent.mkdir()
→true or false
1つ上の階層のディレクトリが作成されていないとfalseになります。

■ディレクトリを作成する
fParent.mkdirs()
→true or false
マジ神レベル!!
上の階層がなくても作ってくれます。
また、Fileオブジェクト作成時にファイルを指定すると、その名前の
ディレクトリが作成されます。


Fileオブジェクトは上の方で書いたようにサーバー上のファイルも使えるので、
サーバー上のファイルをローカルに保存する際に、
ファイルがあるか?ディレクトリがあるか?の判定で便利です。

■ディレクトリ内のファイルを取得
fParent.listFiles()
→File配列

ディレクトリ内のファイルが配列になって、返ってきます。

■ファイル名の変更
fParent.renameTo(new File ( 変更先のファイル名 ) )
→true or false

ファイル名の変更します。
コピーではないので、元ファイルは消えます。
Linuxで言うmvみたいなものです。move。
なので、ファイルを移動させるメソッドとして利用できます。

また、指定するファイルはフルパスで。。。


本日のまとめ
■サンプルソース
import java.io.File;


public class main{

    public static void main(String [] args) {
        //対象のファイル
        String sPath = "C:\\test\\test.txt";

        //ファイルシステムの作成
        File fFile = new File( sPath );

        //ファイル名の取得
        System.out.println( fFile.getName() );

        //親ディレクトリの取得
        System.out.println( fFile.getParent() );

        //ファイルかディレクトリかの判定
        System.out.println( fFile.isFile() );

        //親ディレクトリのファイルシステム作成
        File fParent = new File( fFile.getParent() );

        //親ディレクトリが存在していなかったら作成
        if ( !fParent.exists() )System.out.println( fParent.mkdirs() );
    }
}

2013年11月1日金曜日

AndroidManifest.xml-01(Manifest.xml書き方)

ソースに関してはある程度はググれば調べられるけど、
マニフェストファイルについては、全然調べられない!!

っていう人を対象に、(自分もそうですが、)

AndroidManifest.xmlについて激基本的なところを説明したいと思います。


■サンプル

<manifest
  android:versioncode="1"
  android:versionname="1.0"
  package="com.example.manifest"
  xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-sdk android:minsdkversion="8" android:targetsdkversion="17">
  <application
    android:allowbackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    <activity
      android:label="@string/app_name"
      android:name="com.example.manifest.MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN">
        <category android:name="android.intent.category.LAUNCHER">
      </category></action></intent-filter>
    </activity>
  </application>
</manifest>


上記は
Android4.2のSDKで新規プロジェクトを立ち上げたときの初期のAndroidManifest.xmlです。
これについて説明していきます。


<?xml version="1.0" encoding="utf-8"?>
<manifest
 xmlns:android="http://schemas.android.com/apk/res/android"

この部分は決まった形です。
無心で毎回何も考えずに記述しましょう


  package="com.example.manifest"

ここで指定したパッケージ名がGoglePlayのURLになります。
また、ここで指定するパッケージに初期起動時のActivityクラスを配置するのが個人的には
オススメです。

  android:versionCode="1"
  android:versionName="1.0"

android:versionCodeは整数が必須!!
GooglePlayにアプリを上げる際の管理番号になります。
GooglePlayにあげる場合には必ず、最後にあげた番号よりも
多い数字にしないといけません。

android:versionNameはユーザーがGoglePlayから、みられるバージョン
1.2とか1.0.0.1とかが可能

  <uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />

minSdkVersionはアプリをインストールするのに必要な
AndroidAPIレベルの最低値です。
今回は8となっているので、Android 2.2未満の端末だと、
インストールできないことを示しています。

android:targetSdkVersion="17"はそのアプリをどのAndroidに
合わせるかを示しています。Build時に指定するバージョンと違い、
ザックリ言うと見え方をどのバージョンにするかの指定です。
ついでに言うと、このSdkVersionはなくても全然ビルドできます。


  <application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

applicationタグ内の説明になります。
この中にアクテビティの設定を記述したりします。

android:allowBackup="true"
クラウドにバックアップすることを許可します。
Googleアカウントに紐づいているアプリを管理するあの機能を使えるっていうとです。
特に設定しなくてもアプリは動きます。

android:icon="@drawable/ic_launcher"
どの画像ファイルをアイコンにするかの設定です。
ここでいうアイコンとは、端末内で確認できるアプリの
iPhoneと違い画像サイズについてはサイズの規定とかとくにありません。(推奨サイズとかはあるかもしれませんが。。。。)

android:label="@string/app_name"
タイトルラベルに記述されるアプリのタイトルです。
ホーム画面で表示されるタイトルにもなります。
また、文字列を直書きでも大丈夫です。

android:theme="@style/AppTheme"

targetSdkVersionが14以上から、使えます。
簡単に言うとWebのCSS見たいなもので、
styleに文字色とか背景とかを設定できます。

android:allowBackup、icon、label、theme
の4つも特に記述しなくても、動くっちゃ動きます。
ただ、アイコンやタイトルぐらいは設定しておいた方がいいとは自分は思います。


    <activity
      android:name="com.example.manifest.MainActivity"
      android:label="@string/app_name" >
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

android:name="com.example.manifest.MainActivity"
でActivityファイルのパスを指定します。
上で指定したパッケージ内なら".MainActivity"こんな書き方でも大丈夫です。
android:label="@string/app_name"アクティビティ内でのタイトルに表示されます。

      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>

<intent-filter>のこの部分は結構大事で、これがわかると本当にいろいろなことができるようになります。
これがiPhoneアプリとは違うAndroidの醍醐味的なところです。
だがしかし、結構難しいところでもあるので、さっくりと説明しますと、
他のアプリとの連携や他のアプリからの起動などを記述します。
例えば、カメラアプリから写真加工アプリを呼び出すみたいな。。。

で、今回書かれている内容をみると、
<action android:name="android.intent.action.MAIN" />
"android.intent.action.MAIN"で
データの受け渡しなしの初期起動アクティビティになります。
<category android:name="android.intent.category.LAUNCHER" />
こちらを記述することで、初期起動が可能なアクティビティとカテゴライズされます。



すんごい基本的なAndroidManifest.xmlについての説明は
以上になります。

AndroidManifest.xmlについてよくわからずに
いろいろな機能や広告SDKを組み込むとmanifestファイルが混沌して、
わけわかめーなファイルになってしまうので気を付けてください。