Android C2DM どこまで無料なのか

Android

Android C2DM どこまで無料なのか

Androidアプリには、C2DM(Cloud to Device Messaging)というプッシュ通知を簡単に実装できる仕組みがあります。

C2DMの利用には申し込みが必要で、下記から申し込みます。
https://developers.google.com/android/c2dm/signup?hl=ja

申し込むとメールが来ます。
そのメールや、申し込む間にも

By default, all new sender accounts are granted an initial production-level quota as documented in:

http://code.google.com/android/c2dm/quotas.html

If you need a higher quota, please follow the instructions on that page to submit a quota request.

と、とにかくこれは開発用だとか、テスト用だとか、相当小さいアプリで利用するだけの割りあて(quota)しかないよ、という念押しがかなりされます。

開発用って、どこまで開発用なの??
という疑問が当然出ますが、具体的な数字は
https://developers.google.com/android/c2dm/quotas?hl=ja
に載っています。

1日に200,000メッセージまで、です。

ほっ。
では200,000メッセージを超えたらいくらなのか、というのは公開されていません。
上記のページから、Googleへ問い合わせする必要があるようです。

ところで、余談ですが、クォータというのはなかなか普段聞きなれないですよね。
昔社会科の資料で、明治の縫製工場で働く野麦峠のような話で
「給料がクォータされる」
という言葉を思い出します。。。

ちなみにC2DMは2012年6月26日にデプリケイトされ、GCM(Google Cloud Messaging)というものに移行するそうです。(>_<) これからはGCMを勉強しましょう! http://developer.android.com/guide/google/gcm/index.html

C2DM has been replaced by Google Cloud Messaging for Android

Android Backキーをプログラム的に実装する

Android

Android Backキーをプログラム的に実装する

Android Back キー プログラム 
とかで検索すると、大体がBackキーの動作をプログラム的に変えよう、という話が多いんですが、私が調べたかったのは、何かのボタンをタップで、Backボタンと同じ動作をさせようということです。

で、調べて超簡単だったので書いておきます。

 //例として、アラートダイアログのキャンセルボタンをタップでBackさせる場合
 dlg.setNegativeButton(getString(R.string.cancel),
       new DialogInterface.OnClickListener() {
 
           @Override
           public void onClick(DialogInterface dialog, int which) {
                      onBackPressed(); 
                    }
                });
 
 //これがBackボタンの操作
 @Override
 public void onBackPressed() {
        
        super.onBackPressed();   
 
 }

Android AsyncTaskのテストを行う

Android

Android AsyncTaskのテストを行う
で検索すると、いろいろと記事が出てきますが、自分でも書いておいたほうがわかりやすいので、書いておきます。

 //テスト対象のアクティビティ
 public class OfferDetailActivity extends UtilsActivity implements
        PostJsonTask.PostJsonTaskCallback, View.OnClickListener {
 
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.offer_detail);
 
    context = this;
  
    user = getIntent().getParcelableExtra("user");
 
    Bundle extras = getIntent().getExtras();
 
    getmOfferId(extras);
 
    if (mOfferId != null) {
 
            HashMap<String, String> data = user.makeDataMap();
            String jsonToServer = new Gson().toJson(data);
            PostJsonTask post_json_task = new PostJsonTask(context, jsonToServer, this);
 
            post_json_task.execute(AppConstants.OFFER_DETAIL_URL + "offer_id=" + mOfferId);
 
       } else {
            Toast.makeText(getApplicationContext(), "エラー パラメーターがありません", Toast.LENGTH_LONG).show();
        }
    }
 //テスト対象のAsyncTask
 public class PostJsonTask extends AsyncTask<String, Void, String> {
 
 
    final String TAG = "PostJsonTask";
    Context localContext;
    private String jsonData = null;
    private PostJsonTaskListener mListener;
    private Exception mError = null;
 
    public PostJsonTask(Context context, String data, PostJsonTaskCallback _callback) {
        localContext = context;
        jsonData = data;
        this.callback = _callback;
    }
 
    private PostJsonTaskCallback callback;
 
    //Activiyへのコールバック用interface
    public interface PostJsonTaskCallback {
 
        void postJsonPreExecute();
        void postJsonPostExecute(String result);
        void postJsonCancel();
 
    }
 
   //テストコードのためだけに利用
    public PostJsonTask(Context context, String data) {
        // Required by the semantics of AsyncTask
        super();
 
        this.localContext = context;
    }
 
    @Override
    public void onPreExecute(){
 
        super.onPreExecute();
        if(callback != null) {
            callback.postJsonPreExecute();
        }
 
    }
 
    @Override
    public String doInBackground(String... params) {
 
        String result = null;
 
        try {
            result = getJson(params[0]);
        } catch (RuntimeException e){
            mError = e;
        }
 
        return result;
 
    }
 
    private String getJson(String address){
        try {
            URL url = new URL(address);
            URLConnection conn = url.openConnection();
            conn.setRequestMethod("POST");
            conn.setInstanceFollowRedirects(false);
            conn.setRequestProperty("Accept-Language", "jp");
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
            OutputStream os = conn.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.print(jsonData);
            ps.close();
  
            StringBuffer sb = new StringBuffer();
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line = null;
            while ((line = br.readLine())!= null){
                sb.append(line);
            }
 
            br.close();
            return sb.toString();
        } catch (MalformedURLException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("Invalid URL");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Network error");
        }
    }
 
 
    @Override
    public void onPostExecute(String result) {
 
        super.onPostExecute(result);
 
        Log.d(TAG, result);
 
        if(callback != null) {
            callback.postJsonPostExecute(result);
        }
 
        if (this.mListener != null) {
            this.mListener.onComplete(result, mError);
        }
 
    }
 
    @Override
    public void onCancelled() {
 
        super.onCancelled();
        if(callback != null) {
            callback.postJsonCancel();
        }
 
        if (this.mListener != null) {
            mError = new InterruptedException("AsyncTask cancelled");
            this.mListener.onComplete(null, mError);
        }
    }
 
    //テストのために追加
     public PostJsonTask setListener(PostJsonTaskListener listener) {
        this.mListener = listener;
        return this;
    }
 
   public static interface PostJsonTaskListener {
        public void onComplete(String jsonString,  Exception e);
    }
 
 }
 //テストコード
 public class GetOfferDetailJSONTest extends ActivityInstrumentationTestCase2 {
 
    String mJsonString = null;
    Exception mError = null;
    CountDownLatch signal = null;
    private Activity mActivity;
    private int mOfferId;
    private User mUser;
    private int TEST_OFFER_ID = 107;
 
 
    public GetOfferDetailJSONTest() {
        super(OfferDetailActivity.class);
    }
 
    @Override
    protected void setUp() throws Exception {
 
        signal = new CountDownLatch(1);
 
        mUser = new User.Builder("2", "sakurai", "123456", "", "", "", "", "", "","", 0, 0)
                 .build();
        mOfferId = TEST_OFFER_ID;
 
        mActivity = getActivity();
 
   }
 
    @Override
    protected void tearDown() throws Exception {
 
        signal.countDown();
    }
 
    public void testPreConditions(){
 
        assertNotNull(mActivity);
        assertNotNull(mOfferId);
        assertNotNull(mUser);
 
    }
 
    public void testPostJsonTask() throws InterruptedException {
 
        HashMap<String, String> user_info = mUser.makeDataMap();
 
        Context context = mActivity.getApplicationContext();
        String jsonToServer = new Gson().toJson(user_info);
 
       String url = AppConstants.OFFER_DETAIL_URL + "offer_id=" + mOfferId;
        PostJsonTask task = new PostJsonTask(context, jsonToServer);
        task.setListener(new PostJsonTask.PostJsonTaskListener() {
            @Override
            public void onComplete(String jsonString, Exception e) {
                mJsonString = jsonString;
                mError = e;
                signal.countDown();
            }
        }).execute(url);
        signal.await();
 
        assertNull(mError);
        assertFalse(TextUtils.isEmpty(mJsonString));
 
        assertTrue(mJsonString.startsWith("{    \"res\""));
        assertTrue(mJsonString.endsWith("}"));
 
    }
 }

下記のブログを参考にさせて頂きました。m(_ _)m

http://marksunghunpark.blogspot.jp/2015/05/how-to-test-asynctask-in-android.html

ちなみに、はまっちゃったのはPostJsonTaskから、テストを実行すると何も帰ってこないということ。
理由はわかりませんでした。
しかし、すでにデプリケイトされたDefaultHttpClientを下記のように使っていたんですが

 HttpClient client = new DefaultHttpClient();
 HttpPost post = new HttpPost(params[0]);// in this case, params[0] is URL
 try {
              // set up post data
              StringEntity se = new StringEntity(jsonData, "UTF-8");
               post.setEntity(se);
               post.setHeader("Accept", "application/json");
              post.setHeader("Content-Type", "application/json");

 //あとは省略

これをやめて、上記のように

    URL url = new URL(address);
    URLConnection conn = url.openConnection();
 
    StringBuffer sb = new StringBuffer();
    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

に変更したら、ちゃんと結果を受け取れるようになりました。

Android Activityからstrings.xmlの値を呼び出す

Android

Android Activityからstrings.xmlの値を呼び出す

簡単なことですけどね、いつも忘れてしまうのでメモです。

ActivityなどのJavaからstrings.xmlにある値を呼び出すのは

 getString(R.string.string_name);

です。

レイアウトのxmlからstrings.xmlの値を呼び出すのは

 @string/string_name

です。こっちはよく使いますよね。

Activity以外からstrings.xmlの値を呼びだす

Android ActionBarのアイコンをなくす

Android

Android ActionBarのアイコンをなくす

AndroidのminSDKが11以上で使える話です。
10以下の場合は、AppCompatとか取り込まないとダメなようです。

さて、ActionBarのアイコンをなくしたデザインにしたい!というお話がありました。
具体的には、次のような画像にします。

で、stackOverflowに
action_bar.setDisplayShowHomeEnabled(false);
を使えばイイヨ!と書いてあったので、次のように実装してみました。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ActionBar action_bar = getActionBar();
        action_bar.setDisplayShowHomeEnabled(false);        
 
    }

いいんだけど…

一瞬アイコンが見えるのがとても残念です…。

仕方ないので、カスタムレイアウトにします。
ActivityのonCreateでactionbarを指定します。

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ActionBar action_bar = getActionBar();
        
        action_bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); 
        action_bar.setCustomView(R.layout.actionbar);
 
    }

次のようにlayout/actionbar.xmlを作ります。

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_gravity="center"
    android:background="@color/navy" >
    
 <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="もももも"
    android:textColor="@color/white"
    android:textSize="24sp" />
 
 </LinearLayout>

下記を参考にしました。
http://stackoverflow.com/questions/18418635/how-to-align-title-at-center-of-actionbar-in-default-themetheme-holo-light