Help us understand the problem. What is going on with this article?

撮影した画像を自動でクラウドに保存するアプリを新SDKで作ってみた【ソースコード有】

More than 3 years have passed since last update.

NIFTY Cloud Advent Calendar 2015の最終日が空いていたので、せっかくなので書こうと思います。

以前、撮影した画像を自動でmBaaSに保存するカメラアプリに関する記事を書きましたが、その後新しいSDKが出ていたので、今回はそのSDKを使って同様のアプリを作っていきたいと思います。
※そもそもmBaaSとは?→http://mb.cloud.nifty.com/about.htm

流れは以下の通り
(事前準備)mBaaSへの無料登録→http://mb.cloud.nifty.com/
1. 普通のカメラアプリの作成
2. mBaaSとの連携
3. 画像の保存部分の実装

カメラアプリの作成

今回も以下のサイトのコードを参考にさせていただいてベースのアプリを作成していきます。
http://qiita.com/fslasht/items/be41e84cfbc4bbb91af7

この部分は前回と違いがないので、前回の記事を参考にして実装してください。

実装時の注意点

今回の実装でのプチハマりポイントは以下の通り。

  • android.support.v4.app.Fragmentとandroid.support.v7.app.AppCompatActivityのインポートでエラーが出る  →何故かAndroidSupportRepositoryがインストールされていない状態になっていた。
  • Rが生成されない  →大抵マニフェストファイル辺りでエラーが出ていることが多いので調べると、なんか出てました。   コピペして調べたところ、build gradleのcompileSdkVersionが古いと言われたので、22から23にしたら解消された。

諸々のバージョンアップで少しエラーが出ましたが、完成したソースコードは基本的に前回と同じです。

mBaaSとの連携

はじめにGithubリリースページのZIPファイルをダウンロード・解凍し、中にあるNCMB.Jarをプロジェクトに追加します。

追加の仕方

  • app/libsフォルダにNCMB.jarをコピーします。(ファイル一覧上部のプルダウンが"android"になっていると目的のフォルダが見えないことがあるので、その場合は"project"に変更してください。)
  • app/build.gradleファイルのdependances内に以下を追加します。
    compile 'com.google.code.gson:gson:2.3.1'
    compile files('libs/NCMB.jar')
  • AndroidManifest.xmlのタグの直前に以下のpermissionを追加します。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • MainActivity.javaのonCreateメソッド内に以下のコードを追加します。
NCMB.initialize(this,"APP_KEY","CLIENT_KEY");
  • 上記コードの"APP_KEY"と"CLIENT_KEY"にmBaaSアプリの「アプリケーションキー」と「クライアントキー」を書き込みます。ダッシュボードを開き、左メニューで「アプリ設定」を選択→「基本」の項目にAキーが表示されているのでコピーして文字列として置き換えてください。

これで連携は完了です。

画像保存部分の実装

MainActivity.javaの画像をSDカードに保存する部分を、以下のファイルストアに保存するコードに置き換えます。

NCMBFile file = new NCMBFile("picture.jpg", data, new NCMBAcl());
file.saveInBackground(new DoneCallback() {
                            @Override
                            public void done(NCMBException e) {
                                if (e != null) {
                                    //失敗
                                } else {
                                    //成功
                                }
                            }
                        });

これによってカメラから取得されたdataが”picture.jpg”としてファイルストアに保存されます。
ただし、このままだとファイル名が固定で複数の画像を保存できないため、時間によってファイル名が設定されるようにして、mBaaS連携部分と合わせて書き換えたMainActivity.javaは以下の通りです。

package com.example.USERNAME.ncmbcamerav2;

import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;

import com.nifty.cloud.mb.core.DoneCallback;
import com.nifty.cloud.mb.core.NCMB;
import com.nifty.cloud.mb.core.NCMBAcl;
import com.nifty.cloud.mb.core.NCMBException;
import com.nifty.cloud.mb.core.NCMBFile;

import java.io.FileOutputStream;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity {
    final static private String TAG = "NCMB Camera";

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

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction().add(R.id.container, new CameraFragment()).commit();
        }
        NCMB.initialize(this,"APP_KEY","CLIENT_KEY");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * カメラ撮影用フラグメント
     */
    public static class CameraFragment extends Fragment {

        // ------------------------------------------------------------
        // メンバー変数
        // ------------------------------------------------------------
        private Camera camera_;     // カメラインスタンス
        View rootView_;                 // ルートView
        SurfaceView surfaceView_;       // プレビュー用SurfaceView

        // ------------------------------------------------------------
        // リスナー
        // ------------------------------------------------------------

        // Surfaceリスナー
        private SurfaceHolder.Callback surfaceListener_ = new SurfaceHolder.Callback() {
            // Surface作成
            public void surfaceCreated(SurfaceHolder holder) {
                // カメラインスタンスを取得
                camera_ = Camera.open();
                try {
                    camera_.setPreviewDisplay(holder);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            // Surface破棄時
            public void surfaceDestroyed(SurfaceHolder holder) {
                // カメラインスタンス開放
                camera_.release();
                camera_ = null;
            }

            // Surface変更時
            // プレビューのパラメーターを設定し、プレビューを開始する
            public void surfaceChanged(SurfaceHolder holder, int format,    int width, int height) {
                Log.d(TAG, "surfaceChanged width:" + width + " height:" + height);

                Camera.Parameters parameters = camera_.getParameters();

                // デバッグ用表示
                Size size = parameters.getPictureSize();
                Log.d(TAG, "getPictureSize width:" + size.width + " size.height:" + size.height);
                size = parameters.getPreviewSize();
                Log.d(TAG, "getPreviewSize width:" + size.width + " size.height:" + size.height);

                // プレビューのサイズを変更
                // parameters.setPreviewSize(width, height);    // 画面サイズに合わせて変更しようとしたが失敗する
                parameters.setPreviewSize(640, 480);            // 使用できるサイズはカメラごとに決まっている

                // パラメーターセット
                camera_.setParameters(parameters);
                // プレビュー開始
                camera_.startPreview();
            }
        };

        // シャッターが押されたときに呼ばれるコールバック
        private Camera.ShutterCallback shutterListener_ = new Camera.ShutterCallback() {
            public void onShutter() {
            }
        };

        // JPEGイメージ生成後に呼ばれるコールバック
        private Camera.PictureCallback pictureListener_ = new Camera.PictureCallback() {
            // データ生成完了
            public void onPictureTaken(byte[] data, Camera camera) {
                if (data != null) {
                    FileOutputStream fos = null;
                    try {
                        // 日付と時間で数列を生成
                        Calendar calendar = Calendar.getInstance();
                        int year = calendar.get(Calendar.YEAR);
                        int month = calendar.get(Calendar.MONTH);
                        int day = calendar.get(Calendar.DAY_OF_MONTH);
                        int hour = calendar.get(Calendar.HOUR_OF_DAY);
                        int minute = calendar.get(Calendar.MINUTE);
                        int second = calendar.get(Calendar.SECOND);
                        String fileName = "" + year + (month + 1) + day + hour + minute + second + ".jpg";
                        NCMBFile file = new NCMBFile(fileName, data, new NCMBAcl());
                        file.saveInBackground(new DoneCallback() {
                            @Override
                            public void done(NCMBException e) {
                                if (e != null) {
                                    //失敗
                                } else {
                                    //成功
                                }
                            }
                        });

                    }catch (Exception e){
                        System.out.println("error in saveInBackground");
                        e.printStackTrace();

                    }

                    // プレビューを再開する
                    camera.startPreview();
                }
            }
        };

        // 画面タッチ時のコールバック
        OnTouchListener ontouchListener_ = new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    if (camera_ != null) {
                        // 撮影実行
                        camera_.takePicture(shutterListener_, null, pictureListener_);
                    }
                }
                return false;
            }
        };

        // ------------------------------------------------------------
        // Fragment
        // ------------------------------------------------------------

        // Fragmentコンストラクタ
        public CameraFragment() {
        }

        // View作成
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
            // View作成
            rootView_ = inflater.inflate(R.layout.fragment_main, container, false);

            // View内のView取得
            surfaceView_ = (SurfaceView) rootView_  .findViewById(R.id.surface_view);

            // SurfaceHolder設定
            SurfaceHolder holder = surfaceView_.getHolder();
            holder.addCallback(surfaceListener_);
            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

            // タッチリスナー設定
            rootView_.setOnTouchListener(ontouchListener_);

            return rootView_;
        }
    }
}

ファイルをアップロードするだけであれば、ほとんど以前のSDKと書き方を変えずに使えそうです。
どの辺りが違うのか、もう少し深堀りしてみたいと思います。

今回は以上です。

FukMo10
データ活用支援業をしつつ、エンジニアリングマネジメント界隈をうろうろしています。
fjct
クラウド・IoT 関連サービスを開発・提供している企業です。(こちらは、富士通クラウドテクノロジーズの有志にて運営しております。)
https://fjct.fujitsu.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away