- 投稿日:2020-09-19T23:32:54+09:00
Android 入力後キーボードを閉じる方法
private fun hideKeyboard(view:View?) { if(view != null) { val manager = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager manager.hideSoftInputFromWindow(view.windowToken, 0) } }
- 投稿日:2020-09-19T22:50:48+09:00
Androidでアプリ内のLocaleを変更せずに他言語リソースを取得する
はじめに
Androidで言語を切り替える場合、下記のようにActivityのattachBaseContextでlocaleを設定するのが一般的です。(API 25でupdateConfigurationはdeprecatedとなり、createConfigurationContextの利用が推奨されています。)
ですが、一つの画面に複数の言語リソースを表示したい場合や言語切替時にActivityをrecreateしたくない場合、この方法は使えません。今回は一時的に変更できるようにする方法をメモしときます。
MainActivity.ktoverride fun attachBaseContext(base: Context) { val locale = Locale.ENGLISH val res = resources val config = Configuration(res.configuration) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocales(LocaleList(locale)) } else { config.setLocale(locale) } super.attachBaseContext(base.createConfigurationContext(config)) }やり方
やり方はとても簡単です。attachBaseContextでlocaleを変更したcontextを返さなければいいだけです。
まずは、言語を切替やすくするためにcreateLocalizedContextという拡張関数を作ります。ContextExtention.ktfun Context.createLocalizedContext(locale: Locale): Context { val res = resources val config = Configuration(res.configuration) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocales(LocaleList(locale)) } else { config.setLocale(locale) } return createConfigurationContext(config) }リソースファイルは日本語(default)と英語を用意。
values/strings.xml<string name="hello_world">こんにちは 世界</string>values-en/strings.xml<string name="hello_world">hello world</string>利用方法は下記の通り。簡単に切り替えられました。
MainActivity.ktvar localizedContext = applicationContext.createLocalizedContext(Locale.ENGLISH) print(localizedContext.getString(R.string.hello_world)) // hello world localizedContext = applicationContext.createLocalizedContext(Locale.JAPANESE) print(localizedContext.getString(R.string.hello_world)) // こんにちは 世界
- 投稿日:2020-09-19T22:25:51+09:00
ARCore Depth APIについて
ARCore Depth APIとは
Googleの拡張現実(AR)に関するアップデートであり、ARCoreに追加された新しいAPI。
Depthセンサなどの専用の機器を用いることで、デバイスに搭載されたRGBカメラを用いた深度マップの生成を行うことが可能になっています。そもそもDepthセンサとは何か
Depth = を測定するセンサ。
これによって深度を目に見える形(色など)で表現したものがDepth Map(深度マップ)。
Depth APIはサーモグラフィのような色で表現されています。Depth APIのここがすごい!
大きく分けて2点
- オクルージョンの表現
- 専用の機器を追加で取り付ける必要がない
オクルージョンの表現
遮蔽の表現のこと。手前のものが後ろのものを隠す表現をオクルージョンといいます。
現実の世界では当たり前にあるものではあるが、ARの場合、現実世界のオブジェクトや他の3Dオブジェクトとの距離を考慮しながら3Dモデルの表示をコントロールしなければ、適切な遮蔽表現はできません。ARの現実感に影響する重要な要素でもあります。
←:オクルージョンなし →:オクルージョンあり
引用元:https://japanese.engadget.com/jp-2019-12-25-google-3d-ar-arcore.html専用の機器を追加で取り付ける必要がない
公式の記事によると、センサが搭載されている方が精度は高くなるものの、必須ではないようです。
ARCoreに対応した機器である必要はあるものの、新規に専用のセンサが搭載されたデバイスを買う必要もなく、自由度がかなり高くなっています。
ARCore Android向けのDepth APIの概要:
https://developers.google.com/ar/develop/java/depth/overview実際に使ってみる
環境
Pixel 3a
Andorid 10
Android Studio 4.0.1公式がサンプルを出してくれているため、これを使います。
詳しくはこちらのサイトをご参考にしてください。サンプルプロジェクトを開く
次のコマンドでサンプルプロジェクトを持ってこれます。
git clone https://github.com/google-ar/arcore-android-sdk.gitAndroid Studioで、arcore-android-sdk/samples/hello_ar_javaを開きます。
サンプルを実行する
Andorid Studioからサンプルを実行することで、サンプルアプリを使うことができます。
エミュレータでの起動はできないため、注意してください。サンプルの実行結果
サンプルの内容としては、タップした場所にドロイドくんを設置できるものになっています。
平面の検知をしてくれるので、きれいに床の上に設置されます。
※白い三角形のメッシュが出ているところが検知された平面部分
設置したドロイドくんはオクルージョンが適応されているため、ドロイドくんを物の後ろに設置すると見えなくなります。
Depth APIを体験できるアプリ
APIの紹介動画で紹介されているアプリ
- 投稿日:2020-09-19T20:55:21+09:00
Visual Studio 2019のC#にて、Bitmapオブジェクトを、Android環境下において直接Windows Bitmapファイルにバイナリを叩いて出力する。コードのサンプルです。
表題の通り
かつてのWindows版に続いて
https://qiita.com/oyk3865b/items/58abd56c5c1edcc84118今回は
Visual Studio 2019のC#にて、
Android環境下において
Bitmapオブジェクトを、直接Windows Bitmapファイルにバイナリを叩いて出力する。
コードのサンプルでございます。特段、NuGetなどで他のプラグインや
別途外部dllを必要としないようにしています。要は、バイナリを以下のように、直接叩いています。
1.Bitmapのピクセル情報をバイナリ配列に書き出す。
2.Bitmapのヘッダーの書き込み。
3.4の倍数のルールに従いBitmapバイナリをファイルに書き出す。Android開発環境では、
bitmap.Compress(Bitmap.CompressFormat.Png, 100, fos);みたく
bitmap.Compress(Bitmap.CompressFormat.Bmp, 100, fos);という画像の保存方法が
使用できないため、Androidアプリで
RGB888・Bitmap形式にて出力したい場合に
自力でBitmapをファイルに書き出すことにしたため
作成いたしました。※私は素人ですし
今回のコードは、あくまで最低限の動作する部分にとどめていますので、汚いです。
例外処理や、解放など至らぬ点が、多々ございます。ご了承ください。
もし実際に参考にされる際は、必要個所を必ず訂正・加筆してからにしてください。※動作は沢山メモリを使うので重いです。
その辺は度外視しています。下の画像は、今回コードでの作成例です。
●もともとBitmap形式の画像を取り込んで(変換前)、
今回のコードにて出力した結果(変換後)との
バイナリ比較画像です[参考サイト]
Bitmapデータを、バイナリレベルで触るにあたって調べたレポート1
※リンク先は、私のブログです。気を付けてください。clsBMPF.csusing System; using System.Collections.Generic; using Android.Graphics; class clsBMPF { //画像はARGB8888で扱う Android.Graphics.Bitmap.Config bitmapConfig = Android.Graphics.Bitmap.Config.Argb8888; //■ヘッダー情報関係 //http://oyk3865b.blog13.fc2.com/blog-entry-1394.html //△冒頭の構文=BfType(ファイルタイプ)------------------------------------------------------------------------------------------------------------ readonly byte[] bmp_header_start = new byte[] { 0x42, 0x4D }; //△ヘッダー間隔用のオブジェクト------------------------------------------------------ readonly byte[] bmp_1st_obj = new byte[] { 0, 0, 0, 0 }; //△BfOffBits------------------------------------------------------ readonly byte[] bmp_2nd_obj = new byte[] { 0x36, 0, 0, 0 }; //△BiSize------------------------------------------------------ readonly byte[] bmp_3rd_obj = new byte[] { 0x28, 0, 0, 0 }; //△BiPlanes_BiBitCount------------------------------------------------------ readonly byte[] bmp_4th_obj = new byte[] { 0x1, 0, 0x18, 0 }; public void Output_Bitmap_Image(Bitmap bitmap, string Output_Path) { //Windows Bitmapを自分で作る //bitmap→出力する画像の元データ //Output_Path→出力するパス(重複の場合、上書きする) //https://qiita.com/oyk3865b/items/58abd56c5c1edcc84118 //色設定をARGB8888に統一する。 //https://stackoverflow.com/questions/7320392/how-to-draw-text-on-image bitmap = bitmap.Copy(bitmapConfig, true); //Bitmapのバイナリ置き換えの準備 //https://qiita.com/aymikmts/items/7139fa6c4da3b57cb4fc string err_str = "byteBuffer"; Java.Nio.ByteBuffer byteBuffer = Java.Nio.ByteBuffer.Allocate(bitmap.ByteCount); err_str = "CopyPixelsToBuffer"; bitmap.CopyPixelsToBuffer(byteBuffer); err_str = "Flip"; byteBuffer.Flip(); err_str = "bmparr"; //基礎Bitmapのバイナリへの置き換え byte[] bmparr = new byte[byteBuffer.Capacity()]; err_str = "Get"; byteBuffer.Duplicate().Get(bmparr); err_str = "Clear"; byteBuffer.Clear(); //ファイルサイズの算出(横は4の倍数でないといけない) int width_size = bitmap.Width * 3; //幅1行のバイナリサイズを算出(幅px*3色) //幅バイナリ値は4の倍数に直してから、高さpxをかけ本体サイズを算出。 Int32 bitmap_filesize = ((((width_size + 3) / 4) * 4) * bitmap.Height); //出来るBitmapの全ファイル・サイズを格納 Int32 bitmap_all_filesize = bitmap_filesize + 54; //ヘッダーなど本体以外で54バイト消費 //■出力bmpバイナリの初期化 //出力bmpのバイナリ格納用。 List<byte> ary_bmp_file = new List<byte>(); //出力bmpのバイナリ各オブジェクトの、バイト開始位置を格納用。 List<byte> ary_bmp_byte_head = new List<byte>(); //■ファイルを作成して書き込む //ファイルが存在しているときは、消してから書き込みする if (System.IO.File.Exists(Output_Path)) { System.IO.File.Delete(Output_Path); } using (System.IO.FileStream fs = new System.IO.FileStream(Output_Path, System.IO.FileMode.Create, System.IO.FileAccess.Write)) { //◎冒頭の書き込み ary_bmp_file.AddRange(bmp_header_start); //◎全ファイルサイズの格納(BfSize) //ファイルサイズ値を、バイト配列に変換 ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap_all_filesize)); if (ary_bmp_byte_head.Count < 4) { //必ず、4バイトにする。 //空隙は0で埋める。 for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++) { ary_bmp_byte_head.Add(0); } } ary_bmp_file.AddRange(ary_bmp_byte_head.ToArray()); //書き加える。 ary_bmp_byte_head.Clear(); //不要情報を、クリア。 //◎空白4バイトの書き込み ary_bmp_file.AddRange(bmp_1st_obj); //◎BfOffBitsの書き込み ary_bmp_file.AddRange(bmp_2nd_obj); //◎BiSizeの書き込み ary_bmp_file.AddRange(bmp_3rd_obj); //◎画像幅の指定(BiWidth) //幅の値を、バイト配列に変換 ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap.Width)); if (ary_bmp_byte_head.Count < 4) { //必ず、4バイトにする。 for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++) { ary_bmp_byte_head.Add(0); } } ary_bmp_file.AddRange(ary_bmp_byte_head); //書き加える。 ary_bmp_byte_head.Clear(); //不要情報を、クリア。 //◎画像高さの指定(BiHeight) //高さの値を、バイト配列に変換 ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap.Height)); if (ary_bmp_byte_head.Count < 4) { //必ず、4バイトにする。 for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++) { ary_bmp_byte_head.Add(0); } } ary_bmp_file.AddRange(ary_bmp_byte_head); //書き加える。 ary_bmp_byte_head.Clear(); //不要情報を、クリア。 //◎BiPlanes_BiBitCountの書き込み ary_bmp_file.AddRange(bmp_4th_obj); //◎空白4バイトの書き込み ary_bmp_file.AddRange(bmp_1st_obj); //◎画像ファイルサイズの格納(BiSizeImage) //ファイルサイズ値を、バイト配列に変換 ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap_filesize)); for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++) { ary_bmp_byte_head.Add(0); } ary_bmp_file.AddRange(ary_bmp_byte_head); //書き加える。 ary_bmp_byte_head.Clear(); //不要情報を、クリア。 //◎空白4バイトの書き込み×4 for (int i = 0; i < 4; i++) { ary_bmp_file.AddRange(bmp_1st_obj); } //一旦、バイト型配列の内容をファイルに書き出す fs.Write(ary_bmp_file.ToArray(), 0, ary_bmp_file.Count); //配列を、一旦、クリアする。 ary_bmp_file.Clear(); //■ヘッダーの作成------------ここまで----------------- //Bitmapは、左下から書き込む for (int bmp_y = bitmap.Height - 1; bmp_y >= 0; bmp_y--) { //縦軸分のループ(下から) //横幅の分だけ配列にコピーする。 //このコピー部分は、ループでなくて、配列のコピーでも良い for (int bmp_x = 0; bmp_x <= (bitmap.Width - 1); bmp_x++) { //横軸分のループ(左から) //その座標の点にある、ピクセル(ドット)の色を取得する。 long pos = bmp_y * (bitmap.Width * 4) + bmp_x * 4; //ary_bmp_file.Add(bmparr[pos + 3]); //AはRGB888形式で出力するため入れない ary_bmp_file.Add(bmparr[pos + 2]); //R ary_bmp_file.Add(bmparr[pos + 1]); //G ary_bmp_file.Add(bmparr[pos + 0]); //B } //4の倍数でない場合 if (ary_bmp_file.Count % 4 != 0) { //横方向は、4の倍数とする。 while ((ary_bmp_file.Count % 4) != 0) { ary_bmp_file.Add(0); //0で埋める } } //一旦、バイト型配列の内容をファイルに書き出す fs.Write(ary_bmp_file.ToArray(), 0, ary_bmp_file.Count); //配列を、一旦、クリアする。 ary_bmp_file.Clear(); } //■書き込みの後処理 fs.Close(); //ファイルを閉じる } //バイナリ・メモリの占有を開放する。 ary_bmp_file.Clear(); ary_bmp_file = null; ary_bmp_byte_head.Clear(); ary_bmp_byte_head = null; //画像の解放 Array.Clear(bmparr,0, bmparr.Length); bmparr = null; if (bitmap != null) { bitmap.Dispose(); } bitmap = null; } }
- 投稿日:2020-09-19T15:15:28+09:00
【Android】lateinit と by lazy の使い分け【Kotlin】
プロパティの初期化を遅らせる方法
Android 開発ではプロパティの初期化を遅らせたい場面がけっこうあります。
例えば、以下のようなケース。
class MainActivity : AppCompatActivity() { // この時点では view が生成されていないので初期化できない。 private var textView: TextView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ここで初めて初期化できる。 textView = findViewById(R.id.text_view) } //... }Kotlin でプロパティの初期化を遅らせる方法として
lateinit
とby lazy
がよく使われると思うのですが、機能が似ているのでどのように使い分ければいいのか悩むときがあります。
lateinit
の特徴
lateinit
には以下のような特徴があります。
- int などのプリミティブ型は指定できない
- var で宣言する必要がある
- 必ず non-null となる
先ほどの例を
lateinit
で書くと次のようになります。class MainActivity : AppCompatActivity() { private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ここで初めて初期化できる。 textView = findViewById(R.id.text_view) } //... }最初の例ではプロパティ textView を null で初期化する必要がありましたが、lateinit を使うことで nun-null として扱うことができるようになりました。
ただし、var で宣言する必要があるため、別の値が再代入されてしまう可能性があるのが注意点です。
by lazy
次に
by lazy
の特徴を見てみます。
- 型に制限なし
- val で宣言する必要がある
- nullable、non-null どちらも可能
- 対象のプロパティが初めて呼び出されたときに初期化される
- 初期化されたら次回以降は必ず同じ値を返す
class MainActivity : AppCompatActivity() { // nullable でも OK private val userData: User? by lazy { fetchUserData() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 初めて呼び出されるタイミングで初期化される。 userData?.let { showUserDate(it) } } //... }
by lazy
は一度初期化されると値はキャッシュされ、以降必ず同じ値を返します。そのため、ビューをby lazy
で初期化してしまうと、ビューに変更を加えようとしても反映してくれません。ビューを扱う場合はlateinit
を使う方がいいでしょう。逆に
by lazy
は nullable なプロパティも扱うことができるため、null が入る可能性がある場合はby lazy
を使うべきかと思います。どのように使い分けるべきか
どちらでも OK な場合や、「プリミティブ型だけど、後から変更を加えたい」といったどちらにも当てはまらない場合もあるかと思いますので、それぞれの状況に合わせて使い分けてみてください。
※ 「プリミティブ型だけど、後から変更を加えたい」 場合には
by Delegates.notNull
が選択肢になるかなと思います。
lateinit
が良さげな場面
- 初期化するタイミングを指定したい場合
- 型が View の場合
- 後から変更を加えたい場合
by lazy
が良さげな場面
- 初期化するタイミングにこだわりがない場合
- 型がプリミティブの場合
- 後から変更されたくない場合
- nullable な場合
- 必ず使われるとは限らない場合(使われない場合は初期化されない)
ちなみに
これは個人的な感想になりますが、どちらでも OK な場合は可読性が高そうな
by lazy
を使うようにしています。
by lazy
はプロパティの宣言と初期化処理を一体化させたハッピーセット? なので、ざわざわ初期化処理を確認しに行く必要がありません。本来
onCreate()
などに記述する初期化処理が減ることで、その他の重要なロジックに集中することができるのも嬉しいポイントです。
- 投稿日:2020-09-19T13:21:19+09:00
Google Playアプリ署名に後からオプトインする(pepkなし)
はじめに
この記事は
apk
だけをアップロードしていく想定でオプトアウトの設定にしたが、途中からAppBundle
を使いたくなりオプトインしたい方に向けた記事です。
(ターゲットがとても少数な気がしますが笑)Java Keystoreで
三種類ほどオプトインのやり方がありますが、
pepk
ファイルを紛失していた自分にとって取り得る手段はJava Keystore
でのオプトインでした。
(pepk
ファイルがあるならAndroid Studioの方を選択すれば素早く対応できると思います。)まず
pepk.jar
を指示通り、PCにダウンロードします。
(ダウンロードしてくる場所はC:\Users\USER\Downloads
で問題ないです。普通にダウンロードすれば勝手にこの場所になると思います。)ダウンロードしたら、該当する
.jks
ファイルか.keystore
ファイルを同じC:\Users\USER\Downloads
ディレクトリに持ってきます。そうしたら、後はコマンドを叩くだけです。
まずコマンドプロンプトを起動し、
C:\Users\USER\Downloads
のディレクトリまで移動します。
その後、以下のコマンドを叩きます。
(私はjavaの実行環境を昔に構築してあったので動いたんですが、そうでない方は動くか分かりません。)コマンドjava -jar pepk.jar --keystore=key.jks --alias=key --output=encrypted_private_key_path --encryptionkey=ここは人によって変わるのでConsoleからコピペ
keystore=
の箇所には移動してきたキーストアのファイル名(拡張子込み)、alias=
にはキーの名前で置き換えてください。すると、キーストアとキーのパスワードを求められるので正しく入力したら
key_path
のファイルがDownloads
ディレクトリに生成されていると思います。それをアップロードすることで無事、オプトインすることができます。
- 投稿日:2020-09-19T01:52:41+09:00
Let’s EncryptでSSL証明書を発行しているサイトが古いAndroidで映らなくなる!?⇒ちょっとだけ延命した!
Android OSのバージョンがAndroid7.1.1未満の機種は、2021年9月30日以降Let’s EncryptでSSL証明書を発行しているサイトが映らなくなります。
これは、Let’s EncryptがSSL証明書に利用しているルート証明書の「DST Root X3」という証明書が2021年9月30日で有効期限が切れるからなのですが、実はそれよりも先に対応をしていないともっと早い段階で映らなくなります。
ちなみに、2020/09/19現在、ネットで拾ってきた情報によると、日本国内のスマホのシェアの内、Android7.1.1未満の機種の割合は5%ほどだそうです。(20人に1人映らなくなる・・・!)
Let’s Encryptとルート証明書の仕組み
Let’s EncryptはSSL証明書にLet's Encryptを運営している非営利団体のISRGが発行しいている「ISRG's root」というルート証明書を利用しているのですが、この証明書が比較的新しいルート証明書(2014年発行)のため、古いOSにはインストールされていません。
なので、今までは「DST Root X3」というルート証明書を中間証明書として、「DST Root X3」経由で認証を行っていました。
今までの認証までのイメージ
- DST Root X3・・・Android7.1.1未満も対応したルート証明書
- ISRG's root・・・Android7.1.1未満は非対応の新しいルート証明書
WEBサイト ⇒ DST Root X3 ⇒ ISRG's root ⇒ 認証OK!
ところが、「DST Root X3」の有効期限が2021年9月30日までとなるため、Let's Encryptの認証は「DST Root X3」の中間証明書を経由しなくなります。
今後の認証までのイメージ
WEBサイト ⇒ ISRG's root ⇒ 認証OK!
つまり、Android7.1.1未満も対応したルート証明書である、「DST Root X3」を中間証明書として経由していたから映っていたAndroid7.1.1未満の機種で、サイトが映らなくなってしまいます。
映らなくなるまでのリミット
では、「DST Root X3」の有効期限が2021年9月30日までに対応すればいいかと言われるとそうでもなくて、Let's Encryptがこの「WEBサイト ⇒ ISRG's root ⇒ 認証OK!」の仕組みに切り替える時期はもっと早いです。(そりゃギリギリなわけないですよね)
なので、Let’s Encryptは、救済策として「古いOSも対応したい人は中間証明書を今まで通り使ってね」と中間証明書を指定して証明書を発行できるようなオプション機能を2020/07/25に実装した上で、2020/9/29に切り替えるとアナウンスしていました。
ました。過去形です。
どうやらその救済策の浸透があまり広まっていないらしく、2020/9/18に公式サイトで、切り替えは2021/01/11に延期すると発表がありました。
なので、実際には2021/01/11までに(正確には2021/01/11以降に証明書を更新するまでに)対策をすれば、古いOSでも引継ぎSSL認証が可能です。
対策
2021/01/11まで
- 証明書の発行に、中間証明書を指定して発行するようにオプションを指定する
2021/09/30まで
Android7.1.1未満とサヨナラする心構えをしておく- Let’s Encrypt以外の証明書を用意
- 別の中間証明書を利用する(未確認)
参考
https://community.letsencrypt.org/t/transition-to-isrgs-root-delayed-until-jan-11-2021/125516