20210308のAndroidに関する記事は8件です。

ビジュアルプログラミング(MIT App Inventor)で開発した Androidアプリから #toio の BLE制御を行う

はじめに

ビジュアルプログラミングで Androidアプリが開発できる MIT App Inventor(以下、App Inventor と記載)に関する記事で、BLE通信を使った #toio の制御を行う話です。

以前作ったもの

App Inventor に関し、最近は以下の記事に書いた「音の機械学習や音声合成を使うアプリ」を試しに作っていました。

App Inventor で BLEを使う

App Inventor で BLE を使うには、以下の拡張機能のページに掲載された「BluetoothLE(BluetoothLE.aix)」を読み込んで使う必要があります。

●MIT App Inventor Extensions
 https://mit-cml.github.io/extensions/
MIT_App_Inventor_Extensions_BLE.jpg

なお、冒頭に掲載していた 2つの記事のうち、音の機械学習を行う内容だったほうは、ここに掲載されている「PersonalAudioClassifier」を読み込んで作っています。

アプリを作る

今回は前回書いた記事と同様、MIT公式のバージョンではなく日本語化プロジェクトの日本語版を使って作っています。

アプリを作る細かな手順は今回は省略してますので、それに関する補足などは冒頭で掲載していた以下の記事をご参照ください

●ビジュアルプログラミングで Androidアプリ開発ができる「MIT App Inventor」を試す(ボタンを押したら音声でしゃべるアプリを作る) - Qiita
 https://qiita.com/youtoy/items/93f7c786ff9d664d3032

それでは、、手順の大まかな内容のみ書いていきます。

画面のデザインを編集する

プロジェクトを新規作成し、拡張機能の「BluetoothLE.aix」を読み込んでください。
拡張機能の読み込みは、左側のメニューの一番下にある「エクステンション」の中の「Import extension」から行います。
拡張の読み込み.jpg

拡張機能を読み込んだら、以下のようにコンポーネントを配置します。
画面の編集.jpg

ここで、上記の画面を作るために行った内容を、少し補足します。
画面上に配置したものは、画面の上から置いた順に書いていくと以下のとおりです。「ボタン」の 4つ・3つのまとまりのものは、「横並び」の中に入るように配置します。

  • 「レイアウト の 横並び」
    • 「ユーザーインターフェース の ボタン」 × 4つ
  • 「レイアウト > 横並び」
    • 「ユーザーインターフェース の ボタン」 × 3つ
  • 「ユーザーインターフェース の ラベル」
  • 「ユーザーインターフェース の リストビュー」
  • 「エクステンション の BluetoothLE」 ※ 画面上には表示されません
  • 「センサー の 加速度センサー」 ※ 画面上には表示されません

そして、上記のボタンやラベルの名前・表示するテキストなどを、以下の対応関係になるように設定しています。

  • 「横並び」: (変更なし)
    • 「ボタン」: スキャン
    • 「ボタン」: スキャン停止
    • 「ボタン」: 接続
    • 「ボタン」: 切断
  • 「横並び」: (変更なし)
    • 「ボタン」: 音を鳴らす
    • 「ボタン」: LED点灯
    • 「ボタン」: LED消灯
  • 「ラベル」: ステータス
  • 「リストビュー」: BLEリスト
  • 「BluetoothLE」: (変更なし)
  • 「加速度センサー」: (変更なし)

なお上記の画面作成について、2つ目の「横並び」以外の部分は、以下の公式チュートリアルの内容を見て作った構成です。

●App Inventor + IoT: Basic Bluetooth Connection Setup
 > http://iot.appinventor.mit.edu/assets/tutorials/MIT_App_Inventor_Basic_Connection.pdf

プログラムを作る

今回作ったプログラムは、以下のとおりです。
プログラムの内容.jpg

ここで、プログラムの内容を少し補足していきます。
このプログラムで 11個のブロックの固まりがあるのですが、その中の 7つは以下の公式チュートリアルを見て作ったものです。

●App Inventor + IoT: Basic Bluetooth Connection Setup
 > http://iot.appinventor.mit.edu/assets/tutorials/MIT_App_Inventor_Basic_Connection.pdf

具体的には、以下のブロックです(※ 上の画像とは、並びを少し変えています)。
スキャンや接続などの処理.jpg

これらのブロックは、以下の動作を行わせるためのものです。

  • 「スキャン」ボタンが押されたらデバイスのスキャンを行って、見つかったデバイスの情報をリストビューに表示
  • 「スキャン停止」ボタンが押されたらデバイスのスキャンを止める
  • リストビューに表示されたデバイスを選んだ状態で、「接続」ボタンを押したらデバイスへの接続を行う
  • 「切断」ボタンが押されたら、デバイスとの接続を切る

また、上記の動作に合わせて、画面のラベルの表示を変えたり、リストビューが不可視になるかどうかを制御していたりします。

ここからは、デバイスとの接続が行われた状態で動作させるブロックについて補足します。
それに該当するのが以下のブロックです(※ 上の画像とは、並びを少し変えています)。
接続後に行わせる処理.jpg

4つあるブロックの固まり全てで、最初にデバイスとの接続が行われているかをチェックしています。そして、接続済みであれば、以下の動作が行われるようにしています。

  • 「ボタン: 音を鳴らす」が押されたら、特定の音を鳴らす
  • 「ボタン: LED点灯」が押されたら、LED を特定の色で点灯させる
  • 「ボタン: LED消灯」が押されたら、LED を消灯される
  • スマホが揺さぶられたら、特定の音を鳴らす(加速度センサーで揺れを検知)

そして、それぞれの処理で「WriteBytes」の処理を行う部分で、2つの UUID や値などを設定しています。
2つの UUID は、以下の toio の仕様が書いてあるページのものを使います。

▼ サービスの UUID が書いてある部分(4つのブロックの固まり共通で使っているもの)
 ●通信概要 · toio™コア キューブ 技術仕様
  https://toio.github.io/toio-spec/docs/ble_communication_overview
service_uuid.jpg

▼ 音を鳴らす処理の Characteristic UUID が書いてある部分
 ●サウンド · toio™コア キューブ 技術仕様
  https://toio.github.io/toio-spec/docs/ble_sound
sound_uuid.jpg

▼ ランプを点灯・消灯させる処理の Characteristic UUID が書いてある部分
 ●ランプ · toio™コア キューブ 技術仕様
  hhttps://toio.github.io/toio-spec/docs/ble_light
ランプ_uuid.jpg

そして、特定の音を鳴らしたり、特定の色でランプを点灯させたり、その指示を出すための値の指定も、同様に上記の仕様に書かれたものを使っています。
例えば、「ボタン: 音を鳴らす」が押された時の処理は、以下を見て設定しています。
音を鳴らす時の値の指定.jpg

ブロックの中でいうと、以下の赤枠で囲んだ部分です。
音を鳴らす処理1.jpg
音量について、仕様で最大音量 0xFF というのが 16進数で書かれていますが、それを上記のブロックで指定する場合、置くべきブロックは数値となるようでした。そこで、10進数にした値の 255 を使っています。
さらに、複数の値を指定するには、リストを使って複数の数値を設定すれば良いようでした。

動作している様子

上記の手順を進めて作ったアプリを、実際に動かしてみると以下のようになります。
動画で出ている部分の前にスキャンと toio への接続をすませており、この動画では音を鳴らしたりランプを制御したりする部分をご覧いただけるようにしています。

終わりに

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

ビジュアルプログラミング(MIT App Inventor)で開発した Androidアプリから #toio の制御を行う(BLE用の拡張機能を利用)

はじめに

ビジュアルプログラミングで Androidアプリが開発できる MIT App Inventor(以下、App Inventor と記載)に関する記事で、BLE通信を使った #toio の制御を行う話です。

以前作ったもの

App Inventor に関し、最近は以下の記事に書いた「音の機械学習や音声合成を使うアプリ」を試しに作っていました。

App Inventor で BLEを使う

App Inventor で BLE を使うには、以下の拡張機能のページに掲載された「BluetoothLE(BluetoothLE.aix)」を読み込んで使う必要があります。

●MIT App Inventor Extensions
 https://mit-cml.github.io/extensions/
MIT_App_Inventor_Extensions_BLE.jpg

なお、冒頭に掲載していた 2つの記事のうち、音の機械学習を行う内容だったほうは、ここに掲載されている「PersonalAudioClassifier」を読み込んで作っています。

アプリを作る

今回は前回書いた記事と同様、MIT公式のバージョンではなく日本語化プロジェクトの日本語版を使って作っています。

アプリを作る細かな手順は今回は省略してますので、それに関する補足などは冒頭で掲載していた以下の記事をご参照ください

●ビジュアルプログラミングで Androidアプリ開発ができる「MIT App Inventor」を試す(ボタンを押したら音声でしゃべるアプリを作る) - Qiita
 https://qiita.com/youtoy/items/93f7c786ff9d664d3032

それでは、、手順の大まかな内容のみ書いていきます。

画面のデザインを編集する

プロジェクトを新規作成し、拡張機能の「BluetoothLE.aix」を読み込んでください。
拡張機能の読み込みは、左側のメニューの一番下にある「エクステンション」の中の「Import extension」から行います。
拡張の読み込み.jpg

拡張機能を読み込んだら、以下のようにコンポーネントを配置します。
画面の編集.jpg

ここで、上記の画面を作るために行った内容を、少し補足します。
画面上に配置したものは、画面の上から置いた順に書いていくと以下のとおりです。「ボタン」の 4つ・3つのまとまりのものは、「横並び」の中に入るように配置します。

  • 「レイアウト の 横並び」
    • 「ユーザーインターフェース の ボタン」 × 4つ
  • 「レイアウト > 横並び」
    • 「ユーザーインターフェース の ボタン」 × 3つ
  • 「ユーザーインターフェース の ラベル」
  • 「ユーザーインターフェース の リストビュー」
  • 「エクステンション の BluetoothLE」 ※ 画面上には表示されません
  • 「センサー の 加速度センサー」 ※ 画面上には表示されません

そして、上記のボタンやラベルの名前・表示するテキストなどを、以下の対応関係になるように設定しています。

  • 「横並び」: (変更なし)
    • 「ボタン」: スキャン
    • 「ボタン」: スキャン停止
    • 「ボタン」: 接続
    • 「ボタン」: 切断
  • 「横並び」: (変更なし)
    • 「ボタン」: 音を鳴らす
    • 「ボタン」: LED点灯
    • 「ボタン」: LED消灯
  • 「ラベル」: ステータス
  • 「リストビュー」: BLEリスト
  • 「BluetoothLE」: (変更なし)
  • 「加速度センサー」: (変更なし)

なお上記の画面作成について、2つ目の「横並び」以外の部分は、以下の公式チュートリアルの内容を見て作った構成です。

●App Inventor + IoT: Basic Bluetooth Connection Setup
 > http://iot.appinventor.mit.edu/assets/tutorials/MIT_App_Inventor_Basic_Connection.pdf

プログラムを作る

今回作ったプログラムは、以下のとおりです。
プログラムの内容.jpg

ここで、プログラムの内容を少し補足していきます。
このプログラムで 11個のブロックの固まりがあるのですが、その中の 7つは以下の公式チュートリアルを見て作ったものです。

●App Inventor + IoT: Basic Bluetooth Connection Setup
 > http://iot.appinventor.mit.edu/assets/tutorials/MIT_App_Inventor_Basic_Connection.pdf

具体的には、以下のブロックです(※ 上の画像とは、並びを少し変えています)。
スキャンや接続などの処理.jpg

これらのブロックは、以下の動作を行わせるためのものです。

  • 「スキャン」ボタンが押されたらデバイスのスキャンを行って、見つかったデバイスの情報をリストビューに表示
  • 「スキャン停止」ボタンが押されたらデバイスのスキャンを止める
  • リストビューに表示されたデバイスを選んだ状態で、「接続」ボタンを押したらデバイスへの接続を行う
  • 「切断」ボタンが押されたら、デバイスとの接続を切る

また、上記の動作に合わせて、画面のラベルの表示を変えたり、リストビューが不可視になるかどうかを制御していたりします。

ここからは、デバイスとの接続が行われた状態で動作させるブロックについて補足します。
それに該当するのが以下のブロックです(※ 上の画像とは、並びを少し変えています)。
接続後に行わせる処理.jpg

4つあるブロックの固まり全てで、最初にデバイスとの接続が行われているかをチェックしています。そして、接続済みであれば、以下の動作が行われるようにしています。

  • 「ボタン: 音を鳴らす」が押されたら、特定の音を鳴らす
  • 「ボタン: LED点灯」が押されたら、LED を特定の色で点灯させる
  • 「ボタン: LED消灯」が押されたら、LED を消灯される
  • スマホが揺さぶられたら、特定の音を鳴らす(加速度センサーで揺れを検知)

そして、それぞれの処理で「WriteBytes」の処理を行う部分で、2つの UUID や値などを設定しています。
2つの UUID は、以下の toio の仕様が書いてあるページのものを使います。

▼ サービスの UUID が書いてある部分(4つのブロックの固まり共通で使っているもの)
 ●通信概要 · toio™コア キューブ 技術仕様
  https://toio.github.io/toio-spec/docs/ble_communication_overview
service_uuid.jpg

▼ 音を鳴らす処理の Characteristic UUID が書いてある部分
 ●サウンド · toio™コア キューブ 技術仕様
  https://toio.github.io/toio-spec/docs/ble_sound
sound_uuid.jpg

▼ ランプを点灯・消灯させる処理の Characteristic UUID が書いてある部分
 ●ランプ · toio™コア キューブ 技術仕様
  hhttps://toio.github.io/toio-spec/docs/ble_light
ランプ_uuid.jpg

そして、特定の音を鳴らしたり、特定の色でランプを点灯させたり、その指示を出すための値の指定も、同様に上記の仕様に書かれたものを使っています。
例えば、「ボタン: 音を鳴らす」が押された時の処理は、以下を見て設定しています。
音を鳴らす時の値の指定.jpg

ブロックの中でいうと、以下の赤枠で囲んだ部分です。
音を鳴らす処理1.jpg
音量について、仕様で最大音量 0xFF というのが 16進数で書かれていますが、それを上記のブロックで指定する場合、置くべきブロックは数値となるようでした。そこで、10進数にした値の 255 を使っています。
さらに、複数の値を指定するには、リストを使って複数の数値を設定すれば良いようでした。

動作している様子

上記の手順を進めて作ったアプリを、実際に動かしてみると以下のようになります。
動画で出ている部分の前にスキャンと toio への接続をすませており、この動画では音を鳴らしたりランプを制御したりする部分をご覧いただけるようにしています。

終わりに

今回、App Inventor とその拡張機能を使い、BLE通信を使った #toio との接続・制御を行いました。
そして、掲載していた動画の内容のとおり、無事に動作させることができました。

今後は、toio から値を受け取る処理の実装も試すことができたらと思います。

気になる点

今回は使ってない部分で気になるところがあり、具体的には toio で値をやりとりする仕様の中で 2バイトを使って 255より大きい値を扱うところ(例えば、専用マットの位置座標を扱うところ)です。
今回のような数値を指定する形で、エンディアンを考慮した意図通りのバイトオーダーになるか・それを簡単に扱えるか、という点なのですが、そのあたりは今後確かめていこうと思っています。

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

Azure PipelinesのYAMLでAndroidアプリのApp Center配布パイプラインを構築する方法

「Azure PipelinesのYAMLでAndroidアプリのCI/CD環境を構築する」は3部構成です。
記事を順番に読み進めると、Azure PipelinesでAndroidアプリのCI/CD環境が構築できるようになります。

  • 第一部:CI環境の構築
  • 第二部:App Center配布パイプラインの構築 ←イマココ
  • 第三部:Google Play Console配布パイプラインの構築(未投稿)

はじめに

Azure Pipelinesを使い、AndroidアプリをApp Centerへ配布するCDを構築します。

本記事で説明しないこと

  • Azure Pipelinesの概要や基本的な操作方法
    私が以前書いた記事が参考になると思います
  • Visual Studio App Centerの概要や基本的な操作方法
  • CI環境の構築で説明した内容
    以前説明した内容を説明するのは冗長なのでしません

App Centerのコネクションを追加

Azure PipelinesのYAMLでiOSアプリのApp Center配布パイプラインを構築する方法 と同様なので省略します。

キーストアのアップロード

必要に応じてキーストアをSecure fileへアップロードします。
アップロードの方法は こちら をご参照ください。

私はApp Center用のキーストアはリポジトリに直接コミットしており、Secure fileは使っていません。

キーストアと鍵のパスワードの追加

必要に応じてキーストアと鍵のパスワードをVariablesに追加します。
追加方法は こちら をご参照ください。

私はApp Center用のキーストアと鍵のパスワードは /app/build.gradle に直接書いています。

設定ファイルの作成

今回はビルドタイプを debug に固定し、プロダクトフレーバーを以下の3つすべてでApp Centerに配布できるようにします。

  • develop
  • staging
  • production

プロダクトフレーバーが異なる以外は同じ処理なので、Azure DevOpsのテンプレートを利用し、共通処理を別のYAMLファイルに抜き出します。

まずは全プロダクトフレーバーの設定ファイルを作成します。
見てわかる通り productFlavor に渡す値のみ変えています。

distribute-appcenter-develop-debug.yml
trigger: none

jobs:
- job: distribute_appcenter_develop_debug
  pool:
    vmImage: 'ubuntu-latest'

  steps:
  # App Centerへ配布
  - template: templates/distribute-appcenter-debug-template.yml
    parameters:
      productFlavor: 'develop'
distribute-appcenter-staging-debug.yml
trigger: none

jobs:
- job: distribute_appcenter_staging_debug
  pool:
    vmImage: 'ubuntu-latest'

  steps:
  # App Centerへ配布
  - template: templates/distribute-appcenter-debug-template.yml
    parameters:
      productFlavor: 'staging'
distribute-appcenter-production-debug.yml
trigger: none

jobs:
- job: distribute_appcenter_production_debug
  pool:
    vmImage: 'ubuntu-latest'

  steps:
  # App Centerへ配布
  - template: templates/distribute-appcenter-debug-template.yml
    parameters:
      productFlavor: 'production'

次にテンプレートを作成します。

templates/distribute-appcenter-debug-template.yml
parameters:
- name: productFlavor
  type: string

steps:
# JDKのセットアップ
- task: JavaToolInstaller@0
  inputs:
    versionSpec: '8'
    jdkArchitectureOption: 'x64'
    jdkSourceOption: 'PreInstalled'

# 依存関係の出力
- script: ./gradlew androidDependencies
  displayName: Displays the Android dependencies of the project

# APKの生成
- script: ./gradlew assembleDebug
  displayName: Generate APK for debug

# アーティファクトのステージングへコピー
- task: CopyFiles@2
  inputs:
    Contents: |
      **/app/build/outputs/apk/${{ parameters.productFlavor }}/debug/app-${{ parameters.productFlavor }}-debug.apk
    TargetFolder: '$(Build.ArtifactStagingDirectory)'

# アーティファクトへアップロード
- task: PublishBuildArtifacts@1
  inputs:
    pathtoPublish: '$(Build.ArtifactStagingDirectory)'
    artifactName: 'drop'
    publishLocation: 'Container'

# App Centerへ配布
- task: AppCenterDistribute@3
  inputs:
    serverEndpoint: 'App Center'
    appSlug: '{Organization}/{App}'
    appFile: '**/app/build/outputs/apk/${{ parameters.productFlavor }}/debug/app-${{ parameters.productFlavor }}-debug.apk'
    releaseNotesOption: 'input'
    releaseNotesInput: |
      - Product Flavor:${{ parameters.productFlavor }}
      - Build Type:debug
      - Keystore:{Keystore name}
      - Branch:$(Build.SourceBranchName)
      - Last Commit ID:$(Build.SourceVersion)
      - Last Commit Comment:$(Build.SourceVersionMessage)
      - Release Note:$(ReleaseNote)
    destinationType: 'groups'

App CenterはAAB形式に対応していないため、APK形式でアップロードします。
参考: https://github.com/microsoft/appcenter/issues/955

AAB形式でアップロードしようとすると、以下のエラーが出力されます。

##[error]{"code":"not_supported","message":"Error: Distribution of .aab is not supported for groups/testers"}

他は基本的に Azure PipelinesのYAMLでiOSアプリのApp Center配布パイプラインを構築する方法 と同様なので省略します。

おわりに

Azure PipelinesのYAMLでAndroidアプリをApp Centerへ配布することができました!

テンプレートを利用することで共通処理をまとめられ、設定ファイルがスッキリしました。

参考リンク

APK

Templates - Azure Pipelines

App Center

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

JAVA Convert short[] to byte[]

Method 1:

byte [] ShortToByte_ByteBuffer_Method(short [] input)
{
  int index;
  int iterations = input.length;

  ByteBuffer bb = ByteBuffer.allocate(input.length * 2);

  for(index = 0; index != iterations; ++index)
  {
    bb.putShort(input[index]);    
  }

  return bb.array();       
}

Method 2:

byte [] ShortToByte_Twiddle_Method(short [] input)
{
  int short_index, byte_index;
  int iterations = input.length;

  byte [] buffer = new byte[input.length * 2];

  short_index = byte_index = 0;

  for(/*NOP*/; short_index != iterations; /*NOP*/)
  {
    buffer[byte_index]     = (byte) (input[short_index] & 0x00FF); 
    buffer[byte_index + 1] = (byte) ((input[short_index] & 0xFF00) >> 8);

    ++short_index; byte_index += 2;
  }

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

Navigation Componentで新規登録画面の実装方法(graphのネストと戻るボタンの実装)

はじめに

Navigation Component を使うと新規登録画面の画面遷移が簡単に実装できると耳にしました。
公式サイトにも実装方法が記載されていますが、なかなか難しかったので記事としてまとめます。

動作環境

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

Android Studio:4.1.1
Kotln:1.4.21
Open JDK:1.8
compileSdkVersion:30
targetSdkVersion:30
minSdkVersion:23
Navigation Component:2.3.2

目標

以下を目標とします。

  • Navigation Component UIで新規登録の画面遷移を実装できる

動作イメージ

こんな動きをする新規登録の画面遷移を Navigation Component で実装したい!!

機能概要

よくある話ですが、アプリをインストールして 新規登録 をタップして、ウィザード形式でユーザ登録などを行います。
登録が完了した画面や登録完了後に戻ってきたログイン画面で戻るボタンなどをタップすると正しい画面遷移ができています。
下図のようなイメージです。

01_実現したい機能.png

これってどのように実装しているのだろうと不思議に思うこともあります。
Navigation Componentに、公式サイトに実現の助けになるような機能が搭載されています。
この記事では実現方法を説明していきます。

Fragmentの動作

まず、実装方法の前にFragmentの画面やオブジェクトがどのように管理されているか理解して置く必要があります。
Navigation Componentでは、Fragmentの画面はActivityと同じ用にスタックで管理されています。
下図様なイメージです。

02fragmentのスタック.png

そのため、完了画面に遷移したあと、戻るボタンをタップするとデフォルトの動きでは確認画面に遷移していまいます。
これがFragmentの基本的な動作となります。
そのため、完了画面から戻るボタンや戻る機能を追加するときに、Login画面までスタックをクリアする必要があります。
上記の内容を理解した上で、実装方法を確認してください。

実装方法

前提知識

実装方法の前提知識としては、この記事を理解しておいてください。

アクション

画面遷移でスタックをクリアしたい画面にアクションを接続することにより実現可能です。
下図の様なイメージで、完了画面からログイン画面にアクションを接続します。

03_navigationファイル.png

graphのネスト

デスティネーションが増えていくと、navigationファイルの編集画面が操作しづらくなってしまいます。
なので、機能毎などでgraphをネストするとわかりやすくなります。

1機能のデスティネーションをすべて選択した状態で、「 group into nest graph 」ボタンをクリックします。
操作方法は下図を参照してください。

04_navigationのネスト方法.png

group into nest graph 」ボタンをクリックすると、ネストしたgraphは 角がまるい四角 になります。
ネストしたgraphの詳細を確認したい場合は、 角がまるい四角 をダブルクリックするだけで下図が表示されます。

05_ネストした中身の確認.png

ネストしたgraphからもとの画面に戻りたい時は、Component Treeで外側をクリックすると元の画面に戻ります。

06_ネストの外を表示.png

実際のコードを確認すると、navigationタグがネストしていることが確認できます。

07_コードで確認.png

ネストを利用することにして、機能ごとにnavigaitonをまとめられるので便利ですね。
navigationファイルを新規作成してincludeしても同じ事が行なえます。

スタックを指定したFragmentまでクリアする

先に説明した通り、ウィザード形式のように元の画面までスタックをクリアするには以下の手順を行います。

  1. アクションに設定を追加する

アクションタグの属性に以下の2つを設定します。

  • popUpTo属性
  • popUpToInclusive属性

最終的に下記のようなアクションになります。

<action
    android:id="@+id/action_completeToRegisterUserFragment_to_loginFragment"
    app:destination="@id/loginFragment"
    app:popUpTo="@id/loginFragment"
    app:popUpToInclusive="true" />

popUpTo属性

公式サイトに説明があります。

Navigation ライブラリに対し、navigate() 呼び出しの一環として、バックスタックからいくつかのデスティネーションをポップするように指示します。属性値として、スタック上に残す最も新しいデスティネーションの ID を指定します。

スタックにある、navigationは一番新しいデスティネーションを探し出してくれるみたいですね。
しかし、これだけではうまく動作しません。

popUpToInclusive属性

こちらも公式サイトに説明があります。

app:popUpToInclusive="true" を追加すると、app:popUpTo 内で指定したデスティネーションもバックスタックから削除するように指示できます。

これで古いLogin画面を削除してアクションので指定しているデスティネーションを新規にスタックに乗っけているわけですね。

なるほど、その他の説明もかいてありますが、興味のある方は公式サイトを参照してください。

戻るボタンの処理を変更する

Androidで用意されているソフトウェアやハードウェアの戻るボタンの挙動も修正しなければなりません。
公式サイトに詳しく書かれていますが、どうやら ** OnBackPressedDispatcher#addCallback** で戻るボタンの挙動を変えるみたいです。

実際のコードは以下の通りです。

// 戻るボタンを押下されても、ログイン画面に戻るようにこの画面のライフサイクル中の戻るボタンのコールバックを変更
requireActivity().onBackPressedDispatcher.addCallback(this) {
    val navController = findNavController()
    val action =
        CompleteToRegisterUserFragmentDirections.actionCompleteToRegisterUserFragmentToLoginFragment()
    navController.navigate(action)
}

addCallback() メソッドの引数に ライフサイクルオーナーを渡す方法渡さない方法 があります。

違いは以下のとおりです。

方法 挙動
ライフサイクルオーナーを渡す方法 そのライフサイクルの間、Callbackで指定した戻るボタンの挙動が有効になる
ライフサイクルオーナーを渡さない方法 アプリが終了するまで、Callbackで指定した戻るボタンの挙動が有効にになる

今回のケースでは、ライフサイクルオーナーを渡す方法が正しいとなります。

以上で実装は完了です。

まとめ

Androidアプリでウィザード形式を実装する方法がとても簡単になりました。
Navigation Componentを今まで避けて通ってきてきた私でしたが、今後は積極的に利用したくなりました。

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

Android API29未満で getRawX(index)をする(回転、拡大、縮小に対応)

getX()とgetRawX()

MotionEventにはgetX()とgetRawX()という異なるメソッドがあります。
(もちろん同様にy座標に関するメソッドもあります)

この2つのメソッドの違いは、getX()はEventの対象となるViewのローカル座標、getRawX()はスクリーン上の座標を取得するという点になります。

getRawX()とgetRawX(index)

API29からgetRaw(index)というメソッドが追加されました。
MotionEventではタッチされている座標(pointer)が複数ある場合があり、indexを指定することでpointerごとの座標を取得することができます。

しかし、getRawX(index)はAPI29から追加されたメソッドのため、API29未満では自分で計算を行うことになります。
API29未満にもgetX(index)というメソッドがあります、これは指定したindexのpointerのローカル座標を取得するメソッドです。このメソッドを利用して計算をします。

ローカル座標からスクリーン座標への変換

具体的なコードは以下になります。
Viewが回転、拡大、縮小していた場合にも正しい座標が取得できるようになっています。
回転、拡大、縮小が不要な場合には該当のコードを削除してください。

private fun getRawPoint(view: View, event: MotionEvent, index: Int): PointF {
    val location = intArrayOf(0, 0)
    view.getLocationOnScreen(location)
    var x = event.getX(index) * view.scaleX
    var y = event.getY(index) * view.scaleY
    var angle = Math.toDegrees(atan2(y.toDouble(), x.toDouble()))
    angle += view.rotation.toDouble()
    val length = hypot(x, y)
    x = (length * cos(Math.toRadians(angle))).toFloat() + location[0]
    y = (length * sin(Math.toRadians(angle))).toFloat() + location[1]
    return PointF(x, y)
}

scaleが変更されていた場合を考慮し、x, yの値にscaleをかけています。
また、回転していた場合を考慮し、アークタンジェントを求めるatan2(y, x)を利用して座標から角度を求めrotationと合計しています。
解説のため下図を用意しました。
rotationでαの角度、atan2(y, x)でβの角度を求めています。
lengthが三角関数の斜辺となりα + βが三角関数のθとなるのであとはsin、cosで座標が求められますね。

スクリーンショット 2021-03-08 10.35.06.png

API29未満でMotionEventからスクリーン座標を取得したいときにお使いください。

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

【Flutter】Flutter2.0で何が変わるのか?

Flutter2.0のリリース発表

2021年3月3日水曜日にFlutter2.0が発表されました。

5つのOSへの対応、WEBブラウザへの正式対応

待望のFlutterのメジャーアップグレード
今回のアップグレードにより、Webモバイルデスクトップアパリケーションを正式にサポートいたしました。

Flutter2.0では、基本の部分において、同じソースコードを使用して、ネイティブアプリ(iOS、Android)、Windows、macOS、Linuxの5つのOSにリリースできます。
※また、Chrome、Firefox、Safari、Edgeなどのブラウザを対象としたWebも含まれます。

Flutterは、車、テレビ、スマートホーム関連のモジュールに組み込むこともできます。

Playストアだけでもすでに150,000を超えるFlutterアプリがあり、Alibaba、Tencent、Baidu、WeChat、Grab、Yandex Go、Nubank、Sonos、Fastic、Betterment、realtor.com などの人気アプリを含む世界中の顧客がFlutterを使用しています。

Flutterの効率化

Googleの1,000人を超えるエンジニアがDartとFlutterを使用してアプリを構築しています。
Stadia、Google One、Google Nest Hubなど、これらの製品のFlutterで構築されています。
Google Payは、数か月前に主力のモバイルアプリとしてFlutterに切り替えましたが、すでに生産性と品質が大幅に向上しています。チームはソースコードを統合することで、プラットフォーム間の機能の違いを取り除き、50万行を超えるコードを排除しました。

Google Payは、はるかに効率的であり、技術的負債が大幅に削減され、iOSとAndroidの両方でセキュリティレビューや実験などの統合リリースプロセスが行われると報告しています。

Flutter WEB

今回の初期リリースは、特に3つのアプリケーションにフォーカスしています。

  • PWA
  • SPA
  • 既存のFlutterモバイルアプリのWebへの拡張

TOYOTAのFlutter採用

世界で最も売れている自動車メーカーであるトヨタは、Flutterを搭載したシステムを構築することにより、市場で最高のデジタル体験を車両にもたらす計画を発表しました。
TOYOTA_flutter

個人的に便利になりそうなこと

コマンドラインからのIPAファイルの生成

flutter build ipaのコマンドにより、Xcodeを開かずに、IPAファイルを生成することが可能になりました。
https://github.com/flutter/flutter/pull/67781

テキストの自動補完ライブラリの追加

AutocompleteCoreというライブラリの追加により、テキストボックス等への入力時に候補を表示したりする機能が提供されました。
AutocompleteCore

Flutter Fix

Flutterのメンテナンス速度は驚くほど速く、かなりの更新が発生します。
そのため、非推奨となってしまうコードも多いです。

ですがそのような問題に対しても、以下のコマンドで自動的に非推奨の箇所を明示してくれるようです。

$ dart fix --dry-run

また、自動で修正をさせたい場合は以下のコマンドで実行できます。

$ dart fix --apply

DevTools 「Enable Invert Oversized Images」

表示されているよりも高い解像度の画像を簡単に確認できる機能です。これにより、アプリの過剰なサイズとメモリ使用量を追跡できます。この機能を有効にすると、フラッターインスペクターで特大画像が反転して表示されます。
Enable Invert Oversized Images

DartのNullSafetyサポート

Flutter2と同時にDartの2.12が発表され、待望のNull Safetyがリリースされました。

参考記事

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

(要注意)CoroutineのasyncはConcurrencyが必ずParallelismではないこと

COROUTINEの並列実行の速度測定する為、
本来FLUX(reactor-core)の部分をCOROUTINEに変える時の発見なんです。

タスクはあるウェブサイトから画像264枚をダウンロードし、JPGファイルとして保存する。
私のノートパソコンはWINDOWS 10、AMD Ryzen 5 3500Uだから
以下の処理は8 Threadの並列実行になります。

FLUX(reactor-core)

    Flux.fromIterable(taskStream.map {
        Mono.fromCallable { it.invoke() }.onErrorResume { Mono.empty() }
    })
        .parallel()
        .runOn(Schedulers.parallel())
        .flatMap { x: Mono<Boolean> -> x }
        .subscribe(onProcess, whenError, whenFinish)

結果

...
/galleries/1809504/262.jpg
/galleries/1809504/263.jpg
/galleries/1809504/264.jpg
Calculation of Flux time :53075 milliseconds

Coroutine Async

Kotlin Coroutine 入門2: 並列実行と Structured Concurrency と例外 を参考して以下を作成。

        taskStream.map { activeScope.async { it.invoke() } }.awaitAll()

結果

...
/galleries/1809504/262.jpg
/galleries/1809504/263.jpg
/galleries/1809504/264.jpg
Calculation of Coroutine time :340325 milliseconds

原因

え。。Fluxより6.41倍の時間も掛かった。これは間違いなく1 Threadの実行結果。
でも上の記事ではasyncを使って2秒のタスク二つを2秒で完成したはず。これは何のことでしょう!?

答えはここに見つけた: 
CONCURRENT COROUTINES – CONCURRENCY IS NOT PARALLELISM
簡単に言うとKotlin Coroutine 入門2の例は実際はdelay(2000)を使って何にもしてないからだ。
Coroutineでは1ThreadでもdelayとかawaitとかSuspend関係のコードがあったら、
その暇を利用して他のタスクを実行する!だからdelay(2000)の二つは2.165秒で完成出来た。
しかし、実際CPUの計算を2秒掛かったタスクの場合は4秒以上になってしまう。
入門2の例は並行処理だ、並列処理ではない。

並列処理

CONCURRENCY IS NOT PARALLELISMからは実際の並列実行(Parallel Coroutines)を必要の条件も書いてる

1 Run in GlobalScope (GlobalScopeを使う)

val one = GlobalScope.async { doSomethingUsefulOne() }
val two = GlobalScope.async { doSomethingUsefulTwo() }

2 Specify a coroutine dispatcher (dispatcherを指定する)

val one = async(Dispatchers.Default) { doSomethingUsefulOne() }
val two = async(Dispatchers.Default) { doSomethingUsefulTwo() }

改正

早速改正して

        taskStream.map { activeScope.async(Dispatchers.Default) { it.invoke() } }.awaitAll()

結果

...
/galleries/1809504/262.jpg
/galleries/1809504/263.jpg
/galleries/1809504/264.jpg
Calculation of Coroutine time :56176 milliseconds

これはFluxと完全一致の並列実行速度。そしてこの簡潔な表現、Kotlinやっぱり素晴らしい!

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