NIFTY Cloud Advent Calendar 2015の最終日が空いていたので、せっかくなので書こうと思います。
以前、撮影した画像を自動でmBaaSに保存するカメラアプリに関する記事を書きましたが、その後新しいSDKが出ていたので、今回はそのSDKを使って同様のアプリを作っていきたいと思います。
※そもそもmBaaSとは?→http://mb.cloud.nifty.com/about.htm
流れは以下の通り
(事前準備)mBaaSへの無料登録→http://mb.cloud.nifty.com/
- 普通のカメラアプリの作成
- mBaaSとの連携
- 画像の保存部分の実装
カメラアプリの作成
今回も以下のサイトのコードを参考にさせていただいてベースのアプリを作成していきます。
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と書き方を変えずに使えそうです。
どの辺りが違うのか、もう少し深堀りしてみたいと思います。
今回は以上です。