20190521のAndroidに関する記事は17件です。

AndroidにおいてFirebaseの匿名認証はアプリの再インストールで引き継がれるのか

はじめに

結論から書いちゃいますが、Androidでは引き継がれずuserIdが変わりますので気をつけましょう。

概要

StethoはSharedPreferences覗くために入れてます。
細かいバージョン情報等はbuild.gradleを参照。

検証

ソースコード

build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.31'
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.2.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
app/build.gradle
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.masaibar.firebaseanonymousauthsample"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    implementation 'com.google.firebase:firebase-core:16.0.9'
    implementation 'com.google.firebase:firebase-auth:17.0.0'

    debugImplementation 'com.facebook.stetho:stetho:1.5.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

apply plugin: 'com.google.gms.google-services'
MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var auth: FirebaseAuth

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Stetho.initializeWithDefaults(this) // SharedPreference覗くために追加

        auth = FirebaseAuth.getInstance()
        Log.d("current", "currentUser = ${auth.currentUser?.uid.toString()}")
        signInAnonymously()
    }

    private fun signInAnonymously() {
        auth.signInAnonymously()
            .addOnCompleteListener(this) { task ->
                if (task.isSuccessful) {
                    Log.d("success", "currentUser = ${auth.currentUser?.uid.toString()}")
                } else {
                    Log.d("error", task.exception.toString())
                }
            }
    }
}

検証結果

アプリのデータをクリアした後に再度インストールするとログで出力されるuserIdは別のものになっていました。

もう少し追ってみると、どうやらSharedPreferencesにuserIdを保存しているようです。

image.png

ガッツリSharedPreferencesに保存されておりました。
image.png

おわりに

Androidでは再インストールのたびに匿名認証のuserIdは変わるという前提を忘れてシステムを構築すると痛い目を見そうなので気をつけたいです。
ちなみにiOSは再インストール後も跨げるらしいです(NSUserDefaultに保存されている?未検証)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android開発者のためのOCR入門(tess-two編)

はじめに

AndroidアプリでOCRを実装する際、TessaractのAndroid用ラッパーライブラリtess-twoを利用するか、またはML Kit for Firebaseを利用する方法があります。
今回は、前者のtess-twoを使ってギャラリーから画像を選択してその画像にある文字を読み取るアプリを作ってみたいと思います。

サンプルコードはここに置いてあります。

インストール

まずはtess-twoを入れます。また、画像をギャラリーから読み込む際にExifを読み取って画像の向きを正しい方向に直したいのでexifinterfaceも入れておきます。

app/build.gradle.kts
// 略
dependencies {
    implementation("com.rmtheis:tess-two:9.0.0")
    implementation("androidx.exifinterface:exifinterface:1.0.0")
}

続いてappディレクトリ以下に/assets/tessdata/ディレクトリを作成して言語データを配置します。
今回はこんな感じにしました。

app - assets - tessdata
                  |-- jpn.traineddata
                  `-- eng.traineddata

言語データは下記からダウンロードしてください。tess-twoはtesseract3.0.5以下で作成された言語データが必要です。
https://github.com/tesseract-ocr/tessdata/tree/3.04.00

実装

tess-twoの使い方自体は非常に簡単です。わずか5行程度です。

OCRUtil.kt
        val baseApi = TessBaseAPI()
        // initで言語データを読み込む
        baseApi.init(context.getFilesDir().toString(), "jpn")
        // ギャラリーから読み込んだ画像をFile or Bitmap or byte[] or Pix形式に変換して渡してあげる
        baseApi.setImage(bitmap)
        // これだけで読み取ったテキストを取得できる
        val recognizedText = baseApi.utF8Text
        baseApi.end()

ですが注意点が2つあります。

1つ目はbaseApi.initはの中では、言語データをFileでopenしているためFileで読み込める場所にコピーしてあげる必要があります。

OCRUtil.kt
    private val TESS_DATA_DIR = "tessdata" + File.separator
    private val TESS_TRAINED_DATA = arrayListOf("eng.traineddata", "jpn.traineddata")
    private fun copyFiles(context: Context) {
        try {
            TESS_TRAINED_DATA.forEach {
                val filePath = context.filesDir.toString() + File.separator + TESS_DATA_DIR + it

                // assets以下をinputStreamでopenしてbaseApi.initで読み込める領域にコピー
                context.assets.open(TESS_DATA_DIR + it).use {inputStream ->
                    FileOutputStream(filePath).use {outStream ->
                        val buffer = ByteArray(1024)
                        var read = inputStream.read(buffer)
                        while (read != -1) {
                            outStream.write(buffer, 0, read)
                            read = inputStream.read(buffer)
                        }
                        outStream.flush()
                    }
                }

                val file = File(filePath)
                if (!file.exists()) throw FileNotFoundException()
            }
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

2つ目はtess-twoで画像を読み込む場合、画像の向きが正しくないと当然読み込めません。ギャラリーから読み込む場合、画像が回転してしまっている場合があるのでExifを見て正しい方向に直す必要があります。

MainActivity.kt
    // 画像のuri
    uri?.let {
        contentResolver.openFileDescriptor(it, "r").use {parcelFileDescriptorNullable ->
            parcelFileDescriptorNullable?.let {parcelFileDescriptor ->
                val fileDescriptor = parcelFileDescriptor.fileDescriptor
                bitmapOrigin = BitmapFactory.decodeFileDescriptor(fileDescriptor)
                contentResolver.openInputStream(it).use {
                    it?.let {
                        // ExifInterfaceを初期化
                        val exifInterface = ExifInterface(it)
                        val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
                        val degrees = when (orientation) {
                            // 正しい方向の場合は回転させない
                            1 -> { 0f }
                            // 逆向きなので180度回転させる
                            3-> { 180f }
                            // 左向きの画像になってるので90度回転させる
                            6 -> { 90f }
                            // 右向きの画像になってるので270度回転させる
                            8 -> { 270f }
                            else -> { 0f }
                        }
                        val matrix = Matrix()

                        val imageWidth = bitmapOrigin?.getWidth() ?: 0
                        val imageHeight = bitmapOrigin?.getHeight() ?: 0
                        matrix.setRotate(degrees, imageWidth.toFloat() / 2, imageHeight.toFloat() / 2)
                        bitmap = Bitmap.createBitmap(bitmapOrigin!!, 0, 0, imageWidth, imageHeight, matrix, true)
                    }
                }
            }
        }
    }

ここまで実装したら早速動かしてみましょう。今回はこちらの画像から文字を読み取ってみます。

早速アプリを起動して画像を読み込んでみると…

無事、文字を読み取ることが出来るようになりました!が、、精度がいまいちです…

日本語言語データをカスタマイズ

先程の画像を見てみると数字が全然読み取れていないことに気がつくと思います。今回はこの数字の精度を改善してみたいと思います。

数字の誤変換の原因ですが、こちらのサイトを見てみると認識結果の変換マッピングが原因のようです。なのでjpn.unicharambigsを修正してみます。

今回、tesseract-ocrの環境をDockerで用意しました。こちらの環境を使ってカスタマイズします。
https://github.com/tarumzu/OCRSampleAndroid/tree/master/containers

まずは下記の手順でDockerコンテナにログイン

cd containers
docker-compose build
docker-compose up

docker-compose exec tesseract bash

ログインしたら、jpn.traineddataをダウンロード、次にcombine_tessdataコマンドのeオプションでjpn.traineddataからjpn.unicharambigsを取り出します。

wget https://github.com/tesseract-ocr/tessdata/raw/3.04.00/jpn.traineddata
combine_tessdata -e jpn.traineddata jpn.unicharambigs

取り出したらjpn.unicharambigsをvimで開いて下記の行を削除します。これは左側のキャラクタを右側のものに変換するというルールなのですが正直必要ない変換だと思います。

1   l   1   ー 1
1   | 1   ー 1
1   I   1   ー 1
1   1   1   ー 1
1   |   1   ー 1
1   O   1   。 1
1   °  1   。 1

ここまでできたらjpn.traineddataを再作成します。

combine_tessdata -o jpn.traineddata jpn.unicharambigs

では、先程作成した言語データはcontainersに保存されます。この言語データを使って再度画像を読み込んでみましょう。

たったこれだけで数字の精度が劇的に改善できました!

終わりに

簡単にOCRが実装できましたが言語データのカスタマイズは必須ですね…
次はML Kit for FirebaseでOCRを実装してみて違いなどを記事にしてみたいと思います。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidのリリース用署名情報をbuild.gradleで管理する

リリース用の署名情報(alias、パスワード、keystoreなど)を app/build.gradle に組み込みたい場合がある。

やり方は色々あるが、今回は2つの方法を紹介

方法1. app/build.gradleに直書き

一番シンプルで簡単
git管理に含まれちゃうけどね

app/build.gradle
android {
...
    signingConfigs {
        debug {
        }
        release {
            storeFile file('release.keystore')
            keyAlias '***'
            storePassword '***'
            keyPassword '***'
        }
    }

コミット例
https://github.com/banbara23/Android-Kotlin-Lab/commit/a85e0fdb4d81039361fee970d115dd1998c4b46b

個人プロジェクトなどはこれでいいんじゃないかな

方法2. 別のgradleファイルに書き出す

署名情報をgitに含めたくない場合を想定

会社のプログラムはこれでいいんじゃないかな

今更ながらAndroid の keystore と 署名(signingConfigs) の管理・運用について考えてみた
を参考にした

まず release.gradle というファイルを作成する。

release.gradle
signingConfigs {
    debug {
    }
    release {
        storeFile file('release.keystore')
        keyAlias '***'
        storePassword '***'
        keyPassword '***'
    }
}

そのファイルを build.gradle から読み込む。

app/build.gradle
android {
...
    apply from: 'release.gradle', to: android

最後に release.gradle をgitに含めないよう.gitignoreに追加して終わり

app/release.gradle

コミット例
https://github.com/banbara23/Android-Kotlin-Lab/commit/9ab38238a402b2bd1f3104763229aa0cb63c32ec

もし新しい人がプロジェクトにアサインして環境構築する場合、release.gradle をどこからか持ってくる必要があるけどね。

備考

たぶん個人プロジェクトも方法2で管理した方が良いね。

他にもっと良い方法があれば知りたいなー

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Jetpack Composeについて調査 文法編

はじめに

 Google I/O 2019で発表されたJetpack Composeについて調査しました。本記事ではその中でも特徴的な宣言的UIの構築を行うための文法についてまとめてみます。

注意点

本記事は5/21時点でpre-alpha段階のJetpack Composeについて調査を行なっており、今後の変更で大きくインターフェースが変わる可能性があります。

Jetpack Composeの文法

Hello Worldを書く

定番のHello Worldは以下のように書けます。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CraneWrapper {
                MaterialTheme {
                    val textColor = Color(0xFFFF0000.toInt())
                    Text(
                        text = "Hello World!",
                        style = +themeTextStyle { h2.copy(color = textColor) }
                    )
                }
            }
        }
    }
}

これは以下のように表示されます。

文字列を縦方向に二つ表示してみましょう。

otlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CraneWrapper {
                MaterialTheme {
                    val textColor = Color(0xFFFF0000.toInt())
                    Column {
                        Text(
                            text = "Hello",
                            style = +themeTextStyle { h2.copy(color = textColor) }
                        )
                        Text(
                            text = "World!",
                            style = +themeTextStyle { h2.copy(color = textColor) }
                        )
                    }
                }
            }
        }
    }
}

これは以下のように表示されます。

宣言的にUIが構築されているので、結構読みやすいです。

Hello Worldの解説

上記Hello Worldに登場したJetpack Compose固有のワードについて解説します。

setContent

以下のようにActivityの拡張関数として実装されています。

fun Activity.setContent(composable: @Composable() () -> Unit) =
    setContentView(FrameLayout(this).apply { compose(composable) })

受け取ったcomposableな関数をFrameLayoutに描画して自身にsetしています。
基本的にはこの関数を使うことでJetpack Composeで書かれたUIを描画することになります。

CraneWrapper

Context, FocusManager, TextInputServiceを作り、Jetpack Composeの世界でこれらを使えるようにするためのwrapperです。上記Android上でのUI構築に必須なものを作る部分なので、setContent直下にこれを記述し、これに関数を渡すようにUIを設計しないと例外が発生してしまいます。

MaterialTheme

TextButtonなど、各種コンポーネントに適用できるマテリアルデザインなスタイルを提供します。

+themeTextStyle

上記MaterialThemeで提供されている文字に関するスタイルです。これに文字のスタイルを定義する関数を渡すことができます。
この辺りはKotlinDSLの利点が活かされており、適切なスタイル定義をIDEやコンパイラの支援を受けつつ簡単に書くことができるようになっています。
上記コードでは、h2スタイルの色だけを0xFFFF0000(赤色)に変更するようにスタイルを定義しています。

Text

文字列を表現する関数です。Androidで言うTextViewに相当します。

text

この名前で渡した引数がTextで表示される文字列になります。

style

この名前で渡した引数がTextで表示される文字列のスタイルになります。

Column

複数のコンポーネントを縦に並べて表示することができます。Androidのorientation=”vertical”なLinearLayoutに相当します。

コンポーネントを自作する

Jetpack Composeではコンポーネントの自作も簡単に行うことができます。試しに上記Hello Worldを以下のような仕様でコンポーネントにしてみます。

  • Hello World!と表示する
  • styleをパラメータとして適用できる

これは以下のように実装することができます。

@Composable
fun HelloWorld(style: TextStyle? = null) {
    Text(
        text = "Hello World!",
        style = style
    )
}

このようにして作られたコンポーネントは、以下のようにして使うことができます。

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CraneWrapper {
                MaterialTheme {
                    val textColor = Color(0xFFFF0000.toInt())
                    HelloWorld(
                        style = +themeTextStyle { h2.copy(color = textColor) }
                    )
                }
            }
        }
    }
}

コンポーネントの自作は非常に簡単で、@Composableを付与した関数を作るだけです。ただし、いくつか気をつけなければならない点もあり、

  • 副作用を起こさないようにする
  • 関数の引数として渡されたパラメータ以外は使わないようにする(例えばグローバル変数にアクセスしたりしない)

と行った注意点も存在します。

ステートを持つUIを作る

上記Hello Worldはステートを持たないシンプルなUIでした。
今度は「ボタンをタップする度にカウントアップする」と言う機能を持ったUIを作ってみます。
タップされた回数を保持するのでステートを必要とします。

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CraneWrapper {
                MaterialTheme {
                    CountableButton()
                }
            }
        }
    }
}

@Composable
fun CountableButton() {
    val count = +state { 0 }
    Column {
        Button(
            onClick = {
                count.value++
            },
            text = "Tap Me!!"
        )
        Text(
            text = "Tapped count ${count.value}",
            style = +themeTextStyle { h3 }
        )
    }
}

これは以下のように動作します

タップされた回数をカウントしていきます。上の例ではIntを使いましたが、@Modelを使うことで独自に定義した型を使うこともできます。独自の型を利用する形でCountableButtonコンポーネントを書き換えると以下のようになります。これは上記GIF画像と同じ動きをします。

@Composable
fun CountableButton() {
    val count = +state { CountModel() }
    Column {
        Button(
            onClick = {
                count.value.increment()
            },
            text = "Tap Me!!"
        )
        Text(
            text = "Tapped count ${count.value.num}",
            style = +themeTextStyle { h3 }
        )
    }
}

@Model
class CountModel {
    var num = 0
    fun increment() {
        num++
    }
}

ステートを持つUIの解説

上記ステートを持つUIに登場したJetpack Compose固有のワードについて解説します。

+state

この関数はStateインスタンスを作るトップレベル関数です。
この関数にはStateインスタンスが保持するパラメータを初期化する関数を与えることができます。
上記初期化式で初期化されたパラメータを保持したStateインスタンスを返します。

State

stateを管理するクラスです。上記+state関数で作ることができます。
valueプロパティを経由することで、内部で保持しているパラメータを参照、更新することができます。
このようにしてパラメータを更新すると、UIが再描画され、更新後のパラメータが適用されます。

@Model

stateを表現するクラスに付与しなければならないアノテーションです。
+state関数で作成したStateインスタンスで独自のクラスを保持させたい場合には、これを対象のクラスに付与しなければなりません。
これを付与し忘れた場合、コンパイルは通りますが、パラメータに変更があった際にUIが再描画されません。

KTX

以下のようなコードは

Text(text = "Hello World!")

このようにも書けます。

<Text text="Hello World!" />

このJSXのタグにも似た書き方はKTXと呼ばれているそうです。この書き方はKotlinのコンパイラを拡張する方法で実現されているそうです。しかし、KTXは関数呼び出しで記述する方法を開発する以前の古い記法らしく、現在はKTXからそちらに移行している最中だそうです。今後のことを考えるとこの書き方はしない方が良さそうです。

おわりに

Jetpack Composeについて調べてみました。
Androidの既存の書き方(いわゆる宣言的ではない方法)だと色々と辛いところもありましたが、Jetpack Composeが実用化されると、非常に簡単かつ苦しみを少なくしてUIを構築できそうです。
stableになるまでまだまだ時間はかかりそうですが、非常に期待できそうです。

参考

https://developer.android.com/jetpack/compose
https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/compose/README.md
https://medium.com/q42-engineering/android-jetpack-compose-895b7fd04bf4

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Kotlin] AlertDialogをカスタマイズする

AlertBuilderでAlertDialogをカスタマイズします。(カスタムレイアウト作成した方がおすすめですが)

・標準のAlertDialog
Screen Shot 2019-05-17 at 17.51.55.png

・成果物
Screen Shot 2019-05-17 at 17.51.41.png

ソースコード

AlertDialogFragment.kt
// 省略

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

    // ダイアログのタイトルのカスタマイズ
    val text = TextView(context)
    text.text = "Error"
    text.setPadding(0, 25, 0, 25)
    text.textSize = 20F
    text.textAlignment = View.TEXT_ALIGNMENT_CENTER
    text.setTextColor(resources.getColor(R.color.white, null))
    text.background = resources.getDrawable(R.color.green, null)

    val builder = activity?.let { AlertDialog.Builder(it) }

    // setTitleではなくsetCustomTitleを使う
    builder!!.setCustomTitle(text)
            .setMessage(message)
            .setPositiveButton(okButtonName, onOkClickListener)

    val dialog = builder.show()

    // OKボタンを中央揃えに
    val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
    button.gravity = Gravity.CENTER
    button.setTextColor(resources.getColor(R.color.colorWhite, null))
    button.setPadding(0, -30, 0, -30)
    button.background = resources.getDrawable(R.drawable.green_button, null)
    button.textSize = 15F

    val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 55)
    layoutParams.gravity = Gravity.CENTER
    layoutParams.marginStart = 250
    layoutParams.marginEnd = 250
    layoutParams.topMargin = 20
    layoutParams.bottomMargin = 15
    button.layoutParams = layoutParams

    this.isCancelable = false

    return dialog
}

二つ以上のボタンがある時

AlertDialogFragment.kt
// 省略

        builder!!.setCustomTitle(text)
                .setMessage(message)
                .setPositiveButton("OK",onOkClickListener)
                .setNegativeButton("CANCEL",onCancelClickListener)
                .setOnDismissListener(onDismissListener)

        val dialog = builder.show()

        val okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
        okButton.gravity = Gravity.CENTER
        okButton.setTextColor(resources.getColor(R.color.white, null))
        okButton.setPadding(70, -30, 70, -30)
        okButton.background = resources.getDrawable(R.drawable.green_button, null)
        okButton.textSize = 15F

        val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 55)
        layoutParams.gravity = Gravity.CENTER
        layoutParams.marginStart = 30
        layoutParams.marginEnd = 160
        layoutParams.topMargin = 20
        layoutParams.bottomMargin = 10
        okButton.layoutParams = layoutParams

        val cancelButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE)
        cancelButton.gravity = Gravity.CENTER
        cancelButton.setTextColor(resources.getColor(R.color.green, null))
        cancelButton.setPadding(50, -30, 50, -30)
        cancelButton.textSize = 15F

        val layoutParams2 = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 55)
        layoutParams2.gravity = Gravity.CENTER
        layoutParams2.marginStart = 60
        layoutParams2.marginEnd = 30
        layoutParams2.topMargin = 20
        layoutParams2.bottomMargin = 10
        cancelButton.layoutParams = layoutParams2

// 省略

成果物

Screen Shot 2019-05-17 at 17.51.49.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL 上で Vue.js を用いた Android (iOS) アプリを開発する

はじめに

仕事中、睡魔と戦いながら、Android アプリを作ろうと思いました。

ところが私は、C# は触った程度だし Java はコンソールアプリしかやったことがなく GUI はわかりません。そこで調べてみると、HTML で作れるそうじゃないですか。社会人二年目で Web 系しかしていない私にはありがたい話です。

もう少し欲を出して、業務で使用している Vue.js を使えないかなーと思ったら、ちょうどいいのがあるじゃないですか。

というわけで、Windows Subsystem for Linux (WSL) 上で Vue.js を用いて Android アプリを開発するための手順を紹介します。

つかうもの

  • WSL : Windows 環境がカオスにならないようにするため
  • Vue.js : 今回使いたかったもの
  • nativescript : Javascript でアプリ開発できるようにするすごいやつ
  • アンドロイド実機 : 開発時の実機との連携がすごい

手順

WSL の有効化から実機での起動まで端折らずにやるよ。

  1. WSL 環境の構築
  2. Ubuntu への各種インストール

1. WSL 環境の構築

まずは WSL を有効化し、Ubuntu をインストールします。

  1. コントロールパネル > プログラムのアンインストール を選択

    WSL1.png

  2. 左側 windows の機能の有効化または無効化を選択

    WSL2.png

  3. Windows Subsystem for Linux にチェックを入れ有効化

    WSL3.png

  4. インストール完了次第再起動

  5. (念の為)設定 > 更新とセキュリティ > 開発者向け > 開発者モードを有効にする

    WSL4.png

  6. スタートから Microsoft Store を開く

    WSL5.png

  7. 「Ubuntu」で検索し、Ubuntu を選択

    WSL6.png

  8. 入手をクリックでインストール

    WSL7.png

  9. スタートから Ubuntu を開く

    WSL8.png

  10. インストール完了を待ち、ユーザー名とパスワードを入力(パスワードは表示されないので注意)

    WSL9.png

  11. リポジトリを日本のサーバに変更(sudo 利用時、パスワード入力を求められます)

    $ sudo sed -i -e 's%http://.*.ubuntu.com%http://ftp.jaist.ac.jp/pub/Linux%g' /etc/apt/sources.list
    
  12. パッケージのアップデート

    $ sudo apt update
    $ sudo apt upgrade
    
  13. Windows との共有ディレクトリを作成

    $ ln -s /mnt/c/Users/(username)/shareWithUbuntu ~/shareWithWindows # (username)はWindows側のユーザ名に置き換え
    

以上で WSL 環境の構築は完了です。WSL 環境をリセットしたい場合は
スタート > Ubuntu 右クリック > アプリの設定 > リセット
から可能です。リセット後は Ubuntu を起動するとまたアカウント設定から始まります。環境破壊と再構築、簡単ですね。

2. Ubuntu への各種インストール

NativeScript-Vue での開発に必要なものをインストールしていきます。

  1. Node.js のインストール

    $ sudo apt install -y nodejs npm # node、npmのインストール
    $ sudo npm install n -g          # n package導入
    $ sudo n stable                  # nを使用してnodeのstable版をインストール
    $ sudo apt purge -y nodejs npm   # 最初に入れた古いnode、npmは削除
    $ exec $SHELL -l                 # 再ログイン
    $ node -v                        # バージョン確認
    
  2. NativeScript CLI のインストール

    $ sudo npm install -g nativescript
    
  3. G++ (C++ のコンパイラ)のインストール

    $ sudo apt install g++
    
  4. JDK8 の設定

    $ sudo apt install openjdk-8-jdk         # JDK8インストール
    $ sudo update-alternatives --config java # JAVAのバージョンが複数あればJDK8を選択
    
  5. 環境変数 JAVA_HOME の追加

    1. ~/.bashrc を開く

      $ vi ~/.bashrc
      
    2. Iキーで編集モードに入り、一番下に以下の行を追加

      $ export JAVA_HOME=$(update-alternatives --query javac | sed -n -e 's/Best: *\(.*\)\/bin\/javac/\1/p')
      
    3. ESCキーを押し、:wq と入力

  6. Android SDK のダウンロード(Windows 側での操作

    1. ここ の下の方の「Command line tools only」から Linux のものをダウンロード

      WSL10.png

    2. ダウンロードした zip ファイルを解凍

    3. 解凍した中にある tools ディレクトリを shareWithUbuntu ディレクトリに移動

  7. /usr/lib/android/sdk/ ディレクトリを作成

    $ sudo mkdir -p /usr/lib/android/sdk/
    
  8. shareWithWindows ディレクトリ内の tools ディレクトリを /usr/lib/android/sdk/ に移動

    $ sudo mv ~/shareWithWindows/tools /usr/lib/android/sdk/
    
  9. 環境変数 ANDROID_HOME の追加

    1. ~/.bashrc を開く

      $ vi ~/.bashrc
      
    2. Iキーで編集モードに入り、一番下に以下の行を追加

      $ export ANDROID_HOME="/usr/lib/android/sdk/"
      $ export PATH="${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/"
      
    3. ESCキーを押し、:wq と入力

  10. 再ログイン

    $ exec $SHELL -l
    
  11. 必要なパッケージのインストール

    $ sudo $ANDROID_HOME/tools/bin/sdkmanager "tools" "emulator" "platform-tools" "platforms;android-28" "build-tools;28.0.3" "extras;android;m2repository" "extras;google;m2repository"
    
  12. 再ログイン

    $ exec $SHELL -l
    
  13. 以下のコマンドを実行し、No issues were detected. が出力されれば成功

    $ tns doctor # 統計情報を自動送信して良いか聞かれるが n で構わない
    

NativeScript 関連のインストールはここまで。

3. アプリケーションの作成

アプリケーションを作成します。

  1. Vue 関連パッケージのインストール

    $ sudo npm install -g @vue/cli @vue/cli-init
    
  2. プロジェクトを作成するディレクトリを作成、移動

    $ mkdir workspace && cd workspace
    
  3. プロジェクトの作成

    $ vue init nativescript-vue/vue-cli-template sample-app
    
    # 以下対話形式(vue-devtools以外はデフォルトでもOK)
    ? Project name sample-app                                              # プロジェクト名
    ? Project description A native application built with NativeScript-Vue # プロジェクトの説明
    ? Application name NativeScript-Vue Application                        # アプリ名
    ? Unique application identifier org.nativescript.application           # アプリケーション任意のID
    ? Project version 1.0.0                                                # プロジェクトのバージョン
    ? Author                                                               # 開発者
    ? License MIT                                                          # ライセンスの種類
    ? Select the programming language javascript                           # 使用する言語
    ? Select a preset (more coming soon) Simple                            # 用意するアプリのテンプレート
    ? Install vuex? (state management) Yes                                 # Vuexを使用するか(デフォルトはNo)
    ? Install vue-devtools? No                                             # Vuedevtoolsを使用するか(デフォルトはYes)
    ? Color scheme none                                                    # カラーテーマ
    
  4. プロジェクトディレクトリに移動し、パッケージインストール

    $ cd sample-app && npm install
    
  5. アプリを起動

    $ tns preview --bundle
    

    WSL11.png

プロジェクトの作成からアプリ実行までは簡単ですね。コンソールに表示される QR コードは後ほど利用するので消さないでください。

実機で表示

作成したアプリケーションを実機に表示します。ここでは、Android 側にアプリケーションの導入が必要となります。

  1. Google Play から NativeScript Playground をインストール
  2. Google Play から NativeScript Preview をインストール

    WSL12.png

  3. NativeScript Playground を開き、 Scan QR code をタップ

    WSL13.png

  4. コンソールの QR コードを読み込む

  5. 完了

    WSL14.png

QR の読み込み後に少し時間がかかりますが、個人的には QR 読み込みだけで実機で試せるのは革命的です。

おわりに

NativeScript-Vue のおかげで睡魔に打ち勝つことができました。が、この記事を書いている間は何度も睡魔に負けています。何か実用的なものを NativeScript-Vue で作成してまた記事を上げたいですが、今度は仕事が忙しくなり睡魔に襲われる機会が減りそうなので、なかったことになるかもしれないです。

ここを見れば誰でも同じことができるように心がけていますが、訳わかんねー記述があればコメントください。まあ私も素人ですが。

参考サイト

Windows Subsystem for Linuxをインストールしてみよう!
Ubuntuに最新のNode.jsを難なくインストールする
NativeScript-Vue / Installation(英語)
NativeScript Advanced Setup: Linux(英語)
Windows 10のWindows Subsystem for Linux(WSL)を日常的に活用する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL 上で Vue.js を用いた Android アプリを開発する

はじめに

仕事中、睡魔と戦いながら、Android アプリを作ろうと思いました。

ところが私は、C# は触った程度だし Java はコンソールアプリしかやったことがなく GUI はわかりません。そこで調べてみると、HTML で作れるそうじゃないですか。社会人二年目で Web 系しかしていない私にはありがたい話です。

もう少し欲を出して、業務で使用している Vue.js を使えないかなーと思ったら、ちょうどいいのがあるじゃないですか。

というわけで、Windows Subsystem for Linux (WSL) 上で Vue.js を用いて Android アプリを開発するための手順を紹介します。

つかうもの

  • WSL : Windows 環境がカオスにならないようにするため
  • Vue.js : 今回使いたかったもの
  • nativescript : Javascript でアプリ開発できるようにするすごいやつ
  • アンドロイド実機 : 開発時の実機との連携がすごい

手順

WSL の有効化から実機での起動まで端折らずにやるよ。

  1. WSL 環境の構築
  2. Ubuntu への各種インストール

1. WSL 環境の構築

まずは WSL を有効化し、Ubuntu をインストールします。

  1. コントロールパネル > プログラムのアンインストール を選択

    WSL1.png

  2. 左側 windows の機能の有効化または無効化を選択

    WSL2.png

  3. Windows Subsystem for Linux にチェックを入れ有効化

    WSL3.png

  4. インストール完了次第再起動

  5. (念の為)設定 > 更新とセキュリティ > 開発者向け > 開発者モードを有効にする

    WSL4.png

  6. スタートから Microsoft Store を開く

    WSL5.png

  7. 「Ubuntu」で検索し、Ubuntu を選択

    WSL6.png

  8. 入手をクリックでインストール

    WSL7.png

  9. スタートから Ubuntu を開く

    WSL8.png

  10. インストール完了を待ち、ユーザー名とパスワードを入力(パスワードは表示されないので注意)

    WSL9.png

  11. リポジトリを日本のサーバに変更(sudo 利用時、パスワード入力を求められます)

    $ sudo sed -i -e 's%http://.*.ubuntu.com%http://ftp.jaist.ac.jp/pub/Linux%g' /etc/apt/sources.list
    
  12. パッケージのアップデート

    $ sudo apt update
    $ sudo apt upgrade
    
  13. Windows との共有ディレクトリを作成

    $ ln -s /mnt/c/Users/(username)/shareWithUbuntu ~/shareWithWindows # (username)はWindows側のユーザ名に置き換え
    

以上で WSL 環境の構築は完了です。WSL 環境をリセットしたい場合は
スタート > Ubuntu 右クリック > アプリの設定 > リセット
から可能です。リセット後は Ubuntu を起動するとまたアカウント設定から始まります。環境破壊と再構築、簡単ですね。

2. Ubuntu への各種インストール

NativeScript-Vue での開発に必要なものをインストールしていきます。

  1. Node.js のインストール

    $ sudo apt install -y nodejs npm # node、npmのインストール
    $ sudo npm install n -g          # n package導入
    $ sudo n stable                  # nを使用してnodeのstable版をインストール
    $ sudo apt purge -y nodejs npm   # 最初に入れた古いnode、npmは削除
    $ exec $SHELL -l                 # 再ログイン
    $ node -v                        # バージョン確認
    
  2. NativeScript CLI のインストール

    $ sudo npm install -g nativescript
    
  3. G++ (C++ のコンパイラ)のインストール

    $ sudo apt install g++
    
  4. JDK8 の設定

    $ sudo apt install openjdk-8-jdk         # JDK8インストール
    $ sudo update-alternatives --config java # JAVAのバージョンが複数あればJDK8を選択
    
  5. 環境変数 JAVA_HOME の追加

    1. ~/.bashrc を開く

      $ vi ~/.bashrc
      
    2. Iキーで編集モードに入り、一番下に以下の行を追加

      $ export JAVA_HOME=$(update-alternatives --query javac | sed -n -e 's/Best: *\(.*\)\/bin\/javac/\1/p')
      
    3. ESCキーを押し、:wq と入力

  6. Android SDK のダウンロード(Windows 側での操作

    1. ここ の下の方の「Command line tools only」から Linux のものをダウンロード

      WSL10.png

    2. ダウンロードした zip ファイルを解凍

    3. 解凍した中にある tools ディレクトリを shareWithUbuntu ディレクトリに移動

  7. /usr/lib/android/sdk/ ディレクトリを作成

    $ sudo mkdir -p /usr/lib/android/sdk/
    
  8. shareWithWindows ディレクトリ内の tools ディレクトリを /usr/lib/android/sdk/ に移動

    $ sudo mv ~/shareWithWindows/tools /usr/lib/android/sdk/
    
  9. 環境変数 ANDROID_HOME の追加

    1. ~/.bashrc を開く

      $ vi ~/.bashrc
      
    2. Iキーで編集モードに入り、一番下に以下の行を追加

      $ export ANDROID_HOME="/usr/lib/android/sdk/"
      $ export PATH="${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/"
      
    3. ESCキーを押し、:wq と入力

  10. 再ログイン

    $ exec $SHELL -l
    
  11. 必要なパッケージのインストール

    $ sudo $ANDROID_HOME/tools/bin/sdkmanager "tools" "emulator" "platform-tools" "platforms;android-28" "build-tools;28.0.3" "extras;android;m2repository" "extras;google;m2repository"
    
  12. 再ログイン

    $ exec $SHELL -l
    
  13. 以下のコマンドを実行し、No issues were detected. が出力されれば成功

    $ tns doctor # 統計情報を自動送信して良いか聞かれるが n で構わない
    

NativeScript 関連のインストールはここまで。

3. アプリケーションの作成

アプリケーションを作成します。

  1. Vue 関連パッケージのインストール

    $ sudo npm install -g @vue/cli @vue/cli-init
    
  2. プロジェクトを作成するディレクトリを作成、移動

    $ mkdir workspace && cd workspace
    
  3. プロジェクトの作成

    $ vue init nativescript-vue/vue-cli-template sample-app
    
    # 以下対話形式(vue-devtools以外はデフォルトでもOK)
    ? Project name sample-app                                              # プロジェクト名
    ? Project description A native application built with NativeScript-Vue # プロジェクトの説明
    ? Application name NativeScript-Vue Application                        # アプリ名
    ? Unique application identifier org.nativescript.application           # アプリケーション任意のID
    ? Project version 1.0.0                                                # プロジェクトのバージョン
    ? Author                                                               # 開発者
    ? License MIT                                                          # ライセンスの種類
    ? Select the programming language javascript                           # 使用する言語
    ? Select a preset (more coming soon) Simple                            # 用意するアプリのテンプレート
    ? Install vuex? (state management) Yes                                 # Vuexを使用するか(デフォルトはNo)
    ? Install vue-devtools? No                                             # Vuedevtoolsを使用するか(デフォルトはYes)
    ? Color scheme none                                                    # カラーテーマ
    
  4. プロジェクトディレクトリに移動し、パッケージインストール

    $ cd sample-app && npm install
    
  5. アプリを起動

    $ tns preview --bundle
    

    WSL11.png

プロジェクトの作成からアプリ実行までは簡単ですね。コンソールに表示される QR コードは後ほど利用するので消さないでください。

実機で表示

作成したアプリケーションを実機に表示します。ここでは、Android 側にアプリケーションの導入が必要となります。

  1. Google Play から NativeScript Playground をインストール
  2. Google Play から NativeScript Preview をインストール

    WSL12.png

  3. NativeScript Playground を開き、 Scan QR code をタップ

    WSL13.png

  4. コンソールの QR コードを読み込む

  5. 完了

    WSL14.png

QR の読み込み後に少し時間がかかりますが、個人的には QR 読み込みだけで実機で試せるのは革命的です。

おわりに

NativeScript-Vue のおかげで睡魔に打ち勝つことができました。が、この記事を書いている間は何度も睡魔に負けています。何か実用的なものを NativeScript-Vue で作成してまた記事を上げたいですが、今度は仕事が忙しくなり睡魔に襲われる機会が減りそうなので、なかったことになるかもしれないです。

ここを見れば誰でも同じことができるように心がけていますが、訳わかんねー記述があればコメントください。まあ私も素人ですが。

参考サイト

Windows Subsystem for Linuxをインストールしてみよう!
Ubuntuに最新のNode.jsを難なくインストールする
NativeScript-Vue / Installation(英語)
NativeScript Advanced Setup: Linux(英語)
Windows 10のWindows Subsystem for Linux(WSL)を日常的に活用する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】WSL 上で Vue.js を用いた Android アプリを開発する

はじめに

仕事中、睡魔と戦いながら、Android アプリを作ろうと思いました。

ところが私は、C# は触った程度だし Java はコンソールアプリしかやったことがなく GUI はわかりません。そこで調べてみると、HTML で作れるそうじゃないですか。社会人二年目で Web 系しかしていない私にはありがたい話です。

もう少し欲を出して、業務で使用している Vue.js を使えないかなーと思ったら、ちょうどいいのがあるじゃないですか。

というわけで、Windows Subsystem for Linux (WSL) 上で Vue.js を用いて Android アプリを開発するための手順を紹介します。

つかうもの

  • WSL : Windows 環境がカオスにならないようにするため
  • Vue.js : 今回使いたかったもの
  • nativescript : Javascript でアプリ開発できるようにするすごいやつ
  • アンドロイド実機 : 開発時の実機との連携がすごい

手順

WSL の有効化から実機での起動まで端折らずにやるよ。

  1. WSL 環境の構築
  2. Ubuntu への各種インストール

1. WSL 環境の構築

まずは WSL を有効化し、Ubuntu をインストールします。

  1. コントロールパネル > プログラムのアンインストール を選択

    WSL1.png

  2. 左側 windows の機能の有効化または無効化を選択

    WSL2.png

  3. Windows Subsystem for Linux にチェックを入れ有効化

    WSL3.png

  4. インストール完了次第再起動

  5. (念の為)設定 > 更新とセキュリティ > 開発者向け > 開発者モードを有効にする

    WSL4.png

  6. スタートから Microsoft Store を開く

    WSL5.png

  7. 「Ubuntu」で検索し、Ubuntu を選択

    WSL6.png

  8. 入手をクリックでインストール

    WSL7.png

  9. スタートから Ubuntu を開く

    WSL8.png

  10. インストール完了を待ち、ユーザー名とパスワードを入力(パスワードは表示されないので注意)

    WSL9.png

  11. リポジトリを日本のサーバに変更(sudo 利用時、パスワード入力を求められます)

    $ sudo sed -i -e 's%http://.*.ubuntu.com%http://ftp.jaist.ac.jp/pub/Linux%g' /etc/apt/sources.list
    
  12. パッケージのアップデート

    $ sudo apt update
    $ sudo apt upgrade
    
  13. Windows との共有ディレクトリを作成

    $ ln -s /mnt/c/Users/(username)/shareWithUbuntu ~/shareWithWindows # (username)はWindows側のユーザ名に置き換え
    

以上で WSL 環境の構築は完了です。WSL 環境をリセットしたい場合は
スタート > Ubuntu 右クリック > アプリの設定 > リセット
から可能です。リセット後は Ubuntu を起動するとまたアカウント設定から始まります。環境破壊と再構築、簡単ですね。

2. Ubuntu への各種インストール

NativeScript-Vue での開発に必要なものをインストールしていきます。

  1. Node.js のインストール

    $ sudo apt install -y nodejs npm # node、npmのインストール
    $ sudo npm install n -g          # n package導入
    $ sudo n stable                  # nを使用してnodeのstable版をインストール
    $ sudo apt purge -y nodejs npm   # 最初に入れた古いnode、npmは削除
    $ exec $SHELL -l                 # 再ログイン
    $ node -v                        # バージョン確認
    
  2. NativeScript CLI のインストール

    $ sudo npm install -g nativescript
    
  3. G++ (C++ のコンパイラ)のインストール

    $ sudo apt install g++
    
  4. JDK8 の設定

    $ sudo apt install openjdk-8-jdk         # JDK8インストール
    $ sudo update-alternatives --config java # JAVAのバージョンが複数あればJDK8を選択
    
  5. 環境変数 JAVA_HOME の追加

    1. ~/.bashrc を開く

      $ vi ~/.bashrc
      
    2. Iキーで編集モードに入り、一番下に以下の行を追加

      $ export JAVA_HOME=$(update-alternatives --query javac | sed -n -e 's/Best: *\(.*\)\/bin\/javac/\1/p')
      
    3. ESCキーを押し、:wq と入力

  6. Android SDK のダウンロード(Windows 側での操作

    1. ここ の下の方の「Command line tools only」から Linux のものをダウンロード

      WSL10.png

    2. ダウンロードした zip ファイルを解凍

    3. 解凍した中にある tools ディレクトリを shareWithUbuntu ディレクトリに移動

  7. /usr/lib/android/sdk/ ディレクトリを作成

    $ sudo mkdir -p /usr/lib/android/sdk/
    
  8. shareWithWindows ディレクトリ内の tools ディレクトリを /usr/lib/android/sdk/ に移動

    $ sudo mv ~/shareWithWindows/tools /usr/lib/android/sdk/
    
  9. 環境変数 ANDROID_HOME の追加

    1. ~/.bashrc を開く

      $ vi ~/.bashrc
      
    2. Iキーで編集モードに入り、一番下に以下の行を追加

      $ export ANDROID_HOME="/usr/lib/android/sdk/"
      $ export PATH="${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/"
      
    3. ESCキーを押し、:wq と入力

  10. 再ログイン

    $ exec $SHELL -l
    
  11. 必要なパッケージのインストール

    $ sudo $ANDROID_HOME/tools/bin/sdkmanager "tools" "emulator" "platform-tools" "platforms;android-28" "build-tools;28.0.3" "extras;android;m2repository" "extras;google;m2repository"
    
  12. 再ログイン

    $ exec $SHELL -l
    
  13. 以下のコマンドを実行し、No issues were detected. が出力されれば成功

    $ tns doctor # 統計情報を自動送信して良いか聞かれるが n で構わない
    

NativeScript 関連のインストールはここまで。

3. アプリケーションの作成

アプリケーションを作成します。

  1. Vue 関連パッケージのインストール

    $ sudo npm install -g @vue/cli @vue/cli-init
    
  2. プロジェクトを作成するディレクトリを作成、移動

    $ mkdir workspace && cd workspace
    
  3. プロジェクトの作成

    $ vue init nativescript-vue/vue-cli-template sample-app
    
    # 以下対話形式(vue-devtools以外はデフォルトでもOK)
    ? Project name sample-app                                              # プロジェクト名
    ? Project description A native application built with NativeScript-Vue # プロジェクトの説明
    ? Application name NativeScript-Vue Application                        # アプリ名
    ? Unique application identifier org.nativescript.application           # アプリケーション任意のID
    ? Project version 1.0.0                                                # プロジェクトのバージョン
    ? Author                                                               # 開発者
    ? License MIT                                                          # ライセンスの種類
    ? Select the programming language javascript                           # 使用する言語
    ? Select a preset (more coming soon) Simple                            # 用意するアプリのテンプレート
    ? Install vuex? (state management) Yes                                 # Vuexを使用するか(デフォルトはNo)
    ? Install vue-devtools? No                                             # Vuedevtoolsを使用するか(デフォルトはYes)
    ? Color scheme none                                                    # カラーテーマ
    
  4. プロジェクトディレクトリに移動し、パッケージインストール

    $ cd sample-app && npm install
    
  5. アプリを起動

    $ tns preview --bundle
    

    WSL11.png

プロジェクトの作成からアプリ実行までは簡単ですね。コンソールに表示される QR コードは後ほど利用するので消さないでください。

実機で表示

作成したアプリケーションを実機に表示します。ここでは、Android 側にアプリケーションの導入が必要となります。

  1. Google Play から NativeScript Playground をインストール
  2. Google Play から NativeScript Preview をインストール

    WSL12.png

  3. NativeScript Playground を開き、 Scan QR code をタップ

    WSL13.png

  4. コンソールの QR コードを読み込む

  5. 完了

    WSL14.png

QR の読み込み後に少し時間がかかりますが、個人的には QR 読み込みだけで実機で試せるのは革命的です。

おわりに

NativeScript-Vue のおかげで睡魔に打ち勝つことができました。が、この記事を書いている間は何度も睡魔に負けています。何か実用的なものを NativeScript-Vue で作成してまた記事を上げたいですが、今度は仕事が忙しくなり睡魔に襲われる機会が減りそうなので、なかったことになるかもしれないです。

ここを見れば誰でも同じことができるように心がけていますが、訳わかんねー記述があればコメントください。まあ私も素人ですが。

参考サイト

Windows Subsystem for Linuxをインストールしてみよう!
Ubuntuに最新のNode.jsを難なくインストールする
NativeScript-Vue / Installation(英語)
NativeScript Advanced Setup: Linux(英語)
Windows 10のWindows Subsystem for Linux(WSL)を日常的に活用する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TaskerでSesame miniを手ぶら解錠(RESTask編)

はじめに

SESAMEアプリの手ぶら解錠。とても便利なのですが
どうしても安定性が低くドアの前で待たされることもしばしば。
あれと思ってスマホを開くとようやく開いたり。

特にうちでは2台使っているので2台とも手ぶらで開くというのは
5回に1回もないくらいです。

投稿時点でもアプリの改善などをしてくれているようなので
それに期待しつつそれまでは自前で対応しようということで
AndroidのアプリであるTaskerを使って手ぶら解錠の仕組みを作ります。

HTTP Shortcutsを使った方法はこちらの記事にまとめています。
TaskerでSesame miniを手ぶら解錠(HTTP Shortcuts編)

準備するもの

手順1. ダッシュボードで情報収集

APIキーの取得

CANDY HOUSEのダッシュボードにログインして
下の方のAPI settingsからSEND CODEを押して
送られてきた認証コードを入力してConfirm。

candy1.jpg
CANDY HOUSEダッシュボード
https://my.candyhouse.co

API KeysでADDを押すと数秒してAPIキーが表示されます。
これは後からは参照できなくなるので必ずコピーしておきましょう。

デバイスIDの確認

Sesame Listから操作したいSesameを選択してデバイス画面に移動。
(カーソルがリンク表示になりませんがそのままクリックできます)
各デバイスの画面のURLにデバイスIDが含まれています。

https://my.candyhouse.co/#/sesameList/0/********-****-****-****-************

この****の部分の文字列をデバイスIDとして記録しておきます。

手順2.Taskerの設定

TaskerとRESTaskをインストールして設定をしていきます。
最終的にプロファイルを2つ、タスクを3つ作ります。

手順2-1.タスクの作成と確認アクションの追加

タスクとは動作(アクション)を組み合わせたものです。
作成したいのは解錠するタスクですが、
まずは施錠確認のアクションを作成しましょう。

タスクタブの右下の+ボタンでタスクを作成できます。
名前を入力してタスク編集画面に入り、
右下の+ボタンでアクションを追加します。

Sesame APIにGETしたいのでTasker自体のアクションである
ネット内のHTTP:GETメソッドを使いたいところなのですが
このメソッドではREST APIが使えません。
そこでプラグインのRESTaskを使用します。

アクションの追加でプラグインからRESTaskを選択し
設定の鉛筆アイコンを押すとRESTaskの画面に変わります。

ここで設定するのはHostの部分だけです。

https://api.candyhouse.co/public/sesame/********-****-****-****-************
注)****部分はデバイスID

1 (7).png

次にHeadersタブに移り以下のヘッダーを追加します。

  • Nameは「Authorization」
  • Valueには手順1で取得したAPIキー を入力します。

1 (8).png

これで右上の▶を押すと以下のようなウィンドウが表示されます。

1 (9).png

ざっくりと内容を解説すると、
- 「locked」が施錠状態で、閉まっていればtrue、開いていればfalse
- 「battery」は電池の残量
- 「responsive」はSESAME miniが現在操作可能であるか
を表しています。

手動で鍵を開閉して、lockedの状態が反映されることを確認してみましょう。
確認できたら右上の保存ボタンを押して保存します。

手順2-2.解錠アクションの追加

そのまま解錠アクションも作成します。
手順2ー1と同様にRESTaskの画面に入り、

  • Request TypeをPOST
  • Hostは手順2ー1と同じ
https://api.candyhouse.co/public/sesame/********-****-****-****-************
注)****部分はDevice ID

1 (5).png

  • Enable custom bodyにチェックを入れて
  • Custom bodyに以下を記入します。
{"command":"lock"}

1 (4).png

次にHeadersタブで以下の2つのヘッダーを追加します。

Header Value
Authorization 手順1で取得したAPIキー
Content-Type application/json

1 (6).png

これで右上の▶を押すとTaskIDが表示されて鍵が開きます。
動作が確認できたら保存を押してTaskerに戻ります。

解錠を実行して解錠されたことを確認するために
確認アクションの上に解錠アクションを移動して
その間にタスク→待機/Waitで5秒のウェイトを入れます。

手順2-3.解錠アクションの結果の取得

これでWeb APIを動かす準備は整ったのですが
Taskerからの実行では結果のウィンドウが表示されないので
このままでは施錠結果を確認することが出来ません。
そこで確認アクションの結果を取得するアクションを追加します。

コード→Javascript(小)/Javascriptletを作成し
コードの部分に以下を入力します。

var locked = JSON.parse(rtres).locked

RESTaskの実行結果は%rtresという変数に入るので
その中からlockedの内容を取得しています。

ちなみに%rtresをそのまま通知などで表示させると
手順2ー1で見たウィンドウの内容が表示されます。

これで%lockedに施錠状態(trueかfalse)が入ったので
それを表示したり、分岐したりして使います。

1.png

これを解錠という名前のタスクとして保存しましょう。
ちなみに通知の内容はこんな感じにしています。

4.png

手順3.Sesame 発見プロファイルを作成

プロファイルはトリガーとアクションを関連付けるものです。
今回はSesame miniをBluetoothで検知できたら解錠します。

公式アプリでは接続できたらBluetooth経由で解錠しているので
接続自体が不安定だったり接続するまで時間がかかったりしますが、
Sesame miniのBluetoothを検知したらWeb APIで解錠するので
Bluetooth接続の不安定性に左右されません。

右下の+ボタンでプロファイルを作成して名前(「発見」等)を入力し
状態カテゴリーから「ネット」→「近くのBluetooth」を選択。
住所欄で虫眼鏡の検索アイコンを押して
Sesame miniのMACアドレスを指定します。

CANDYHOUSEのDASHBOARDのSesame List
Serialに表示されているのがMACアドレスです)

Low-Energy(LE)機器と未接続の機器にチェックを入れて戻り、
最後にタスクとして解錠を指定します。

1 (2).png

これでSesame miniを検知すると解錠タスクが起動します。

これだけでも近づけば解錠するという機能は出来たのですが
家の中にいる時でも玄関に近づくたびに解錠されてしまいます。

そこで一度解錠したらこのプロファイルは無効にするようにします。

解錠タスクの編集画面に移ってアクションの追加で
「Tasker」→「プロファイルの状態」から名前に「発見」を指定し
セットに「オフ」を指定します。

5.png

これで一度解錠したらその後は近付いても解錠されません。
このままではずっと解錠されなくなってしまうので、
次は自宅の付近に近付いたらプロファイルを有効にします。

手順4.発見オン/発見オフ タスクを作成

発見をオン/オフするタスクを作成します。
発見オンと名付けたタスクを作成し、
「Tasker」→「プロファイルの状態」から名前に「発見」を指定し
セットに今回は「オン」を指定します。
同様に発見オフというタスクも作成します。
動作確認のために通知を作成しておいた方がいいでしょう。

手順5.自宅付近プロファイルを作成

プロファイルの作成から現在地を選択し、自宅の位置を指定します。
範囲は30mか50mがいいと思います。
このプロファイルにはタスクとして発見オンを指定します。

指定した発見オンの部分を長押しすると出口タスクを追加できます。
このトリガーが無効になったときに発動するタスクを指定するもので、
ここでは発見オフを指定してバッテリー消費を抑えます。

p (6).png

まとめ

以上で、自宅から30mか50m以内に入ったら発見プロファイルが有効になり
周囲のBluetoothを探し始めます。

BluetoothでSesame miniが検知されたら解錠タスクが起動し
Web APIから解錠されます。

解錠されたら発見プロファイルは無効化されるため
再度玄関に近付いても解錠されることはありません。

自宅付近を通過した際には近付いた時に発見が有効になりますが
離れると発見が無効化されるためバッテリー消費を抑えます。

おわりに

セキュリティには気をつけて運用してください。

Taskerのデフォルトの設定では画面消灯中の監視は
600秒になっており10分毎にしかチェックされません。
その場合は自宅の範囲を広げて時間稼ぎをするか、
チェックの間隔を短くして対処してください。

チェックの間隔を短くするとバッテリー消費は増えます。
私は120秒にして使っています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アプリ開発で使う mitmproxy 4.0 のコマンド一覧

mitmproxy 4.0チートシート

  • アプリ開発でよく使うコマンドをまとめた
    • 大きく操作が変わったversion 4での内容です
  • 基本的な操作 q, tab, enter, j, k, ctr + d, shift + g, gg は除く

コマンド一覧みたい

?

flow list をフィルタリングしたい

f

並び替え(sort, order by)したい

o

画像のみをフィルタリングしたい

# fを押して
f

# : set view_filter= が表示されるので、下記を入力
~a jpg

POST(Method)のみをフィルタリングしたい

f
~m POST

Content-typeでフィルタリング

f
~t json

flow list をコピーしたい

  • Altを押したまま選択
  • macだとfnを押したまま選択

flow listを消したい

z

curlコマンドに変換(コピー)したい

# :を押してコマンドプロンプトにして
:

# 以下を入力でクリップボードにcurl形式でコピー
# @focusで表示中の内容をコピー
export.clip curl @focus

常に一番下に新しい通信を表示したい(followingモード)

F (shift + f)

# 解除は再度F
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android エミュレーターでネットワークに繋がらない時

1. SDKの場所を確認する

  • Android Studioを開き、右下のConfigerを押下
    image.png

  • Project Defaults → Project Structureを選択
    image.png

  • Android SDK LocationにSDKのパスがあることを確認
    image.png

emulatorのDNSを変更させる

  • ターミナルでSDKのパスに移動
cd /Users/xxx/Library/Android/sdk
  • さらにemulatorフォルダに移動
cd emulator
  • emulatorの一覧を確認
./emulator -list-avds

と打つと、私の場合はこれ↓が返ってきました

Nexus_5X_API_19
Nexus_6_API_22
Pixel_2_XL_API_23
Pixel_XL_API_19
  • デバイスを一個ずつ起動させる(Nexus_5X_API_19の箇所は自由に変えてください)
./emulator -avd Nexus_5X_API_19 -dns-server 8.8.8.8

この手順でネットワークにつながりました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android GoogleMapの使い方メモ

はじめに

最近プロジェクトでGoogleMapAPIを利用したので、細かな使い方を記事として残しておきます。

プロジェクト作成〜地図の表示

AndroidStudioのプロジェクトウィザードで簡単に作れます。
1.png

生成された、google_maps_api.xml を開き、YOUR_KEY_HERE を更新します。

2.png

キーは https://console.developers.google.com から取得します。

3.png

ビルドして実行すると、このように表示されます。
4.png

UIの設定

移動

スワイプで地図を平行移動できます。

googleMap.uiSettings.isScrollGesturesEnabled = true

ズーム

ピンチイン・アウトで縮尺を変更できます。

googleMap.uiSettings.isZoomGesturesEnabled = true

回転

ピンチからの回転で地図が回転できます。

googleMap.uiSettings.isRotateGesturesEnabled = true

ズームボタン

画面右下にズーム変更ボタンが表示されます。

10.png

googleMap.uiSettings.isZoomGesturesEnabled = true

ツールバー

マーカーをタップした時に、
画面の右下に表示されるGoogleMapのユーザインターフェイスです。

googleMap.uiSettings.isMapToolbarEnabled = true

5.png

ズームボタンと同時に表示するとこのように表示されます。

11.png

ティルト

2本指でスワイプで視点を傾けることができます。

googleMap.uiSettings.isTiltGesturesEnabled = true

6.png

コンパス

画面左上の表示されます。

googleMap.uiSettings.isCompassEnabled = true

7.png

現在地表示

ACCESS_COARSE_LOCATION または ACCESS_FINE_LOCATION
の許可を取得しておく必要があります。

googleMap.isMyLocationEnabled = true

9.png

現在地ボタン

画面の右上に表示されます。
タップすると現在地にカメラと縮尺が調整されます。

googleMap.isMyLocationButtonEnabled = true

8.png

カメラの移動

// アニメーションなし
googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng))

// アニメーションあり
googleMap.animateCamera(CameraUpdateFactory.newLatLng(sydney))

カメラの縮尺変更

val zoomValue = 14.0f // 1.0f 〜 21.0f を指定
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoomValue))

おおよその詳細レベル

1: World
5: Landmass/continent
10: City
15: Streets
20: Buildings

ズームレベルの詳細はこちらを参考に
https://developers.google.com/maps/documentation/android-sdk/views
https://qiita.com/SnowMonkey/items/795779913be692c12a0b

マーカー表示

val sydney = LatLng(-34.0, 151.0)
googleMap.addMarker(
    MarkerOptions()
        .position(sydney)             // 地図上のマーカーの位置
        .title("Marker in Sydney")    // マーカーをタップ時に表示するテキスト文字列
        .snippet("Australian cities") // タイトルの下に表示される追加のテキスト
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)) // アイコン
)

12.png

また、マーカーをタップしなくても吹き出しを表示することも出来る。

val marker = googleMap.addMarker(
    MarkerOptions()
        .position(latLng)
        .title("test")
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
)
marker.showInfoWindow() // タップした時と同じ挙動

円の描画

val latLng = LatLng(35.681236, 139.767125) // 東京駅
val radius = 1000 * 10.0 // 10km
googleMap.addCircle(
    CircleOptions()
        .center(latLng)          // 円の中心位置
        .radius(radius)          // 半径 (メートル単位)
        .strokeColor(Color.BLUE) // 線の色
        .strokeWidth(2f)         // 線の太さ
        .fillColor(0x400080ff)   // 円の塗りつぶし色
)

13.png

線の描画

googleMap.addPolyline(
    PolylineOptions()
        .add(LatLng(35.681236, 139.767125)) // 東京駅
        .add(LatLng(34.7331, 135.5002))     // 新大阪駅
        .color(Color.RED)                   // 線の色
        .width(8f)                          // 線の太さ
)

14.png

2点間に距離を計算(SphericalUtil)

距離を簡単に計算するのに必要なユーティリティライブラリを追加します。
https://developers.google.com/maps/documentation/android-sdk/utility/

/app/build.gradle
dependencies {
  ...
  // add
  implementation 'com.google.maps.android:android-maps-utils:0.5'
}

検証として、東京駅新大阪駅 の緯度経度を使います。

val latLngA = LatLng(35.681236, 139.767125)
val latLngB = LatLng(34.7331, 135.5002)

// 距離をメートル単位で返す
val distance = SphericalUtil.computeDistanceBetween(latLngA, latLngB)

println("AB: $distance m")
println("AB: ${distance / 1000} km")

出力

AB: 401709.92548952583 m
AB: 401.70992548952586 km

以下は、PCのブラウザでGoogleMapで距離を測った結果です。

ab.png

2点間に距離を計算(Location)

ユーティリティライブラリを入れなくても、Locationクラスで2点間の距離を計算できます。
簡単に呼び出せるように以下のExtensionを定義しておきます。

fun LatLng.distanceBetween(toLatLng: LatLng): Float {
    val results = FloatArray(1)
    try {
        Location.distanceBetween(
            this.latitude, this.longitude,
            toLatLng.latitude, toLatLng.longitude,
            results
        )
    } catch (e: IllegalArgumentException) {
        return -1.0f
    }
    return results[0]
}

呼び出し元

val latLngA = LatLng(35.681236, 139.767125)
val latLngB = LatLng(34.7331, 135.5002)

// 距離をメートル単位で返す
val distance = latLngA.distanceBetween(latLngB)

println("AB: ${latLngA.distanceBetween(latLngB)} m")
println("AB: ${latLngA.distanceBetween(latLngB) / 1000} km")

SphericalUtilと比較した結果、こちらは少し誤差が出るようです。

 AB: 402483.6 m
 AB: 402.48358 km

まとめ

本記事で記述しているのはGoogleMapAPIの中でもごく一部です。
忘れた頃にいちいちググりにいくのが面倒なので、ひとまずまとめておきました。

公式資料

https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap
http://googlemaps.github.io/android-maps-utils/javadoc/com/google/maps/android/SphericalUtil.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いまさらWorkManager〜基本編③(繰り返し処理を実行する)〜

はじめに

この記事で説明した通り、様々な機能を活用していきましょう。
今回は、繰り返し処理を設定する方法を学習していきます。

動作環境

この記事の動作環境は以下のとおりです。

  • Android Studio:3.3
  • Kotln:1.3.11
  • Open JDK:1.8
  • compileSdkVersion:28
  • targetSdkVersion:28
  • minSdkVersion:19

目標

繰り返して処理を実行出来るようになる!

概要

繰り返し処理を実行したい場合は、PeriodicWorkRequestクラスを利用します。
連続して実行は出来ませんが、ある一定時間(インターバル)を置いて実行が可能です。
イメージとしてはこんな感じでしょうか。

PeriodicWorkRequest.png

実際に一定のインターバルでノーティフィケーションを表示する処理をすると下図のような実行結果になります。

実装方法

基本的な実装方法はOnetimeWorkRequestと同じですが、最後のRequestオブジェクトを作成する時に、インターバルなどの設定を行います。

PeriodicWorkRequestオブジェクトを生成するには、Builderクラスを利用します。
そのコンストラクタで、インターバルを設定します。

共通の条件

インターバルの最小時間は決まっています。 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS 以上でなければなりません。
定義を確認してみると。。。

public final class PeriodicWorkRequest extends WorkRequest {

    /**
     * The minimum interval duration for {@link PeriodicWorkRequest} (in milliseconds).
     */
    public static final long MIN_PERIODIC_INTERVAL_MILLIS = 15 * 60 * 1000L; // 15 minutes.
 〜〜〜省略〜〜〜〜
}

となっていました。
なので、最低でも 15分 以上は開ける必要があります。

PeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass, long repeatInterval, TimeUnit repeatIntervalTimeUnit)

コンストラクタの引数

引数 値と説明
第1引数 Workerクラスのクラス情報
第2引数 インターバルの時間
第3引数 時間の単位

説明

APIの説明を読むと以下のように記述されています。

引数で指定したインターバル期間に実行するPeriodicWorkRequeクラスのオブジェクトを生成します。
PeriodicWorkRequestはインターバルの期間中に1度だけ実行されることが保証されています。
バッテリーや端末の状況によっては、その影響を受ける可能性はあります。

実行する処理は15以上間隔を開ける必要があります。それ以外の条件が満たされていれば、その期間中に実行可能状態になります。また、実行する処理の時間はフレックス期間を設定し、制限することも可能です。

ということは、インターバル期間中に実行が可能なれば実行できるみたいです。
きっちり15分間とかではなく、その15分間の中で実行されるイメージみたいです。
分かりづらいですが、絵にすると下図のような感じになります。

PeriodicWorkRequest_実行タイミング.png

また、フレックスについては、後述します。

実装例

val periodicWork = PeriodicWorkRequest.Builder(
    MyWorker::class.java,
    15, TimeUnit.MINUTES
).build()

public PeriodicWorkRequest.Builder (Class<? extends ListenableWorker> workerClass, Duration repeatInterval)

コンストラクタの引数

引数 値と説明
第1引数 Workerクラスのクラス情報
第2引数 インターバルの時間と単位

説明

基本的には、上記に記載した「PeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass, long repeatInterval, TimeUnit repeatIntervalTimeUnit)」と同じです。
違いとしては、インターバルの時間をDurationクラスのメソッドを使用して、時間と単位を一度に設定できることです。

実装例

val periodicWork = PeriodicWorkRequest.Builder(
    MyWorker::class.java,
    Duration.ofMinutes(15)
).build()

PeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass, long repeatInterval, TimeUnit repeatIntervalTimeUnit, long flexInterval, TimeUnit flexIntervalTimeUnit)

コンストラクタの引数

引数 値と説明
第1引数 Workerクラスのクラス情報
第2引数 インターバルの時間
第3引数 インターバルの時間の単位
第4引数 フレックスの時間
第5引数 フレックスの時間の単位

説明

インターバルの時間内にフレックス時間を設け、そのフレックス時間内に処理が実行されます。
フレックス時間は下記で計算された時間からインターバル終了までとします。

フレックス開始時間 = インターバル時間 ー フレックス時間

また、インターバルの時間は最低でもPeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS(15分以上) 以上、フレックスの時間は、最低でもPeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS(15以上)に設定しなければなりません。

これだけだといまいちピンとこなかったので、図にしてみました。

PeriodicWorkRequest_フレックス.png

要するに、インターバルの中にフレックスがありそのフレックスの時間の間で処理を実行します。
フレックス時間の終了はインターバルの終了時間と同じであるため、インターバルの最初の方で処理が実行されることはありません。

実装例

val periodicWork = PeriodicWorkRequest.Builder(
    MyWorker::class.java,
    20, TimeUnit.MINUTES, // インターバルの時間
    15, TimeUnit.MINUTES  // フレックスの時間
).build()

PeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass, Duration repeatInterval, Duration flexInterval)

コンストラクタの引数

引数 値と説明
第1引数 Workerクラスのクラス情報
第2引数 インターバルの時間と単位
第3引数 フレックスの時間と単位

説明

基本的な内容はPeriodicWorkRequest.Builder(Class<? extends ListenableWorker> workerClass, long repeatInterval, TimeUnit repeatIntervalTimeUnit, long flexInterval, TimeUnit flexIntervalTimeUnit)と同じです。
違いとしては、Durationクラスを利用して、時間と単位を一気に設定します。

実装例

val periodicWork = PeriodicWorkRequest.Builder(
    MyWorker::class.java,
    Duration.ofMinutes(20), // インターバルの時間
    Duration.ofMinutes(15) // フレックスの時間
).build()

動作確認をした際のコード

最後に、動作確認した際のコードをすべて掲載します。
AndroidManifest.xmlはプロジェクトのテンプレートから変更していないため、掲載はしません。

build.gradle
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "jp.co.casareal.workmanagerperiodicwork"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    def work_version = "1.0.0"

    implementation "android.arch.work:work-runtime-ktx:$work_version"

}
MyWork.kt
package jp.co.casareal.workmanagerperiodicwork

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.support.v4.app.NotificationCompat
import androidx.work.Worker
import androidx.work.WorkerParameters
import java.text.SimpleDateFormat

class MyWorker(cxt: Context, params: WorkerParameters) : Worker(cxt, params) {
    val notificationManager =
        applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    // カテゴリー名(通知設定画面に表示される情報)
    val name = "通知のタイトル的情報を設定"
    // システムに登録するChannelのID
    val id = "casareal_chanel"
    // 通知の詳細情報(通知設定画面に表示される情報)
    val notifyDescription = "この通知の詳細情報を設定します"

    private val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

    companion object {
        var nid = 1;
    }


    init {
        // Channelの取得と生成
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager.getNotificationChannel(id) == null
            val mChannel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
            mChannel.apply {
                description = notifyDescription
            }
            notificationManager.createNotificationChannel(mChannel)
        }
    }

    override fun doWork(): Result {


        val notification = NotificationCompat.Builder(applicationContext, id).apply {

            setContentText("${nid}回目のメッセージ:${simpleDateFormat.format(System.currentTimeMillis())}")
            setSmallIcon(R.drawable.ic_launcher_background)
        }

        notificationManager.notify(MyWorker.nid, notification.build())

        MyWorker.nid++

        return Result.success()
    }
}
MainActivity.kt
package jp.co.casareal.workmanagerperiodicwork

import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import kotlinx.android.synthetic.main.activity_main.*
import java.time.Duration

class MainActivity : AppCompatActivity() {


    private val manager = WorkManager.getInstance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)



        buttonStart.setOnClickListener {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val periodicWork = PeriodicWorkRequest.Builder(
                    MyWorker::class.java,
                    Duration.ofMinutes(15)
                ).build()

                val operation = manager.enqueue(periodicWork)

            }


        }
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <Button
            android:id="@+id/buttonStart"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

まとめ

繰り返し実行する場合は、インターバルの時間を設定することが大事なようです。
そこで、インターバルの時間を開始してから実行を遅らせるためにフレックスの時間を利用することによって適切なタイミングでの実行が可能になっていることがわかりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Genymotionのemulatorがネットワークに繋がらない

はじめに

最近、expoを用いてクロスプラットフォームにアプリ開発を行っています。
その中でandroidで動作確認を行うのにGenymotionを使っているのですが、emulatorが全然ネットワークにつながってくれなかったのでその解決方法について書きます。

Genymotionとは

ここでは詳しく説明しませんが、Android StudioのAVDよりも高速に動くemulatorです。Virtual Box上で動きます。
個人利用であれば無料でで使えます。
詳細はこちら

問題

全然ネットワークにつながってくれない。

GenymotionもしくはVirtual Boxのネットワークの設定が間違っているのでしょうが、どこをどういじればいいかわからず、とりあえずググッて出てきた解決策を試してみました。
実際にこれらを試してみましたが、だめでした。
https://stackoverflow.com/questions/33075796/genymotion-emulator-no-internet-connection
https://qiita.com/shymst/items/1bb60ca0c8a020ce6bc2

解決策

Virtual Boxの設定でNATネットワークを作成するとネットワークに繋がりました。

  • Virtual Boxの設定 >> ネットワーク
  • NATネットワークの追加(設定はデフォルトのまま)
  • emulator起動
  • wifiつながった

参考: https://stackoverflow.com/a/36151812

さいごに

たったこれだけのことでしたが、仮想環境でのネットワークについての理解がまったくないのでめちゃくちゃ時間がかかりました。
これを期に仮想環境、ネットワーク周りの勉強をしようと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidのFragmentでOverlayViewを表示する

Solution

res/layout/partial_overlay.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/overlay_screen_bg">

    <!-- クリックイベントをセットするためのView -->
    <View
        android:id="@+id/view_bg"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

...
</androidx.constraintlayout.widget.ConstraintLayout>
HogeFragment.kt
class HogeFragment : Fragment() {

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        showOverlayView()
    }

    private fun showOverlayView() {
        // activityのrootを取得
        val rootLayout = requireActivity().findViewById(android.R.id.content) as FrameLayout
        // overlayViewを表示
        val overlayView = View.inflate(requireActivity(), R.layout.partial_overlay, rootLayout)

        // view_bgをタップしたら非表示にする
        overlayView.view_bg.setOnClickListener {
            rootLayout.removeViewAt(rootLayout.childCount - 1)
        }
    }
}

参考

https://stackoverflow.com/questions/14194967/android-addview-adding-a-new-view-on-top-of-activity

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Picassoで画像キャッシュを無効にする

案件でPicassoのキャッシュを無効にする事になり、ちょっと調べた情報をまとめる。

Picassoのデフォルトの動作

Picassoのデフォルトの動作は以下
- メモリキャッシュはアプリケーションRAMの15%
- ディスクキャッシュはストレージサイズの2%で最大50MB
- 注:これはAPI 14+で、またはOkHttpのようなすべてのAPIレベルでディスクキャッシュを提供するスタンドアロンライブラリを使用している場合にのみ利用可能
- ディスクおよびネットワークアクセス用の3つのダウンロードスレッド。

Picassoは常に最初にメモリキャッシュから画像をロードしようとする。
メモリキャッシュになければ、ディスクキャッシュをチェックする。
ディスクキャッシュもなければネットワーク処理をリクエストする。

つまり、メモリ -> ディスク -> ネットワーク の順にチェックする。

また、Picassoで表示する画像はメモリ/ディスクキャッシュの両方に保管される。

キャッシュ無効

メモリキャッシュ、ディスクキャッシュを両方とも無効にする必要がある。
java
Picasso.get()
.load(url)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE)
.into(imageView);

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WebViewとネイティブのメリットデメリット

App内で動作する簡易ブラウザ(WebView)と通信からUIまでアプリ内で完結するネイティブアプリのメリットデメリットをまとめてみました。

■WebViewのメリット・デメリット

メリット

  • コストが安い
    Web上にコンテンツがあり、それを流用したい場合、新規で開発することなく同じコンテンツを表示できます。

  • アプリストアへの申請・審査の不要
    ネイティブアプリの申請や審査が不要になります。
    ※ただし、部分的なWebView表示の場合ネイティブと変わらないので審査は必要です。

  • アプリアップデートの不要
    アプリの保守をする必要がないため、アプリ自体のアップデートがありません。
    ※仕様が変更された時は、アプリの動作を変更する可能性があるため、一概ではないです。

デメリット

  • 課金ができない
    Apple Store審査ガイドラインの規約違反になり、リジェクトされる恐れがあります。
    ※Webブラウザでの課金は可能(リンクをブラウザへ飛ばす必要あり)
    引用:Apple Store審査ガイドライン:3.1.1 App内課金

  • レスポンシブ対応
    画面解像度に応じたUI設計の対応が必要。
    Webの表示をそのまま、アプリに適応させると表示崩れが起こる可能性がある。

  • 脆弱性

    1. 自社以外の外部アクセス

      URL直打ちで、自社ではないサイトにアクセスできる場合、悪用される恐れがある。
      外部アクセスできるサイトに制限を設ける必要がある。

    2. 端末内のファイルストリームにアクセスできる

      「file://」で始まるURI(※URLではない)でアクセスすることで、端末内部のファイルにアクセスすることができる。
      アクセスログからユーザー情報が漏洩されてしまう。

    3. iOSは、Cookieを保存しない

      アプリが終了するとCookieが消えるので、永続化する必要がある

    4. XSSの恐れ

      攻撃スクリプトを入力できてしまう。
      サニタイジング(無害化)する必要がある。

  • 通信エラー場合の表示
    通信エラー場合のWebView画面を表示する前に、レスポンスチェックを行い、ネイティブの別画面を表示させる。
    WebViewは通信した結果の表示なので、真っ白になったり、URLエラーになったりしてUI的に作りが悪い。

  • 環境によっては表示が遅い
    Web通信が伴うため、通信環境によっては表示が遅い場合があります。

  • 導線が不可能
    WebViewの誘導はできない。
    alt

■ネイティブ画面のメリット・デメリット

メリット

  • Webではできない画面設計・操作設計ができる
    アプリ内のUIを使用するため、自由に画面設計を行うことが可能

  • 導線設計が可能
    アプリに誘導できる

  • セキュリティ
    URL直打ちではないため、外部のサイトへアクセスさせることはないです。

デメリット

  • コストが高い
    iOS / Androidの開発者が必要
    開発言語が異なるため、それぞれの開発者が必要

  • アプリストアへの申請・審査
    ネイティブアプリの申請や審査が必要です。

  • アプリアップデートが必要
    保守でアプリ修正があった場合は、アップデートが必要

  • 課金
    App内課金を(30%)手数料(30%)が発生する

引用

https://appbu.jp/webapps-nativeapps
https://ja.developer.box.com/docs/android-security-guidelines
https://appkitbox.com/knowledge/android/20130819-84
https://teratail.com/questions/100872
https://mexess.blog/2018/08/03/post-304/
https://qiita.com/noboru_i/items/240ffcb2036f3b5cbc3b
https://qiita.com/noboru_i/items/bc39d95638e9e55437fa#cookie%E3%81%AE%E8%A8%AD%E5%AE%9A
https://qiita.com/i_nak/items/be0fac91bdc68aa165db
https://backapp.co.jp/blog/11594/
https://support.ebis.ne.jp/search_service/15033/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む