一般社団法人ランコントレ・ミグノンさんに5千円寄付しました。ポケモンGoの進捗。
ズボラなのでブログというものをあまり書けず、今回も2つの主題を一つのエントリに詰め込むことになってしまいタイトルからして変です(;^ω^)
8月は一般社団法人ランコントレ・ミグノンさんに5千円寄付しました。
http://rencontrer-mignon.org/rencontrer-mignon.org
ランコントレ・ミグノンさんは動物の保護、シェルター運営、譲渡などを行っていらっしゃいます。
ほぼ日のサイトに糸井重里さんと代表の友森さんのインタビューが載っていました(๑•̀ㅂ•́)و✧
これを書いているのは9月3日ですが、振り込みしたのは8月29日でした。
ここのところ時々ポケモンGoにハマっているので、支援活動に対するテンションが下がり気味です。
長い活動の中で身が入らない時期もあるのでチンタラしながらでもとにかく続けていこうと思います。
ところで、そのポケモンGoの進捗ですが最近レベル26になりました。
ジワジワ強くなっています。
普段のゲーム活動としては夜のウォーキング時のポケスト巡り、週1ぐらいでどこかの公園でポケモン捕獲、ちょっと課金ぐらいです。
戦闘員たち
一度ミニリュウを集めに上野の不忍池に行きました。
無事ミニリュウ+ハクリューを20匹ぐらいかな?捕まえてカイリューに進化ましたが周りの人たちがミニリュウが出現するたびにダッシュするのでかなりびっくりしました。
独特の面白さを感じたのでまた行きたいです。
NPO法人東京キャットガーディアンさんに5000円寄付しました。そしてPokemon Goの日々な46歳
こんにちは!
今月はNPO法人東京キャットガーディアンさんに5000円寄付しました。
大塚・西国分寺の猫カフェ型開放型シェルターを拠点としつつ保護猫活動などの活動をなさっています。
譲渡総数5000頭を突破されているそうで、長いこと本当によく頑張ってこられたんだなあ、すごいなあと思っています。
今度大塚の猫カフェに行ったみたいです。
Pokémon GO がリリースされましたね。
www.pokemongo.jp
やり始めたら童心にかえったのか、結構ハマってしまいました。
ポケモンをゲットするために今までの2倍は歩くようになりました。
もう健康アプリという認識です😁
リアルな街歩きの最中に可愛らしいポケモンと出会えるのが嬉しくて。
プレーヤーのマナーが問題になったり何かとお騒がせですが、位置情報系のアプリが盛り上がっていくのが楽しみです。
↓近所で。感動しました↓
プレー開始2〜3日は若者にまじってポケモンをしていることが恥ずかしかったのですが、
今や全然気にせずやるようになってしまいました(●´ϖ`●)
↓ジムレベルが上がったタイミングで一瞬だけジムに自分のポケモンを置くことが出来ました!↓
今はレベル20になって、ポケモンの個体値が気になり出しました。
個体値のいいポケモンを育てると強くなるらしいですよ。
↓このクサイハナは88-93%の高スコアなやつです。↓
面倒な計算はアプリでやっています。
Special Thanks to...
https://play.google.com/store/apps/details?id=com.andcreate.app.pokemon_go_iv_checkerplay.google.com
ポケモンゲットな日々が体重減少に結びつくよう願っています。
Android4.4のWebViewでopenFileChooserが動かない件の対処方法2つ JavascriptInterfaceを使う/Crosswalkを使う
WebViewで表示しているページ上で、ユーザーがボタンを押したらローカルのイメージ一覧を表示するような処理があるとします。
そして、ボタンを押されたイベントをネイティブで受け取る→暗黙的Intentでギャラリーを呼び出しファイルを選択させるということをしようとするとします。
この場合、WebChromeClientを拡張してopenFileChooserメソッド又はonShowFileChooserメソッド(Lollipop以上のバージョンはメソッド名が変わっています)をoverrideして、ネイティブ側でイメージ一覧を表示する方法が考えられますが、Android4.4ではopenFileChooserメソッドが取り除かれていてメソッドが呼び出されないという不具合があります。
2013年からissueに上がっているのですが、公式では対応されていないようです。
WebViewはAndroid4.4でChromiumベースに変わっており、5.0以上は、Androidのプラットフォームとは切り離されてアップデートされるようになりました。これによりアップデートの進まないAndroidプラットフォームでもWebViewのセキュリティ上の脆弱性に素早く対応出来るようになりました。
Android 5.0未満を切り捨てることが出来れば一番いいと思うんですが2016/06現在なかなかそうはいかないです。
WebView for Android - Google Chrome
(ところでAndroid 4.3以前のWebViewについてはGoogleはサポートを終了しているようですが、4.4ってまだサポート対象なのでしょうか?軽いググりではよくわかりませんでした。)
issueやStackOverflowではopenFileChooserが動かない問題に対してだいたい2つの対処方法をおすすめされていて、当エントリでも試したことを書いておきます。
何か間違いを発見したりもっと良い方法を御存知でしたらご教示いただけますととても助かりますm(_ _)m
対処方法
1. openFileChooserでイベントを受け取るのを諦めてJavaScriptInterfaceを利用してWeb側からネイティブのメソッドを呼び出してもらう。
2.Chromiumエンジンで作られているWebViewのバックポートライブラリをアプリに組み込み、AndroidのWebViewの代わりに使用する。(ここではintel製のCrosswalkを使うものとします。)
利点と欠点
1.の方法
利点 | 欠点 |
---|---|
・どのAndroidバージョンでもこの方法で動くはずなので、バージョン毎にコードを分けたりする必要がない。 ・外部ライブラリを使用しないのでアプリサイズが増えない。 | ・WebページにAndroidのメソッドを呼び出すようにコーディングする必要があるので、Webページ側のコーディングが出来ない場合はこの方法は利用できない。 ・処理がWebページ側とネイティブ側にまたがっているのでコードを追いづらい。 |
2.の方法
利点 | 欠点 |
---|---|
・4.4未満のバージョンでもChromiumエンジンの統一されたWebViewを使える。(ライブラリのメンテナンス状況によるがセキュリティ上のメリットも大きい) | ・アプリサイズが大きくなる。(サンプルアプリを作ったら44Mを超えてしまいました。) ・Google公式のサポート対象ではなくサードパーティ製のライブラリなので今後の開発状況が気になる。 ・WebViewをたくさん使うアプリなら導入のメリットがあると思うが、使用箇所が少ない場合は私には1の方が手軽に感じた。 |
どちらにも良し悪しあるので、プロジェクトによって合う方法を選べばいいと思います。
Multiple APKの機能を利用してAndroid5以降は公式のWebViewを使用してそれ以前のapkは2の方法を使うという手もあります。
開発コストがかかるので場合によりけりですが。
1.の方法の例
色んなところでおすすめされているサイト
Android Kitkat Webview image upload!codemumbai.wordpress.com
↑これを参考にして手元でテストしやすいように書き換えたサンプルです。
なお、サンプルではuploadボタンが付いていますがAndroidネイティブ側ではファイルをアップロードする実装はしていません。
- ローカルのassetsフォルダ配下にhtmlファイルを置いておいてWebViewで表示しています。
- Browse Galleryボタンを押すと、JavaScriptのメソッドからAndroidネイティブで定義しているメソッドを呼び出します。WebViewのaddJavascriptInterfaceメソッドを用いてバインディングしています。JavascriptInterfaceの使い方はこちらをごらんください。https://developer.android.com/guide/webapps/webview.html#BindingJavaScript
- サンプルプロジェクト全体はこちらからご覧になれます。
file:///android_asset/index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Website name</title> <script type="text/javascript"> function browsepicture(){ Android.openGallary(); } function uploadfunction(){ Android.uploadImage(); } function setFileUri(uri) { document.getElementById('lbluri').innerHTML = uri; } </script> </head> <body> <div data-role="page" id="pageone"> <div data-role="header"> <h1>Kitkat WebView</h1> <p><a href="https://codemumbai.wordpress.com/android-webview-image-upload-solved/">modified from android-webview-image-upload-solved</a> </p> </div> <div data-role="content"> <input type="button" value="Browse Gallary" onClick="browsepicture()"/> <br> <label id="lbluri"></label> <br> <input type="button" value="Upload" onClick="uploadfunction()"/> </div> </div> </body> </html>
MainActivity.java
package sakura_fish.com.exam.kitkatwebview; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.webkit.JavascriptInterface; import android.webkit.WebView; import android.widget.Toast; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import permissions.dispatcher.NeedsPermission; import permissions.dispatcher.RuntimePermissions; @RuntimePermissions public class MainActivity extends AppCompatActivity { protected final int SELECT_PICTURE = 1; private Context mContext; private WebView mWebView; private boolean isImageSelected; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; isImageSelected = false; mWebView = (WebView) findViewById(R.id.webview); settingWebView(); mWebView.loadUrl("file:///android_asset/index.html"); } @Override protected void onResume() { if (mWebView != null) { mWebView.onResume(); } super.onResume(); } @Override protected void onPause() { super.onPause(); if (mWebView != null) { mWebView.onPause(); } } @Override protected void onDestroy() { if (mWebView != null) { mWebView.stopLoading(); mWebView.setWebChromeClient(null); mWebView.setWebViewClient(null); mWebView.destroy(); mWebView = null; } mContext = null; super.onDestroy(); } @SuppressLint("SetJavaScriptEnabled") protected void settingWebView() { mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setAllowFileAccess(true); // ここでバインディング mWebView.addJavascriptInterface(new WebAppInterface(), "Android"); // openFileChooser not called on Android 4.4 // mWebView.setWebChromeClient(new WebChromeClient() { // @SuppressWarnings("unused") // public void openFileChooser(ValueCallback<Uri> uploadMsg) { // selectImage(); // } // // @SuppressWarnings("unused") // public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { // selectImage(); // } // // // For Android 4.1 // @SuppressWarnings("unused") // public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { // selectImage(); // } // // // Android 5.0 + // @Override // public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { // selectImage(); // return true; // } // }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.d(MainActivity.class.getSimpleName(), "requestCode:" + requestCode + " resultCode:" + resultCode); if (requestCode == SELECT_PICTURE && resultCode == Activity.RESULT_OK) { Uri selectedImage = data.getData(); setFileUriToWebView(selectedImage.toString()); InputStream input = null; try { input = mContext.getContentResolver().openInputStream(data.getData()); if (input != null) { // create image cache file isImageSelected = true; createPickCache(data.getData()); } } catch (FileNotFoundException e) { e.printStackTrace(); } } } private void setFileUriToWebView(String uriString) { mWebView.loadUrl("javascript:setFileUri('" + "uri : " + uriString + "')"); } void checkStoragePermission() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { chooseImage(); return; } if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { MainActivityPermissionsDispatcher.chooseImageWithCheck(MainActivity.this); } else { chooseImage(); } } @NeedsPermission({Manifest.permission.READ_EXTERNAL_STORAGE}) void chooseImage() { Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.setType("image/*"); startActivityForResult(Intent.createChooser(i, "File Chooser"), SELECT_PICTURE); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); MainActivityPermissionsDispatcher.onRequestPermissionsResult(MainActivity.this, requestCode, grantResults); } private boolean createPickCache(@NonNull final Uri uri) { try { InputStream in = null; in = mContext.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(in); in.close(); if (bitmap == null) { Log.e(MainActivity.class.getSimpleName(), "bitmap is null!"); return false; } // サーバー送信用に画像を圧縮してテンポラリファイルに保存する float maxImageSize = 1500; ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { float ratio = Math.min( maxImageSize / bitmap.getWidth(), maxImageSize / bitmap.getHeight()); int width = Math.round(ratio * bitmap.getWidth()); int height = Math.round(ratio * bitmap.getHeight()); Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false); newBitmap.compress(Bitmap.CompressFormat.JPEG, 70, stream); byte[] byteArray = stream.toByteArray(); File file = new File(mContext.getCacheDir() + "cache.jpg"); if (file.exists()) file.delete(); FileOutputStream fo = null; fo = new FileOutputStream(file); fo.write(byteArray); fo.flush(); fo.close(); newBitmap.recycle(); return true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { bitmap.recycle(); } return false; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return false; } private void sendImageToServer() { File file = new File(mContext.getCacheDir() + "cache.jpg"); if (!file.exists()) { throw new IllegalStateException("No cache file!"); } // TODO ここにサーバーに送信する処理を書く Toast.makeText(mContext, "TODO: 画像送信しました!", Toast.LENGTH_LONG).show(); isImageSelected = false; setFileUriToWebView(""); } // JavascriptInterface class WebAppInterface { @JavascriptInterface public void openGallary() { checkStoragePermission(); } @JavascriptInterface public void uploadImage() { if (isImageSelected) { sendImageToServer(); } else { Toast.makeText(mContext, "You need to select image!", Toast.LENGTH_LONG).show(); } } } }
2.の方法の例
CROSSWALKとは何ぞや?というところは、公式や参考サイト様をご覧になるのがいいと思います。
当サンプルでは、CROSSWALKの埋め込みのViewを使います。
WebViewをCROSSWALKのViewに置き換えるイメージです。ファイルの選択はCrossWalkに任せています。
※Attention! こちらのサンプルではファイル選択→画像表示しかしていません。CrossWalkについて詳しい情報は公式サイトなどご覧ください。
サンプルの全体はこちらからご覧になれます。
github.com
/build.gradle
allprojects {
repositories {
jcenter()
maven {
url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'
}
}
}
/app/build.gradle
dependencies {
compile 'org.xwalk:xwalk_core_library:18.48.477.13'
}
file:///android_asset/index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Website name</title> </head> <body> <div data-role="page" id="pageone"> <div data-role="header"> <h1>CrossWalk</h1> <p><a href="https://crosswalk-project.org/documentation/android/embedding_crosswalk.html">Android embded runtime</a> </p> </div> <div data-role="content"> <input type="file"> </div> </div> </body> </html>
MainActivity.java
package sakura_fish.com.exam.crosswalk; import android.content.ContentResolver; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.webkit.ValueCallback; import android.widget.ImageView; import org.xwalk.core.XWalkUIClient; import org.xwalk.core.XWalkView; import java.io.FileNotFoundException; import java.io.IOException; public class MainActivity extends AppCompatActivity { private XWalkView mXWalkView; private ImageView mImageView; private ValueCallback<Uri> mUploadMessage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mXWalkView = (XWalkView) findViewById(R.id.xwalkview); mImageView = (ImageView) findViewById(R.id.imageview); settingXWalkView(); mXWalkView.load("file:///android_asset/index.html", null); } @Override protected void onPause() { super.onPause(); if (mXWalkView != null) { mXWalkView.pauseTimers(); mXWalkView.onHide(); } } @Override protected void onResume() { super.onResume(); if (mXWalkView != null) { mXWalkView.resumeTimers(); mXWalkView.onShow(); } } @Override protected void onDestroy() { super.onDestroy(); if (mXWalkView != null) { mXWalkView.onDestroy(); } } protected void settingXWalkView() { mXWalkView.setUIClient(new MyUIClient(mXWalkView)); } class MyUIClient extends XWalkUIClient { MyUIClient(XWalkView view) { super(view); } @Override public void openFileChooser(XWalkView view, ValueCallback<Uri> uploadFile, String acceptType, String capture) { Log.d(MainActivity.class.getSimpleName(), "openFileChooser"); super.openFileChooser(view, uploadFile, acceptType, capture); mUploadMessage = uploadFile; } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.d(MainActivity.class.getSimpleName(), "requestCode:" + requestCode + " resultCode:" + resultCode); if (mUploadMessage == null) return; Uri result = data == null || resultCode != RESULT_OK ? null : data.getData(); mUploadMessage.onReceiveValue(result); mUploadMessage = null; Bitmap bm = null; ContentResolver resolver = getContentResolver(); try { bm = MediaStore.Images.Media.getBitmap(resolver, result); mImageView.setImageBitmap(bm); bm = null; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
参考サイト様
qiita.com
stackoverflow.com
stackoverflow.com
Embedding Crosswalk in Android Studio – diego.org
iti.hatenablog.jp
blog.asial.co.jp