20201011のAndroidに関する記事は10件です。

#6 Kotlin Koans Introduction/Data classes 解説

1 はじめに

Kotlin公式リファレンスのKotlin Koans/Data classesの解説記事です。

Kotlin Koansを通してKotlinを学習される人の参考になれば幸いです。

ただし、リファレンスを自力で読む力を養いたい方は、
すぐにこの記事に目を通さないで下さい!

一度各自で挑戦してから、お目通し頂ければと思います:fist:

2-1 クラス定義・コンストラクタ

Kotlinではclassというキーワードをつけることでクラスを定義します。

クラス名の右横に()で囲んでコンストラクタを実装することができます。

コンストラクタは、クラスをインスタンス化するのと同時に呼ばれる関数のことです。

コンストラクタには2つの働きがあります。

・ プロパティを定義すること。

・ クラスのインスタンス化とパラメータの初期値の設定を同時に行うこと。

クラス定義・コンストラクタの実装を実際のコードで確認してみましょう。

//クラス定義。Invoiceがクラス名。
class Invoice { /*...*/ }   

//空のクラスの定義。クラス名の後の{]は省略できる。
class Empty          

//コンストラクタの実装。PersonクラスにfirstNameというプロパティを定義し、
//Personクラスがインスタンス化されるときに、firstNameに値が代入される。
//constructorというキーワードは省略できる。
class Person constructor(firstName: String) { /*...*/ }   

2-2 データクラス

クラスを定義する際、classのまえにdata修飾子を加えることで、クラスをデータクラスとして定義することができます。

データクラスは、

equal()関数、hashCode()関数、toString()関数、componentN()関数、copy()関数を自動的に実装してくれます。

2-3 セッター・ゲッター

Javaを学習された方には馴染みが深いかと思いますが、Kotlinにもゲッター・セッターが存在します。

ゲッター・セッターとはどちらも関数の1種です(Javaではメソッドと言います)。

Javaではクラス外からプロパティ(Javaではフィールドと言います)に値をセットするときにセッターを、クラス外からプロパティを利用したいときにゲッターを利用します。

ただ、Kotlinではこれら2つの関数を定義しなくても、暗黙的に定義されています。

なので特に何も定義しなくても、クラス外から目的のクラスのプロパティに値をセットしたり、利用したりできるのです。

実際にKotlinでクラス外でプロパティを利用しているコードを見てみましょう。

fun usePersonInfo(){
  //Personクラスのインスタンスを生成。
 val p = Person()
  //Personクラスのプロパティnameとageに値を代入。
  p.name = "名前"
 p.age = 20
 //プロパティnameとageの値を変数nとaに代入。
 val n = p.name
  val a = p.age
}

//Personクラスを定義しプロパティnameとageを作成。初期値として、空文字と0を代入。
class Person{
  var name : String = ""
  var age : Int = 0
}

3 Introduction/Data classesの解説

Kotlin Koans Introduction/Data classesの解説です。
随時本サイトの内容を引用させていただきます。

右側の本文を見てみましょう。

Rewrite the following Java code to Kotlin:

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

Then add a modifier data to the resulting class. This annotation means the compiler will generate a bunch of useful methods in this class: equals/hashCode, toString and some others. The getPeople function should start to compile.

Read about classes, properties and data classes.

JavaのコードをKotlinで書き換えなさい。
そのとき、data修飾子を追加しなさい。これにより、コンパイラはdata修飾子のついたクラスに便利な関数を生成する(equal()関数やhashCode()関数、toString()関数など)。getPeople関数はコンパイルし始めるべきである。


Javaのコードを見てみましょう。

Personクラス内には、String型のプロパティnameとInt型のプロパティageが存在しています。

Person()関数によってクラス外からプロパティに値がセットできます(この関数がJavaのセッターです。)。

getName()関数とgetAge()関数によってクラス外からプロパティが利用できます(この関数がJavaのゲッターです。)。

これをKotlinで書き換えます。

class Person

このコードに加筆して、以下の3点を加えます。

  1. プロパティnameとage
  2. ゲッター
  3. セッター

ただし、Kotlinではゲッター・セッターは定義不要なので、
1.のプロパティnameとageの定義のみ行えばOKです。

プロパティをクラス内で定義するには、コンストラクタを利用するor利用しないの2通りあります。

ただ、getPeople()関数の中身を見てみると、

Person("Alice", 29), Person("Bob", 31)

Personクラスのインスタンスを生成すると同時に、引数を設定しています。

この引数はコンストラクタに渡されPersonクラスのプロパティの初期値になります。

なので今回はプロパティの定義にコンストラクタを利用しないといけません。

また、data修飾子を追加するように問題文に指定があるので、

data class Person(val name : String,val age : Int)

のようにすれば良いです(valはvarでもOKです。)。


(参考)

Personクラスの定義をdata修飾子無しでするとどうなるのでしょう?

Runボタンを押して見ると、

Fail: testListOfPeople: expected:<[Person[(name=Alice, age=29), Person(name=Bob, age=31)]]> but was:<[Person[@726f3b58, Person@442d9b6e]]>

というエラーメッセージが出ます。

これは、Person(name=Alice,age=29)とPerson(name=Bob,age=31)を期待していたのに、

Person@726f3b58とPerson@442d9b6eなんだけど、、、。といったニュアンスのメッセージです。

実は、dataクラスはtoString()関数を
インスタンスのプロパティの情報を人間のわかるように表示するように実装します。

以下で具体例を見てみましょう。

通常のクラスを用いた場合

fun showExample(){
  val e = Example("例1","例2")
 println(e.toString())
}

class Example(val ex1 : String,val ex2 : String)

これを実行すると、Person@gen37232と表示されます(@以下は任意の英数字の羅列です)。
Exampleクラスをdataクラスに書き換えて見ましょう。

dataクラスを用いた場合

fun showExample(){
  val e = Example("例1","例2")
 println(e.toString())
}

data class Example(val ex1 : String,val ex2 : String)

これを実行すると、Person(ex1=例1,ex2=例2)と表示されます。

これだとPersonインスタンスのプロパティの情報が理解できますね。


4 最後に

次回はKotlin Koans Introducion/Nullable typesの解説をします:muscle:

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

#6Kotlin Koans Introduction/Data classes 解説

1.はじめに

Kotlin公式リファレンスのKotlin Koans/Data classesの解説記事です。

Kotlin Koansを通してKotlinを学習される人の参考になれば幸いです。

ただし、リファレンスを自力で読む力を養いたい方は、
すぐにこの記事に目を通さないで下さい!

一度各自で挑戦してから、お目通し頂ければと思います:fist:

2-1 クラス定義・コンストラクタ

Kotlinではclassというキーワードをつけることでクラスを定義します。

クラス名の右横に()で囲んでコンストラクタを実装することができます。

コンストラクタは、クラスをインスタンス化するのと同時に呼ばれる関数のことです。

コンストラクタには2つの働きがあります。

・ プロパティを定義すること。

・ クラスのインスタンス化とパラメータの初期値の設定を同時に行うこと。

クラス定義・コンストラクタの実装を実際のコードで確認してみましょう。

//クラス定義。Invoiceがクラス名。
class Invoice { /*...*/ }   

//空のクラスの定義。クラス名の後の{]は省略できる。
class Empty          

//コンストラクタの実装。PersonクラスにfirstNameというプロパティを定義し、
//Personクラスがインスタンス化されるときに、firstNameに値が代入される。
//constructorというキーワードは省略できる。
class Person constructor(firstName: String) { /*...*/ }   

2-2 データクラス

クラスを定義する際、classのまえにdata修飾子を加えることで、クラスをデータクラスとして定義することができます。

データクラスは、

equal()関数、hashCode()関数、toString()関数、componentN()関数、copy()関数を自動的に実装してくれます。

2-3 セッター・ゲッター

Javaを学習された方には馴染みが深いかと思いますが、Kotlinにもゲッター・セッターが存在します。

ゲッター・セッターとはどちらも関数の1種です(Javaではメソッドと言います)。

Javaではクラス外からプロパティ(Javaではフィールドと言います)に値をセットするときにセッターを、クラス外からプロパティを利用したいときにゲッターを利用します。

ただ、Kotlinではこれら2つの関数を定義しなくても、暗黙的に定義されています。

なので特に何も定義しなくても、クラス外から目的のクラスのプロパティに値をセットしたり、利用したりできるのです。

実際にKotlinでクラス外でプロパティを利用しているコードを見てみましょう。

fun usePersonInfo(){
  //Personクラスのインスタンスを生成。
 val p = Person()
  //Personクラスのプロパティnameとageに値を代入。
  p.name = "名前"
 p.age = 20
 //プロパティnameとageの値を変数nとaに代入。
 val n = p.name
  val a = p.age
}

//Personクラスを定義しプロパティnameとageを作成。初期値として、空文字と0を代入。
class Person{
  var name : String = ""
  var age : Int = 0
}

3 Introduction/Data classesの解説

Kotlin Koans Introduction/Data classesの解説です。
随時本サイトの内容を引用させていただきます。

右側の本文を見てみましょう。

Rewrite the following Java code to Kotlin:

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

Then add a modifier data to the resulting class. This annotation means the compiler will generate a bunch of useful methods in this class: equals/hashCode, toString and some others. The getPeople function should start to compile.

Read about classes, properties and data classes.

JavaのコードをKotlinで書き換えなさい。
そのとき、data修飾子を追加しなさい。これにより、コンパイラはdata修飾子のついたクラスに便利な関数を生成する(equal()関数やhashCode()関数、toString()関数など)。getPeople関数はコンパイルし始めるべきである。


Javaのコードを見てみましょう。

Personクラス内には、String型のプロパティnameとInt型のプロパティageが存在しています。

Person()関数によってクラス外からプロパティに値がセットできます(この関数がJavaのセッターです。)。

getName()関数とgetAge()関数によってクラス外からプロパティが利用できます(この関数がJavaのゲッターです。)。

これをKotlinで書き換えます。

class Person

このコードに加筆して、以下の3点を加えます。

  1. プロパティnameとage
  2. ゲッター
  3. セッター

ただし、Kotlinではゲッター・セッターは定義不要なので、
1.のプロパティnameとageの定義のみ行えばOKです。

プロパティをクラス内で定義するには、コンストラクタを利用するor利用しないの2通りあります。

ただ、getPeople()関数の中身を見てみると、

Person("Alice", 29), Person("Bob", 31)

Personクラスのインスタンスを生成すると同時に、引数を設定しています。

この引数はコンストラクタに渡されPersonクラスのプロパティの初期値になります。

なので今回はプロパティの定義にコンストラクタを利用しないといけません。

また、data修飾子を追加するように問題文に指定があるので、

data class Person(val name : String,val age : Int)

のようにすれば良いです(valはvarでもOKです。)。


(参考)

Personクラスの定義をdata修飾子無しでするとどうなるのでしょう?

Runボタンを押して見ると、

Fail: testListOfPeople: expected:<[Person[(name=Alice, age=29), Person(name=Bob, age=31)]]> but was:<[Person[@726f3b58, Person@442d9b6e]]>

というエラーメッセージが出ます。

これは、Person(name=Alice,age=29)とPerson(name=Bob,age=31)を期待していたのに、

Person@726f3b58とPerson@442d9b6eなんだけど、、、。といったニュアンスのメッセージです。

実は、dataクラスはtoString()関数を
インスタンスのプロパティの情報を人間のわかるように表示するように実装します。

以下で具体例を見てみましょう。

通常のクラスを用いた場合

fun showExample(){
  val e = Example("例1","例2")
 println(e.toString())
}

class Example(val ex1 : String,val ex2 : String)

これを実行すると、Person@gen37232と表示されます(@以下は任意の英数字の羅列です)。
Exampleクラスをdataクラスに書き換えて見ましょう。

dataクラスを用いた場合

fun showExample(){
  val e = Example("例1","例2")
 println(e.toString())
}

data class Example(val ex1 : String,val ex2 : String)

これを実行すると、Person(ex1=例1,ex2=例2)と表示されます。

これだとPersonインスタンスのプロパティの情報が理解できますね。


4 最後に

次回はKotlin Koans Introducion/Nullable typesの解説をします:muscle:

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

Navigation Architecture Componentで現在アクティブなFragmentか判定する方法

Navigation Architecture Componentでイベントの中で画面遷移処理行う場合に、
ダイアログの裏側のFragmentが反応してしまい、backStackがおかしくなることが頻発したため、
自身がアクティブなFragmentか判定するために、extensionを書いたのでメモしておきます。

サンプルコード

Fragment.kt
package com.example.extension

import androidx.fragment.app.Fragment
import androidx.navigation.NavDestination
import androidx.navigation.fragment.DialogFragmentNavigator
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.fragment.findNavController

/**
 * isCurrentDestination
 */
fun Fragment.isCurrentDestination(): Boolean {
  val currentDestination: NavDestination = findNavController().currentDestination ?: return false
  when (currentDestination) {
    is DialogFragmentNavigator.Destination -> {
      if (currentDestination.className == this.javaClass.name) {
        return true
      }
    }
    is FragmentNavigator.Destination -> {
      if (currentDestination.className == this.javaClass.name) {
        return true
      }
    }
  }
  return false
}

使用例

  /**
   * 戻るボタンクリック処理
   */
  fun onClickBackButton() {
    if (!isCurrentDestination()) {
      return
    }
    findNavController().popBackStack()
  }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android で各ストレージの総容量、空き容量を取得する方法

Android 10(Q)から導入されたScoped Storageの影響で、以前のストレージの容量関連の取得ができなくなっていたため、Android 10以降でも利用できる方法を模索したのでメモしておきます。
※ ディレクトリ毎のパーミッションを取ればできますが、
 容量関連の情報を取るためだけに、ユーザにパーミッション許可を求めたくなかったため、
 StatFsを用いた方法にしてあります。

なお、勢いで書いたので、実際には動かしてないです。。。
※サンプルコードの他に外部ストレージアクセスのパーミッション取得が必要な可能性あり。

サンプルコード

MainActivity.kt
package com.example.storage.ui

import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.os.StatFs
import android.os.storage.StorageManager
import android.os.storage.StorageVolume
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import java.util.*

/**
 * Main Activity
 */
class MainActivity : AppCompatActivity() {
  /**
   * ==============================================
   *  インスタンス変数
   * ==============================================
   */
  /** タグ */
  private val tag: String = "MainActivity"

  /** ストレージマネージャー */
  private val storageManager: StorageManager?
    get() = getSystemService(StorageManager::class.java)

  /**
   * ==============================================
   *  ライフサイクルイベント
   * ==============================================
   */
  /**
   * onCreate
   */
  @SuppressLint("DiscouragedPrivateApi")
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    Log.i(tag, "onCreate")

    for (storageVolume: StorageVolume in storageManager!!.storageVolumes) {
      // ストレージボリュームのリストを取得し、1件づつ処理
      // ストレージボリュームから絶対パスを取得
      val path: String = when {
        Build.VERSION.SDK_INT>= Build.VERSION_CODES.R -> {
          // Android 11以降
          storageVolume.directory?.absolutePath
        }
        else -> {
          // Android 10以前
          // NOTE: Android10以前ではgetPathがprivateなため無理やり実行して取得
          val getPath = StorageVolume::class.java.getDeclaredMethod("getPath")
          getPath.invoke(storageVolume) as String?
        }
      } ?: continue // 絶対パスが取得できない場合は、スキップ

      val statFs = StatFs(path)
      // 総容量
      val totalSpase: Long = statFs.blockCountLong * statFs.blockSizeLong / 1024L / 1024L
      // 空き容量
      val freeSpase: Long = statFs.availableBlocksLong * statFs.blockSizeLong / 1024L / 1024L
      // 使用容量
      val usedSpase: Long = totalSpase - freeSpase

      Log.d(tag, "Path: $path")
      Log.d(tag, " Used space: ${String.format(Locale.US, "%,12d", usedSpase)}MB")
      Log.d(tag, " Free space: ${String.format(Locale.US, "%,12d", freeSpase)}MB")
      Log.d(tag, "Total space: ${String.format(Locale.US, "%,12d", totalSpase)}MB")
      Log.d(tag, "Total space: ${String.format(Locale.US, "%,12d", totalSpase)}MB")
    }
  }
}

実行結果

Log
 Path: /storage/emulated/0
  Used space:          495MB
  Free space:        5,455MB
 Total space:        5,951MB
 Path: /storage/1200-3709
  Used space:            0MB
  Free space:          509MB
 Total space:          509MB
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#5 Kotlin Koans Introduction/Strings 解説

1 はじめに

Kotlin公式リファレンスのKotlin Koans/Stringsの解説記事です。

Kotlin Koansを通してKotlinを学習される人の参考になれば幸いです。

ただし、リファレンスを自力で読む力を養いたい方は、
すぐにこの記事に目を通さないで下さい!

一度各自で挑戦してから、お目通し頂ければと思います:fist:

2 正規表現・エスケープシーケンス

正規表現(regex patterns)とは、文字列に関する文法事項とイメージして頂ければOKです。

特殊な記号を用いて文字列パターンを指定します。

エスケープシーケンスとは、画面上に文字を出力する際に、文字そのものを出力するのではなく、文字色の変更やカーソルの移動、文字の消去など、文字出力の制御を行う特殊な文字列のことです。

/ 記号と他の記号のセットで用います。

エスケープシーケンス(エスケープ文字)とは? ~見えない文字を表現する~|データ分析用語を解説を引用させていただきました。)

具体例は#3で解説します。

3 Introduction/Stringsの解説

Kotlin Koans Introduction/Stringsの解説です。
随時本サイトの内容を引用させていただきます。

右側の本文を見てみましょう。

Read about different string literals and string templates in Kotlin.

Raw strings are useful for writing regex patterns, you don't need to escape a backslash by a backslash. Below there is a pattern that matches a date in format 13.06.1992 (two digits, a dot, two digits, a dot, four digits):

fun getPattern() = """\d{2}\.\d{2}\.\d{4}"""

Using month variable rewrite this pattern in such a way that it matches the date in format 13 JUN 1992 (two digits, a whitespace, a month abbreviation, a whitespace, four digits).

different string literals and string templatesを読みなさい。
raw stringsは正規表現を記述するのに便利である。バックスラッシュでバックスラッシュをエスケープする必要がない。
以下は13.06.1992(2個の数字、1個のドット、2個の数字、1個のドット、4個の数字)のフォーマットの日付にマッチする文字列パターンである。

変数monthを用いてこのパターンを13 JUN 1992(2個の数字、スペース、月の略記、スペース、4個の数字)に書き直しなさい。

とあります。(13,06,1992,JUNはあくまでパターンの例ですので、気にする必要はありません。)

つまり、

val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)"

fun getPattern(): String = TODO()

変数month(月の略記が格納されている)を用いて、TODO()部分で13 JUN 1992のフォーマットを実現すれば良いわけです。

ここで、正規表現・エスケープシーケンスを利用します。

使用する、正規表現・エスケープシーケンスは以下のとおりです。

  • \d:1~9のいずれかの数字を表現します。
  • {}:直前の文字を{}内の数字の回数分表示します。
  • ${}:{}内の文字列を、別の文字列に埋め込む見ます(これをString Templateと言います。)

これらを用いて、TODO()を

"""\d{2} ${month} \d{4}"""

と書き換えればOKです。


(参考)
13.06.1992を表現している正規表現・エスケープシーケンスを見てみましょう。

"""\d{2}\.\d{2}\.\d{4}"""

\.は正規表現での.の「任意の1文字を指定する」といった意味を打ち消し、.そのものを表示します。


4 最後に

次回はKotlin Koans Introducion/Data classesの解説をします:muscle:

あと、みなさんraw stringsってどう日本語で呼んでいるんでしょうか、、、?

よろしければご意見ください:raised_hands:

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

Androidアプリ開発の準備

概要

20代後半の現SEです。
SIerに所属していますが、業務内容が技術とは離れているため、スキルアップのために自己学習を行っています。
今までも、PHPやPython等を勉強してきまいしたが、効率的なスキルアップにはインプットだけでなく、アウトプットも大事ということで、Qiitaに投稿しようと思いました。

何故Androidアプリかというと、以下の理由です。
 ①自分が持っているスマホがAndroidのため
 ②所持しているOSがWindowsのため(iPhoneアプリはMacが必要)
 ③現時点でポピュラーなJavaを習得できる
 ④広告収入をつければ儲けの仕組みを併用で勉強できる(儲けは考慮しない)

本当は今までやっているWeb系でもよかったのですが、どうせなら新しいことを学びたいと思いAndroidアプリを選びました。

Androidアプリについて

言語

  • Java
  • Kotlin
  • C言語
  • Ruby

Androidアプリの開発ではJavaとKotlinが有名なようですが、Javaを選択しました。

IDE

JavaではEclipseが使われていますが、Android専用の「Android Studio」というIDEがあるため、こちらを使用します。

OS

Windows 10

Android Studioのダウンロード

Android Studioは下記のサイトからダウンロードできます。
Android Studio
1.PNG
「DOUNLOAD ANDROID STUDIO」をクリック

Android Stuidoのインストール

ダウンロードしたEXEファイルを開きます
→android-studio-ide-193.6626763-windows.exe

表示されるウィンドウに沿って、進めていきます

2.png
Nextを選択します。

3.png
デフォルトのままNextを選択します。

4.png
インストールフォルダの選択です。
人によって指定したいフォルダを選択すればよいと思います。
私はデフォルトで進めました。

5.png
スタートメニューのどのフォルダに表示するか、ですね。
これもデフォルトでよいと思います。

6.png
Installが完了し、Finishを選択します。

Android Studioの初回起動

Android Studioを起動すると、デザイン等の選択が選べます。

7.png
慣れてくれば色々改造してもいいと思いますが、Standardでいきます。

8.png
ソースコードの表示ですね。
黒でもよいとは思いますが、何となく白にしました。
多分後からでも変えられると思います。

9.png
現在のインストールされている内容の確認ですね。
Finishを選択します。

10.png
Finishを選択します。

11.png
これで初回起動の設定が完了となります。

次回からはプロジェクトの立ち上げをして、HelloWorldとか電卓とかの簡単なアプリを作っていこうと思います。

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

AndroidアプリをGooglePlayConsoleで好きなタイミングで公開する

Androidアプリの公開タイミングについて
審査後すぐ公開ではなく、自分の好きなタイミングで公開する手順を試したので
書き記しておく

公式のヘルプ
https://support.google.com/googleplay/android-developer/answer/9859654?hl=ja

前情報

  • 新しいUI(2020/11/2〜)を使用

手順

  1. GooglePlayConsoleにログイン > アプリ指定
  2. 左メニュー > 公開の概要
  3. 管理対象の公開のステータスを「オン」にする スクリーンショット 2020-10-10 11.26.40.png スクリーンショット 2020-10-10 11.26.50.png
  4. 左メニュー > リリース > 製品版 > 新しいリリースを作成
  5. 新しいバージョンのAPKファイルをアップロード > リリースのレビュー > 製品版としての公開を開始
  6. 審査中となる
  7. 「公開の概要」画面でも「審査中の変更」として表示される スクリーンショット 2020-10-10 11.40.05.png
  8. ストアページの内容変更も同様に「審査中の変更」になる
  9. 審査が終了し、「公開の概要」画面で「変更の公開準備完了」として表示される。 スクリーンショット 2020-10-11 11.34.59.png この時点では、アプリもページの変更もまだ公開されていない
  10. 自分の公開したいタイミングで「公開して確認」ボタン押下 000.png スクリーンショット 2020-10-11 11.46.32.png
  11. ボタンを押してすぐに公開されました

感想

時間指定ではなく、ボタンを押すと公開。
また、管理対象の公開機能で保留されない変更もあるので注意が必要

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

自宅以外の場所にいる子供の位置情報を全自動で通知して安心したい

はじめに

小学校の中~高学年ともなると、塾や習い事、友達の家に遊びに行ったりなど、一人で外出することが多くなるかと思います。
親としては子供が一人で外出している最中に、どこにいるのか、変なことに巻き込まれていないかなどを常に心配なものです。
子供が今どこにいるかが分かれば、少しは心配も減る・安心できるかと思い、子供に持たせたスマホ位置情報を全自動で通知する仕組みを作りました。

スマホは Andorod、自動化処理は Macrodroid を使用します。
Macrodroid とは、自分で設定した処理を実行できる Android アプリです。すべて GUI で設定ができるため、プログラマーでなくても直感的にスクリプトが作成できます。
image.png

要件定義

  • 子供の手を介さずに全自動で行う
  • 自宅 WIFI の SSID 圏外になったら、位置情報の通知処理を開始する
  • 通知は10分ごとにメールで行う
  • 自宅 WIFI の SSID 圏内になったら、位置情報の通知処理を停止する
  • 帰宅したことを通知する

環境

Android10 (SHARP AQUOS Phone SH-M07)
Macrodroid 5.6.3

Macrodroid 設定

マクロは「位置情報自動通知」と「帰宅」の2つ作ります。
いずれも、自宅 WiFi の SSID の検知状況をトリガーとしています。

マクロ その1(位置情報自動通知)

・画面下部の「マクロ」を選択します
image.png

・画面右下の ボタンを選択します
image.png

トリガー設定

トリガー完成形

image.png

トリガー設定手順

  • 「トリガー」枠の を選択
  • 「接続」→「WIFIのSSIDの変化」を選択
  • 「指定SSID圏外になったとき」を選択
  • 自宅 WIFI の SSID を選択します。SSID が複数ある場合は、対象にしたいものすべて選択します(OR条件なので、いずれか一つが見つかれば動作対象となります)。
  • 位置情報の権限を求められたら、「常に許可」を選択

以上で、トリガーの設定は完了です

アクション

アクション完成形

image.png

アクション設定手順

  • 「アクション」枠のを選択
  • 「条件分岐/繰り返し」を選択
  • 「条件で繰り返す(While)」を選択
  • 「1回は必ず実行し、条件を満たす限り繰り返す」を選択
  • 「条件」枠の を選択
  • 「接続」→「WiFiの状態」→「WiFi切断」→ 自宅 WiFi の SSID を選択

  • 「アクション」枠のを選択
  • 「位置」 →「位置情報を強制的に更新」を選択

  • 「アクション」枠のを選択
  • 「MacroDroid固有」 →「次のアクション実行前に待機」→「1分」と設定(「アラーム機能を使う」をチェックしたほうが正確に動作します)

  • 「アクション」枠のを選択
  • 「メッセージ」→「メールを送る」→「Gmailアカウント」を選択。メールアドレス、件名は任意で。本文は以下をコピペ。貼り付けたら画面上部のチェックマークを選択して保存
(自動)
現在位置を送信します。
次回の送信時刻は10分後です。

地図
http://www.google.com/maps?q=[last_loc_latlong]&center=[last_loc_latlong]

日時
[year]/[month_digit]/[dayofmonth] [hour]:[minute]:[second]

緯度経度
[last_loc_latlong]

誤差
[last_loc_accuracy]

Mobile Network Code
[mnc]

Cell ID
[cell_id]

  • 「アクション」枠のを選択
  • 「MacroDroid固有」 →「次のアクション実行前に待機」→「9分」と設定

  • 「アクション」枠の ↓↑ を選択
  • 各アクションの右側に表示される をドラッグするとアクションの位置が入れ替えられます。完成形の形に並べ替えてください。

保存

画面上部の「マクロ名を入力」に任意のマクロ名を入力し、画面右下の =+ ボタンを選択。
(私は「自宅WIFIを検出しなくなったら10分毎に現在位置を自動送信する」という名称にしています。長い)
以上で、マクロ その1 の設定は完了です。

マクロ その2(帰宅通知)

・アプリホーム画面下部の「マクロ」→ 画面右下の ボタンを選択します

トリガー設定

トリガー完成形

image.png

トリガー設定手順

  • 「トリガー」枠の を選択
  • 「接続」→「WiFi の SSID の変化」を選択
  • 「指定 SSID 圏内に入ったとき」を選択
  • 自宅 WIFI の SSID を選択

アクション

アクション完成形

image.png

アクション設定手順

  • 「アクション」枠のを選択
  • 「メッセージ」→「メールを送る」→「Gmailアカウント」を選択。メールアドレス、件名は任意で。本文は以下をコピペ。貼り付けたら画面上部のチェックマークを選択して保存
子供の携帯が家に着きました。

地図
http://www.google.com/maps?q=[last_loc_latlong]&center=[last_loc_latlong]

日時
[year]/[month_digit]/[dateofmonth] [hour]:[minute]:[second]

緯度経度
[last_loc_latlong]

誤差
[last_loc_accuracy]

Mobile Network Code
[mnc]

Cell ID
[cell_id]

保存

画面上部の「マクロ名を入力」に任意のマクロ名を入力し(私は「帰宅」という名称にしています。)、画面右下の =+ ボタンを選択。
以上で、マクロ その2 および、すべての設定が完了です。
最終的には、以下のような画面になります。
image.png

テスト

スマホを持って自宅 WiFi の電波が届かなくなる距離まで離れてみましょう(スマホの WiFi を ON/OFF してもいいと思いますが、実態に即したテストを行うことが大事です)。
メールアクションで設定した宛先に通知されるはずです。
メール本文の URL をタップすると、スマホの位置が地図アプリで確認できます。

補足

  • 10分ごとの更新としていますが、待機時間を短くすることで通知頻度を挙げられます。ただし、バッテリーの消費が早くなります。
  • 「位置情報を強制的に更新」アクションは、おまじないみたいなものです。盛大にズレることもあるので気休め程度にお考えください。
  • 子供のプライバシーも大切です。子供には「君のスマホには、外出時の位置を知らせる仕組みが設定していて、必要なときに確認できるようにしているよ」と必ず伝えておきましょう。子供が拒否したら、自動通知の仕組みを解除することも検討してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自宅以外の場所にいる子供の位置情報を全自動で通知できたら、少しは安心できるかもしれない

はじめに

小学校の中~高学年ともなると、塾や習い事、友達の家に遊びに行ったりなど、一人で外出することが多くなるかと思います。
親としては子供が一人で外出している最中に、どこにいるのか、変なことに巻き込まれていないかなどを常に心配なものです。
子供が今どこにいるかが分かれば、少しは心配も減る・安心できるかと思い、子供に持たせたスマホ位置情報を全自動で通知する仕組みを作りました。

スマホは Andorod、自動化処理は Macrodroid を使用します。
Macrodroid とは、自分で設定した処理を実行できる Android アプリです。すべて GUI で設定ができるため、プログラマーでなくても直感的にスクリプトが作成できます。
image.png

要件定義

  • 子供の手を介さずに全自動で行う
  • 自宅 WIFI の SSID 圏外になったら、位置情報の通知処理を開始する
  • 通知は10分ごとにメールで行う
  • 自宅 WIFI の SSID 圏内になったら、位置情報の通知処理を停止する
  • 帰宅したことを通知する

環境

Android10 (SHARP AQUOS Phone SH-M07)
Macrodroid 5.6.3

Macrodroid 設定

マクロは「位置情報自動通知」と「帰宅」の2つ作ります。
いずれも、自宅 WiFi の SSID の検知状況をトリガーとしています。

マクロ その1(位置情報自動通知)

・画面下部の「マクロ」を選択します
image.png

・画面右下の ボタンを選択します
image.png

トリガー設定

トリガー完成形

image.png

トリガー設定手順

  • 「トリガー」枠の を選択
  • 「接続」→「WIFIのSSIDの変化」を選択
  • 「指定SSID圏外になったとき」を選択
  • 自宅 WIFI の SSID を選択します。SSID が複数ある場合は、対象にしたいものすべて選択します(OR条件なので、いずれか一つが見つかれば動作対象となります)。
  • 位置情報の権限を求められたら、「常に許可」を選択

以上で、トリガーの設定は完了です

アクション

アクション完成形

image.png

アクション設定手順

  • 「アクション」枠のを選択
  • 「条件分岐/繰り返し」を選択
  • 「条件で繰り返す(While)」を選択
  • 「1回は必ず実行し、条件を満たす限り繰り返す」を選択
  • 「条件」枠の を選択
  • 「接続」→「WiFiの状態」→「WiFi切断」→ 自宅 WiFi の SSID を選択

  • 「アクション」枠のを選択
  • 「位置」 →「位置情報を強制的に更新」を選択

  • 「アクション」枠のを選択
  • 「MacroDroid固有」 →「次のアクション実行前に待機」→「1分」と設定(「アラーム機能を使う」をチェックしたほうが正確に動作します)

  • 「アクション」枠のを選択
  • 「メッセージ」→「メールを送る」→「Gmailアカウント」を選択。メールアドレス、件名は任意で。本文は以下をコピペ。貼り付けたら画面上部のチェックマークを選択して保存
(自動)
現在位置を送信します。
次回の送信時刻は10分後です。

地図
http://www.google.com/maps?q=[last_loc_latlong]&center=[last_loc_latlong]

日時
[year]/[month_digit]/[dayofmonth] [hour]:[minute]:[second]

緯度経度
[last_loc_latlong]

誤差
[last_loc_accuracy]

Mobile Network Code
[mnc]

Cell ID
[cell_id]

  • 「アクション」枠のを選択
  • 「MacroDroid固有」 →「次のアクション実行前に待機」→「9分」と設定

  • 「アクション」枠の ↓↑ を選択
  • 各アクションの右側に表示される をドラッグするとアクションの位置が入れ替えられます。完成形の形に並べ替えてください。

保存

画面上部の「マクロ名を入力」に任意のマクロ名を入力し、画面右下の =+ ボタンを選択。
(私は「自宅WIFIを検出しなくなったら10分毎に現在位置を自動送信する」という名称にしています。長い)
以上で、マクロ その1 の設定は完了です。

マクロ その2(帰宅通知)

・アプリホーム画面下部の「マクロ」→ 画面右下の ボタンを選択します

トリガー設定

トリガー完成形

image.png

トリガー設定手順

  • 「トリガー」枠の を選択
  • 「接続」→「WiFi の SSID の変化」を選択
  • 「指定 SSID 圏内に入ったとき」を選択
  • 自宅 WIFI の SSID を選択

アクション

アクション完成形

image.png

アクション設定手順

  • 「アクション」枠のを選択
  • 「メッセージ」→「メールを送る」→「Gmailアカウント」を選択。メールアドレス、件名は任意で。本文は以下をコピペ。貼り付けたら画面上部のチェックマークを選択して保存
子供の携帯が家に着きました。

地図
http://www.google.com/maps?q=[last_loc_latlong]&center=[last_loc_latlong]

日時
[year]/[month_digit]/[dateofmonth] [hour]:[minute]:[second]

緯度経度
[last_loc_latlong]

誤差
[last_loc_accuracy]

Mobile Network Code
[mnc]

Cell ID
[cell_id]

保存

画面上部の「マクロ名を入力」に任意のマクロ名を入力し(私は「帰宅」という名称にしています。)、画面右下の =+ ボタンを選択。
以上で、マクロ その2 および、すべての設定が完了です。
最終的には、以下のような画面になります。
image.png

テスト

スマホを持って自宅 WiFi の電波が届かなくなる距離まで離れてみましょう(スマホの WiFi を ON/OFF してもいいと思いますが、実態に即したテストを行うことが大事です)。
メールアクションで設定した宛先に通知されるはずです。
メール本文の URL をタップすると、スマホの位置が地図アプリで確認できます。

補足

  • 10分ごとの更新としていますが、待機時間を短くすることで通知頻度を挙げられます。ただし、バッテリーの消費が早くなります。
  • 「位置情報を強制的に更新」アクションは、おまじないみたいなものです。盛大にズレることもあるので気休め程度にお考えください。
  • 子供のプライバシーも大切です。子供には「君のスマホには、外出時の位置を知らせる仕組みが設定していて、必要なときに確認できるようにしているよ」と必ず伝えておきましょう。子供が拒否したら、自動通知の仕組みを解除することも検討してください。人の行動を監視するという非常に誤解されやすい仕組みでもあるため、事前の説明と同意は必須です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TimePickerDialogで未来時刻が選択出来ないようにする

TimePickerDialogで未来時刻が選択出来ないようにしようと思った。

しかし...

実装方法を探しに探したが、全く見つからぬ٩( ᐛ )و

そして私は MaterialDateTimePicker というライブラリに出会った。

実装してみる

  • build.gradleのdependenciesに以下を追加
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
  • MaterialDateTimePickerを表示してみる(ボタンをクリックしたらダイアログが表示されるようにしています)
class MainActivity : AppCompatActivity() {

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

        findViewById<Button>(R.id.show_timepicker_button).setOnClickListener {
            clickButton()
        }
    }

    private fun clickButton() {

        // TimePickerDialogのOKをクリックした時に呼ばれる。
        // キャンセルや画面をクリックしてDialogを終了した時には呼ばれない
        val callback = TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute, second ->
          // 選択した時間を取得して、何らかの処理を行う
        }

        val cal = Calendar.getInstance()

        TimePickerDialog.newInstance(
            callback,
            cal.get(Calendar.HOUR),
            cal.get(Calendar.MINUTE),
            false
        ).show(supportFragmentManager, TAG)
    }

    companion object {
        private const val TAG = "MainActivity"
    }
}

表示された( ´∀`)

未来時刻が選択出来ないようにする

val cal = Calendar.getInstance()
val hour = cal.get(Calendar.HOUR_OF_DAY)
val minute = cal.get(Calendar.MINUTE)

TimePickerDialog.newInstance(callback, hour, minute, false).apply {
  // 現在の時刻を上限に設定する
  val timepoint = Timepoint(hour, minute)
  setMaxTime(timepoint)
}.show(supportFragmentManager, TAG)

未来時刻が選択出来ないようになりました

その他の設定

  • MaterialDateTimePickerのデザインを変更
TimePickerDialog.newInstance(callback, hour, minute, false).apply {
  // TimePickerDialogのカラーを設定
  accentColor = ContextCompat.getColor(this@MainActivity, R.color.colorAccent)
  // OKボタンの文言を設定
  setOkText("よしっ!")
  // OKボタンのカラーを設定
  setOkColor(ContextCompat.getColor(this@MainActivity, R.color.colorAccent))
  // キャンセルボタンの文言を設定
  setCancelText("あかん")
  // キャンセルボタンのカラーを設定
  setCancelColor(ContextCompat.getColor(this@MainActivity, android.R.color.darker_gray))
}.show(supportFragmentManager, TAG)
  • TimePickerDialog.newInstanceのis24HourModeをtrueにすると24時間表示になる

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