WebRTC(iOS上のブラウザでは使えません) で、いろいろアプリをテストする上での一つのツールとして、使ってない iPhone4S を WEBカメラにしてみて解った情報です。(Android は DroidCam のほうがいいので詳細は後日調べる予定 / クライアントソフトはこちら) このアプリ、USB は使えないですし、USB のWEBカメラは安いのがいくらでもあるので、自分の家の特定位置の監視カメラとして使う以外はメリットはあまりありません。(外部からのアクセスは無理です。外部からは、Android 系の IPカメラアプリとルータ設定でできるかもしれません:未確認) そもそも、古い忘れられたソフトだと思われるので、古い iPhone(iOS8.0以降) 以外で試してみようとは思わないほうがいいと思います。広告も結構大きいのが出て画面ふさぎますし (その間も WEBカメラとしては使えるので、定点監視カメラなら広告は関係ないです)。 EpocCam のダウンロードとインストール アプリの更新は、2015年10月17日よりされていません。インターネットで検索しても探すのに一苦労します。 こちらから PC 用のソフトをダウンロードしてインストールします。(自分の環境は Windows7 で、32ビットですが、ダウンロード時の選択はありませんでした) インストールオプションとして、Barcode Reader を同時にインストールしようとするので、チェックを外す必要があります。 インストール時にいろいろ警告が出ました。たぶん、インターネットアクセスに対しての信頼性の警告だと思いますが、何が起きてもかまわないテストマシンでやってるのでインストールを続行しました。気になる方は、この時点で使う事をあきらめたほうがいいと思います。また、自分以外の環境では警告が出ない可能性もあるので、なんとも言い切れません。 iPhoneアプリのほうは、クライアントの確認が終わってから、インストールして開けばいいです。(当然ですが、Wi-Fi が使える必要があります。USB が使えるような雰囲気がネットではありますが、どうも USB は対応をやめたようです) サービスが登録されて自動起動になるので注意 Bonjour は、Apple 系アプリ用のサービスなので、既に入っている可能性もあります。しかし、新たに入る場合だと、サービスは当然停止設定にして必要な時だけ起動します。 あと、開発元の Kinoni のサービスも自動起動されるので、停止設定にして必要な場合にのみ起動します。 付属のビュアーは必要無いので起動しないように 単なる、動作確認用です。起動していると肝心の WEB カメラの画像が劣化します。 ファイアーウォールの設定が必要な場合もあります こうなると専門知識無いとどうにもなりませんが、PC のファイヤーウォールの設定を行うか、ファイヤーウォールを止めてとりあえず動作確認するかです。 昔は USB で使えたであろう残骸 ソースを見ると USB の文字が残ってますが、表示内にはありません。検索用のキーワードにも残ってますし、適当にアップデートした感満載です。ま、でも USB で使うメリットは現在コスト的にほぼ無いので開発をやめるのは当然でしょう。Wi-Fi 使っても十分速いわけですし(ビュアー起動するとアウトですが) で、上のソース場所は表から見ると以下のようになっています。 ※ 何故か、Debut Video Capture software による動作確認となっています。 さて、実行画面 一番下の英文は、x で閉じれます。最初、雷マークが出てタッチすると、なんか説明みたいなのが出ます。矢印は反転指定で、昔は逆になったままだとかネットで見かけましたが、これで正しくできるようになっています。 通常運用では、以下のように上部に広告が出て、何かのタイミング( たぶん画面にユーザがアクセスした時 )で、画面いっぱいの広告が日本語で出たり英語で出たりします。基本、左上の x 閉じるのですが、中には広告が何枚も移り変わって最後にならないと閉じれない場合もあります。 だから、リアルタイムの WEB カメラには使えません 最後に、Google Chrome からの画面です ※ 玄関で定点カメラテスト
2016年10月28日
iPhone を EpocCam というアプリで Wi-Fi の WEBカメラにする場合のいろいろ知っておく事。
2016年10月25日
Firebase storage に画像をアップロードする。1) ギャラリーから、2) 実行中の画面 / Android
Firebase の環境とプロジェクトの作成方法 9.6.1 を使用する場合 エミュレータ : SDK Manager で最新に更新 実機 : Google Play 開発者サービスを最新に更新 Firebase ドキュメント : Android でファイルをアップロードする実行中の画面は、putBytes で、ギャラリーは putFile でアップロード可能です(putStream もあります)。Firebase storage のルールはテスト時のみ、allow write: if true; に変更して行いました。 Firebase リファレンス : StorageReference
public class MainActivity extends AppCompatActivity { private static final int READ_REQUEST_CODE = 42; private FirebaseStorage storage; private StorageReference storageRef; private StorageReference imageRef; private ProgressDialog progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ▼ テスト用のルール // service firebase.storage { // match /b/freebase-654b7.appspot.com/o { // match /{allPaths=**} { // allow read; // allow write: if true; // } // } // } // アップロード中のダイアログ progress = new ProgressDialog(MainActivity.this); storage = FirebaseStorage.getInstance(); storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/"); // 現在の画面をアップロード( View ) Button captureButton = (Button) MainActivity.this.findViewById(R.id.captureButton); captureButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // View から Bitmap 取得 View view = v.getRootView(); // 画面全体 view.setDrawingCacheEnabled(true); Bitmap cache = view.getDrawingCache(); Bitmap rootViewCapture = Bitmap.createBitmap(cache); view.setDrawingCacheEnabled(false); // 画像アップロード用パス決定 Calendar cal = Calendar.getInstance(); SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss"); String uploadImagePath = String.format("image/%s.png",sf.format(cal.getTime())); imageRef = storageRef.child(uploadImagePath); // byte[] に変換 ByteArrayOutputStream baos = new ByteArrayOutputStream(); rootViewCapture.compress(Bitmap.CompressFormat.PNG, 100, baos); byte[] data = baos.toByteArray(); // アップロード中の表示 progress.setProgressStyle(ProgressDialog.STYLE_SPINNER); progress.setMessage("画像をアップロードしています"); progress.show(); UploadTask uploadTask = imageRef.putBytes(data); uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { progress.dismiss(); Log.i("lightbox","アップロードに成功しました"); long size = taskSnapshot.getMetadata().getSizeBytes(); Log.i("lightbox",String.format("サイズ : %d",size)); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { progress.dismiss(); Log.i("lightbox","アップロードに失敗しました"); } }); } }); // ギャラリーの画像をアップロード Button galleryButton = (Button) MainActivity.this.findViewById(R.id.galleryButton); galleryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); startActivityForResult(intent, READ_REQUEST_CODE); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // ギャラリーからの戻り if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { Uri uri = null; if (data != null) { // Uri が戻されます uri = data.getData(); Log.i("lightbox", "Uri: " + uri.toString()); // 画像アップロード用パス決定 ContentResolver contentResolver = MainActivity.this.getContentResolver(); Cursor cursor = contentResolver.query(uri, null, null, null, null); cursor.moveToFirst(); // とりあえず固定( 2 => _display_name ) String name = cursor.getString(2); Log.i("lightbox", "FileName: " + name); //この カーソルの詳細 String[] columnName = cursor.getColumnNames(); for(int i = 0; i < columnName.length; i++) { Log.i("lightbox", String.format("%d : %s : %s",i, columnName[i], cursor.getString(i))); } String uploadImagePath = String.format("image/%s",name); imageRef = storageRef.child(uploadImagePath); // アップロード中の表示 progress.setProgressStyle(ProgressDialog.STYLE_SPINNER); progress.setMessage("画像をアップロードしています"); progress.show(); // 画像アップロード UploadTask uploadTask = imageRef.putFile(uri); uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { progress.dismiss(); Log.i("lightbox","アップロードに成功しました"); long size = taskSnapshot.getMetadata().getSizeBytes(); Log.i("lightbox",String.format("サイズ : %d",size)); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { progress.dismiss(); Log.i("lightbox","アップロードに失敗しました"); } }); } } } }
build.gradle(Project)
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.2' classpath 'com.google.gms:google-services:3.0.0' } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
build.gradle(Module: app)
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "23.0.3" defaultConfig { applicationId "lightbox.sep.fire1" minSdkVersion 19 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.google.firebase:firebase-core:9.6.1' compile 'com.google.firebase:firebase-storage:9.6.1' compile 'com.google.firebase:firebase-auth:9.6.1' } apply plugin: 'com.google.gms.google-services'
※ AndroidManifest.xml には、android.permission.INTERNET を設定
Android : 画像関連のテスト用カメラアプリ
カメラそのものをどうこうしたいのでは無く、他の機能(Firebase storage 等)との連携テストに使いたいので、いわゆる旧式のカメラ API を使用しています。 ▼ Android Studio では 『非推奨』と言われます また、撮影は AndroidManifest.xml で縦の portrait 固定にして行う為、実際に横にした時の判定にセンサを使い、縦の時は 90度回転して画像を保存しなおしています。テストの実機は 4.4.4 のみですが、そもそもテスト用なので全ての対応はソース次第で好きに変更できます。 いろいろサンプルを探したのですが、全方向に対応したものが無かったので機能を集めました。 ※ センサの判定はかなり適当です MyCamera クラス
public class MyCamera implements SurfaceHolder.Callback { // 古いカメラAPI。カメラで何かしたいわけでは無いのでこれで実装 private Camera camera; private SurfaceHolder holder; private Camera.Size size; private MainActivity mainActivity; private SurfaceView cameraView; private List<Camera.Size> sizeList; public MyCamera(Context context) { Log.i("lightbox", "コンストラクタ"); this.mainActivity = (MainActivity) context; // MainActivity の画面のSurfaceView を使う cameraView = (SurfaceView)mainActivity.findViewById(R.id.cameraView); // SurfaceView から holder を取り出して、カメラに渡す holder = cameraView.getHolder(); // この中でイベント処理を行う holder.addCallback(MyCamera.this); } public Camera getCamera() { return camera; } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i("lightbox", "開く"); // カメラを開く camera = Camera.open(); try { // カメラに holder を渡す camera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } // 縦固定(AndroidManifest.xml) で、portrait 撮影を前提 camera.setDisplayOrientation(90); // カメラの情報 Camera.Parameters params = camera.getParameters(); // サポートされているサイズの一覧 sizeList = params.getSupportedPreviewSizes(); // 対応しているサイズの先頭のサイズ size = sizeList.get(0); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 縦固定(AndroidManifest.xml) なので一度しか通らない Log.i("lightbox", "開始"); // 縦横サイズの設定 Camera.Parameters params = camera.getParameters(); // ギャラリーでは縦になるが、保存画像は横 params.setRotation(90); Camera.Size optimalSize = getOptimalPreviewSize(sizeList,width,height); params.setPreviewSize(optimalSize.width,optimalSize.height); camera.setParameters(params); camera.startPreview(); } // http://qiita.com/zaburo/items/b5d3815d3ec45b0daf4f private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio=(double)h / w; if (sizes == null) return null; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; for (Camera.Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("lightbox", "終了"); // プレビュー終了 if ( camera != null ) { camera.stopPreview(); camera.release(); camera = null; } } }
MainActivity
public class MainActivity extends AppCompatActivity implements SensorEventListener { private String imagePath; private MyCamera camera; private SensorManager sensor; private Sensor acc; private boolean portrait; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); camera = new MyCamera(MainActivity.this); sensor = (SensorManager)getSystemService(SENSOR_SERVICE); acc = sensor.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); sensor.registerListener(MainActivity.this,acc,SensorManager.SENSOR_DELAY_NORMAL); portrait = true; // ************************************* // ギャラリーに保存 // ************************************* Button galleryButton = (Button) MainActivity.this.findViewById(R.id.galleryButton); galleryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i("lightbox", "クリック"); Camera.Parameters params = camera.getCamera().getParameters(); if ( portrait ) { // ギャラリーでは縦になるが、保存画像は横 params.setRotation(90); } else { // ギャラリーで横、保存画像も横 params.setRotation(0); } camera.getCamera().setParameters(params); // 撮影 camera.getCamera().takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { if (data != null) { // ギャラリー用に内部ストレージにフォルダを作成 String firebaseImageDir = Environment.getExternalStorageDirectory().getPath() + "/firebase"; File file = new File(firebaseImageDir); // ディレクトリ初期作成 if (!file.exists()) { if (file.mkdir() == false) { Log.i("lightbox", "ディレクトリを作成できませんでした"); return; } } // ギャラリー用画像保存パス Calendar cal = Calendar.getInstance(); SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss"); imagePath = firebaseImageDir + "/" + sf.format(cal.getTime()) + ".jpg"; FileOutputStream jpg; try { jpg = new FileOutputStream(imagePath); if ( portrait ) { // 縦の場合、回転して保存する Bitmap bmp1 = BitmapFactory.decodeByteArray (data, 0, data.length); int width = bmp1.getWidth(); int height = bmp1.getHeight(); Matrix matrix = new Matrix(); matrix.postRotate (90); Bitmap bmp2 = Bitmap.createBitmap (bmp1, 0, 0, width, height, matrix, true); bmp2.compress(Bitmap.CompressFormat.JPEG,90,jpg); } else { jpg.write(data); jpg.close(); } // ギャラリーに反映 MediaScannerConnection.scanFile( MainActivity.this, new String[] { imagePath }, new String[] { "image/jpeg" }, null); } catch (Exception e) { e.printStackTrace(); } // イベントの引数のカメラ camera.startPreview(); } } }); } }); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { // センサで実際の縦と横を判定 if ( -4 <= event.values[0] && event.values[0] <= 4 ) { portrait = true; } else { portrait = false; } } public boolean getPortrait (){ return portrait; } @Override protected void onResume() { super.onResume(); sensor.registerListener(MainActivity.this,acc,SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause() { super.onPause(); sensor.unregisterListener(this); } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest package="lightbox.sep.fire1" xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA"/> </manifest>
2016年10月21日
シンプル Android Data Binding : Android Studio 2.2 / 古い定義との違いと、以前のプロジェクトでエラーが出る場合の対処
▼ 記事内のソースコードへ移動 @BindingAdapter でカスタムセッター Syain クラスへ直接セットして表示 Google Gson と tools.jar を使用した REST API 経由の読み込み Firebase API を使用した読込みプロジェクトの Gradle に classpath "com.android.databinding:dataBinder:1.0-rc1" は必要ありません Module(app) の Gradle に dataBinding { enabled = true } があれば動作します去年より地味に進化していました。build.gradle(Module: app) の簡単な設定で利用可能になります build.gradle(Module: app)
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "23.0.3" dataBinding { enabled = true } defaultConfig { applicationId "lightbox.june.bindingbasic" minSdkVersion 19 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' }
バインド用のクラスには、後々 Google Gson 使うので 超単純にしています。 ※ @BindingAdapter はテキストを直接使えないコントロール用のカスタムセッターです Syain.java @{user.birthday} が app:dateText にセットされるよう画面で定義されているので、birthday が setDateText の引数として渡されます。
public class Syain { public String code; public String name; public String birthday; @BindingAdapter("dateText") public static void setDateText(DatePicker datePicker, String text) { if ( text != null && !text.equals("")) { Log.i("lightbox",text); int year = Integer.parseInt(text.substring(0, 4)); int month = Integer.parseInt(text.substring(5, 7)) - 1; int day = Integer.parseInt(text.substring(8, 10)); datePicker.updateDate(year, month, day); } } }
画面も、layout 要素の記述がスリムになりました。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="lightbox.june.bindingbasic.Syain"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/textView" android:textSize="40dp" android:text="@{user.code}"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/textView2" android:layout_marginTop="20dp" android:textSize="40dp" android:text="@{user.name}"/> <DatePicker android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/datePicker" android:layout_below="@+id/textView2" android:datePickerMode="spinner" android:calendarViewShown="false" app:dateText="@{user.birthday}"/> </LinearLayout> </layout>
さて、MainActivity ですが、Android の公式ページがいまだに誤記しているのがタマに傷です。R.layout.main_activity では無く、R.layout.activity_main です。当たり前ですけれど。コピペするとえらい目にあいます。 MainActivity.java 最も単純なバインドのサンプルです。Syain クラスはコンストラクタも無く、JSON のプロパティは、public の同名の変数にセットされます。コメント部分は、JSON 文字列から Syain のインスタンスを作成して使用するパターンです( Google Gson は コンストラクタを使用しません )
package lightbox.june.bindingbasic; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import lightbox.june.bindingbasic.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main); // String json = "{\"code\": \"0001\",\"name\": \"やまだ たろう\",\"birthday\": \"1959/10/10\"}"; // Gson gson = new Gson(); // Syain user = gson.fromJson(json,Syain.class); Syain user = new Syain(); user.code = "0001"; user.name = "やまだ たろう"; user.birthday = "1959/10/10"; binding.setUser(user); } }
これで動くはずですが、良く解らないエラーが出る場合は以下の二つを試すといいはずです。画面に関しては以前からある問題点で、さらに動くまでは画面定義に日本語は直接書かないほうがいいと思います。 エラー時の対処 : パターン11) 画面定義の data 要素部分を削除してすぐ戻す 2) エラーが出るソースを開くと DataBindingUtil 部分のエラーがなくなる 3) binding.メソッドのエラーが残るので、プロジェクトをいったん閉じて開くエラー時の対処 : パターン21) 画面定義の activity_main.xml を activity_main2.xml に変更して すぐに activity_main に戻す 2) ファイルメニューの Invalidate Caches / Restart を実行Data Binding Android - Type parameter T has incompatible upper bounds : ViewDataBinding and MainActivity DataBindingUtil.setContentView - Type parameter T has incompatible upper bounds Google Gson と tools.jar を使用した読み込み ※ URL は、Firebase のログインの必要無いデータを使用しています
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Data Binding 仕様の画面表示 binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main); // アクセスする URL String url = "https://freebase-44e8b.firebaseio.com/class/0001.json?print=pretty"; // URL から読み込み Tools.callHttpGet(url, "utf-8", new Tools.OnAsyncTaskListener() { @Override public void onAsyncTaskListener(String s) { Log.i("lightbox", s); Gson gson = new Gson(); // JSON 文字列を Syain クラスのインスタンスに変換 Syain user = gson.fromJson(s,Syain.class); // Data Binding で画面にデータをセット binding.setUser(user); } }); } }
Firebase Database REST API のドキュメント Google Gson のダウンロード tools.jar のダウンロード Firebase API を使用した読込み
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private FirebaseDatabase database; private DatabaseReference mDatabase; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Data Binding 仕様の画面表示 binding = DataBindingUtil.setContentView(MainActivity.this,R.layout.activity_main); // Firebase database 参照用 database = FirebaseDatabase.getInstance(); mDatabase = database.getReference(); // データの参照 mDatabase.child("class/0001").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // 取得したデータを Syain クラスのインスタンスとして取得 Syain user = dataSnapshot.getValue(Syain.class); // Data Binding で画面にデータをセット binding.setUser(user); } @Override public void onCancelled(DatabaseError databaseError) { // Firebase database の参照に失敗した時の処理 // ※ 参照権限がない場合等 } }); } }
Firebase API + Android Studio : Database のデータを Java に取得する方法は3通りあります。 Firebase API + Android Studio : Database 処理の基本設定
2016年10月18日
Firebase storage の画像を ファイルとしてダウンロードして ギャラリーに保存する( ImageView にも表示する ) / Android
Firebase の環境とプロジェクトの作成方法 9.6.1 を使用する場合 エミュレータ : SDK Manager で最新に更新 実機 : Google Play 開発者サービスを最新に更新 Firebase ドキュメント : ローカル ファイルにダウンロードするMainActivity galleryFile に直接ダウンロードすると簡単ですが、createTempFile によって、アプリのキャッシュにいったん保存して処理したほうが、いろんな情報と関連付けできるのでそうしています。キャッシュに作成したファイルは最後で削除はしていますが、アプリの設定画面の『キャッシュを消去』でクリアできました。 ※ ダウンロードする画像のタイプはここではチェックしていません
public class MainActivity extends AppCompatActivity { // ダウンロード用 private FirebaseStorage storage; private StorageReference storageRef; private StorageReference imageRef; private ImageView imageView; private File galleryFile; private String imagePath; private File localFile; private ProgressDialog progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ▼ テスト用のルール // service firebase.storage { // match /b/freebase-654b7.appspot.com/o { // match /{allPaths=**} { // allow read; // allow write: if true; // } // } // } // ダウンロードするファイル storage = FirebaseStorage.getInstance(); storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/"); imageRef = storageRef.child("sworc2.png"); // 表示する場所 imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView); // ダウンロード中のダイアログ progress = new ProgressDialog(MainActivity.this); // ************************************* // ギャラリーにダウンロードして表示 // ************************************* Button galleryButton = (Button) MainActivity.this.findViewById(R.id.galleryButton); galleryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ダウンロード中の表示 progress.setProgressStyle(ProgressDialog.STYLE_SPINNER); progress.setMessage("画像をダウンロードしています"); progress.show(); // ギャラリー用に内部ストレージにフォルダを作成 String firebaseImageDir = Environment.getExternalStorageDirectory().getPath() + "/firebase"; File file = new File(firebaseImageDir); // ディレクトリ初期作成 if (!file.exists()) { if (file.mkdir() == false) { Log.i("lightbox", "ディレクトリを作成できませんでした"); return; } } // ギャラリー用画像保存パス Calendar cal = Calendar.getInstance(); SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss"); imagePath = firebaseImageDir + "/" + sf.format(cal.getTime()) + ".png"; galleryFile = new File(imagePath); localFile = null; try { // 第三引数省略時、System.getProperty("java.io.tmpdir") に作成 // ( このアプリの キャッシュ ) // 例) /data/data/lightbox.sep.fire1/cache localFile = File.createTempFile("sworc2", "png"); } catch (IOException e) { e.printStackTrace(); } if ( localFile == null ) { Log.i("lightbox","テンポラリファイルを作成できませんでした"); return; } Log.i("lightbox","実際の TempFile のパス"); Log.i("lightbox",localFile.getAbsolutePath()); // ファイルにダウンロード imageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { // ダウンロード中の表示解除 progress.dismiss(); // ダウンロードした localFile をファイルコピー try { FileInputStream inStream = new FileInputStream(localFile); FileOutputStream outStream = new FileOutputStream(galleryFile); FileChannel inChannel = inStream.getChannel(); FileChannel outChannel = outStream.getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); inStream.close(); outStream.close(); } catch (IOException e) { e.printStackTrace(); } Log.i("lightbox","キャッシュ内の一覧"); File[] files = new File(System.getProperty("java.io.tmpdir")).listFiles(); for(int i = 0; i < files.length; i++){ Log.i("lightbox",files[i].getAbsolutePath()); } // ギャラリーに反映 MediaScannerConnection.scanFile( MainActivity.this, new String[] { imagePath }, new String[] { "image/png" }, null); // ImageView に表示 FileInputStream fis = null; try { fis = new FileInputStream(localFile); if ( fis != null ) { Bitmap image = BitmapFactory.decodeStream(fis); imageView.setImageBitmap(image); // キャッシュファイルの削除 localFile.delete(); } } catch (FileNotFoundException e) { e.printStackTrace(); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.i("lightbox","ダウンロードできませんでした"); Log.i("lightbox",e.getMessage()); // ダウンロード中の表示解除 progress.dismiss(); // メッセージ表示 Toast.makeText(MainActivity.this,"ダウンロードに失敗しました",Toast.LENGTH_LONG).show(); } }); } }); } }
テンポラリファイルを使わずにダイレクトに保存するソースコード build.gradle(Project)
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.1' classpath 'com.google.gms:google-services:3.0.0' } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
build.gradle(Module: app)
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "23.0.3" defaultConfig { applicationId "lightbox.sep.fire1" minSdkVersion 19 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.google.firebase:firebase-core:9.6.1' compile 'com.google.firebase:firebase-storage:9.6.1' compile 'com.google.firebase:firebase-auth:9.6.1' } apply plugin: 'com.google.gms.google-services'
※ AndroidManifest.xml には、android.permission.INTERNET を設定 ※ AndroidManifest.xml には、android.permission.WRITE_EXTERNAL_STORAGE を設定
Firebase storage の画像の URL を取得して、通常と同様に Stream でダウンロードして ImageView に表示する / Android
Firebase の環境とプロジェクトの作成方法 9.6.1 を使用する場合 エミュレータ : SDK Manager で最新に更新 実機 : Google Play 開発者サービスを最新に更新 Firebase ドキュメント : URL 経由でデータをダウンロードするgetDownloadUrl メソッドで Uri を取得した後は、通常どおり AsyncTask 内で Stream で読み込みます。 MainActivity
public class MainActivity extends AppCompatActivity { // ダウンロード用 private FirebaseStorage storage; private StorageReference storageRef; private StorageReference imageRef; private ImageView imageView; private ProgressDialog progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ▼ テスト用のルール // service firebase.storage { // match /b/freebase-654b7.appspot.com/o { // match /{allPaths=**} { // allow read; // allow write: if true; // } // } // } // ダウンロードするファイル storage = FirebaseStorage.getInstance(); storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/"); imageRef = storageRef.child("sworc2.png"); // 表示する場所 imageView = (ImageView) MainActivity.this.findViewById(R.id.imageView); // ダウンロード中のダイアログ progress = new ProgressDialog(MainActivity.this); // ************************************* // 表示2 ( Uri を取得 => Stream ) // ************************************* Button button = (Button) MainActivity.this.findViewById(R.id.downloadButton); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ダウンロード中の表示 progress.setProgressStyle(ProgressDialog.STYLE_SPINNER); progress.setMessage("画像をダウンロードしています"); progress.show(); // ダウンロード用の URL を取得 imageRef.getDownloadUrl() .addOnSuccessListener(new OnSuccessListener<Uri>() { @Override public void onSuccess(Uri uri) { Log.i("lightbox","ダウンロード開始"); new AsyncTask<Uri, Void, Bitmap>(){ @Override protected Bitmap doInBackground(Uri... params) { Uri uri = params[0]; Log.i("lightbox",uri.toString()); URL url = null; try { url = new URL(uri.toString()); } catch (Exception e) { e.printStackTrace(); } Bitmap image = null; // 読み込みオプション BitmapFactory.Options options; try { // インターネット上の画像を取得して、Bitmap に変換 options = new BitmapFactory.Options(); // 実際に読み込む事をオプションに設定 options.inJustDecodeBounds = false; // ストリーム取得 InputStream is = (InputStream) url.getContent(); // ストリームよりビットマップを作成 image = BitmapFactory.decodeStream(is, null, options); // ストリームを閉じる is.close(); } catch (Exception e) { e.printStackTrace(); } return image; } @Override protected void onPostExecute(Bitmap bitmap) { if ( bitmap != null ) { imageView.setImageBitmap(bitmap); } // ダウンロード中の表示解除 progress.dismiss(); } }.execute(uri); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.i("lightbox","データ取得に失敗しました"); Log.i("lightbox",e.getMessage()); // ダウンロード中の表示解除 progress.dismiss(); // メッセージ表示 Toast.makeText(MainActivity.this,"ダウンロードに失敗しました",Toast.LENGTH_LONG).show(); } }); } }); } }
build.gradle(Project)
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.1' classpath 'com.google.gms:google-services:3.0.0' } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
build.gradle(Module: app)
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "23.0.3" defaultConfig { applicationId "lightbox.sep.fire1" minSdkVersion 19 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.google.firebase:firebase-core:9.6.1' compile 'com.google.firebase:firebase-storage:9.6.1' compile 'com.google.firebase:firebase-auth:9.6.1' } apply plugin: 'com.google.gms.google-services'
※ auth はここでは使用していません ※ AndroidManifest.xml には、android.permission.INTERNET を設定
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。 Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。 また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。 ※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです 対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。 ※ エキスパートモードで表示しています アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります<% if:page_name eq 'archive' -%> アーカイブページでのみ表示される内容 <% /if %> <% if:page_name eq 'category' -%> カテゴリページでのみ表示される内容 <% /if %> <% if:page_name eq 'tag' -%> タグページでのみ表示される内容 <% /if %>この記述は、以下の場所で使用します
|