20190828のAndroidに関する記事は12件です。

【ドメイン取得不要】無料でAdMobのapp-ads.txtに対応してみた

はじめに

ちゃんとしたドメインのアプリのLPやウェブサイトを持ってる人には縁のない記事です?‍♂️
そんなものを持っていない私が、この度Firebase Hostingを利用してお金をかけずにapp-ads.txtの設置に成功したので備忘録として残しておきます?

「そもそもapp-ads.txtってなんやねん」という人はこの記事がわかりやすかったです。

AdMobから「AdMob使ってるならapp-ads.txt置いてね」というメールが届きました。

ファイルは、掲載している Google Play ストア(Android)または App Store(iOS)のウェブサイトのルートディレクトリに公開する必要があります。
詳細については、IAB による app-ads.txt の仕様を参照してください。

メールをよく読んでみると、どうやらアプリストアに登録しているウェブサイトのルートディレクトリに設置する必要があるようです。
また、よくハマりがちですがヘルプを読むとサブドメインに置くのはNGのようです⚠️

本記事が想定している読者は下記に当てはまるような方々です。

△ そもそもストアにウェブサイトを登録していなかった(今回外部サイトでもいいので準備する必要があります?)
○ アプリのLPが無いため、ストアに登録していたウェブサイトが外部サービスだった?
○ やるべきことは分かったが、app-ads.txtのためだけにドメインにお金を払いたくない?

私自身も個人でいくつかアプリを提供していますが

  • ストアに登録しているウェブサイトはTwitterだった
  • ドメインを持っているがブログで利用中(ルートディレクトリにファイルを置けない)

というような状態でした。

手順

Firebaseコンソール上での作業

Hostingの利用開始

Firebaseプロジェクト未作成の場合は最初にプロジェクトを作成する必要があります。

image.png

プロジェクトの[Hosting]を開いて[始める]を押すと、このようにドメインが2つ発行されます。
ランダムな文字列でかっこ悪いのが玉に瑕ですが無料なので目を瞑ってどちらかを使っていきましょう。

image.png

ローカルでの作業

Firebase Hostingの初期化とセットアップ

Firebaseを初めて利用する場合にはFirebase CLIのインストールが必要です。
詳細な方法としては公式ドキュメントを見てください。

# 初回のみ必要(ログイン済みの状態でログインしても問題ないです!)
$ firebase login

# 適当なディレクトリを作る
$ mkdir hoge && cd hoge

# プロジェクトの初期化をする
$ firebase init

# このときHostingだけ選択する
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◯ Functions: Configure and deploy Cloud Functions
❯◉ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules

# 準備したプロジェクトを選択する
? Select a default Firebase project for this directory:
  [don\'t setup a default project]

# 何も入力せずにEnterを押せばOK
? What do you want to use as your public directory? (public)

# 何も入力せずEnterを押す(No扱いっぽい)
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) 
   :
  (中略)
   :
✔  Firebase initialization complete!

app-ads.txtの設置

image.png

AdMobコンソールの[すべてのアプリ]→[APP-ADS.TXT]タブを開き、右端にある[APP-ADS.TXTの設定方法]を押すとこのような画面が表示されます。
2のコードスニペットをコピーしてそのまま利用します。

image.png

# コードスニペットを書き込んだapp-ads.txtを生成する
$ echo "コピーしたコードスニペット" > public/app-ads.txt

登録するウェブサイトの設置

このタイミングでLPを作りたい人はそれをそのままpublic/index.htmlとして置き換えましょう。

これが今回の肝なのですが、私のようにapp-ads.txt設置前と変わらず外部サイトを掲載したい場合は、下記のようなindex.htmlを用いてリダイレクトさせます?

index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="1;URL=登録したいウェブサイトのURL(外部サイト可)">
</head>
</html>

deployする

# public配下がこのようになっていることを確認する
$ ls public/
404.html    app-ads.txt index.html

# deployする
$ firebase deploy

Deployに成功したら Firebase Hostingに発行されたドメイン/index.html にアクセスし、自分の設定したindex.htmlが置かれていることを確認します。
(外部サイトへリダイレクト設定した場合には1秒程度でリダイレクトがかかるはずです。)
また、Firebase Hostingに発行されたドメイン/app-ads.txtにアクセスできることも確認しておきます。

各種アプリストアのコンソール上での作業

ウェブサイトURLの登録

各OSのアプリストアのコンソールからアプリのウェブサイトが登録できるのでそこに一つ前の手順で確認した Firebase Hostingに発行されたドメイン/index.html を入力します。
このときアプリのウェブサイトでなくデベロッパーのウェブサイトと間違えやすいですが、そうするとAdMobのクローラーに見つけてもらえなくなるので注意が必要です⚠️

反映を待つ

これで必要な作業はすべて終了です、あとは待つだけです!
正常に設置できるとこのような表示になりますが、緑のランプが点灯するまで予想以上に時間がかかりますので気長に待ちましょう?
私の場合はちゃんと設置できているのに1日経過後にグレーのランプになり、最終的に3~4日くらい経ってから緑になりました。

image.png

おわりに

最初は「app-ads.txtのためにドメイン取るんか…正直面倒だし勿体ないな」と思っていましたが、このようにすれば無料でも対応可能でした、Firebase万歳ですね?
リダイレクトのさせ方は諸説あると思いますがウェブは全然分からないのでとりあえず目についた方法で対応しています、詳しい方は是非コメント欄にて優しくご指摘いただけますと幸いです?‍♂️

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

[Maps SDK] 地図にMarkerを置く(その2・Utility Library編)

前に書いた記事で、Maps SDK for Android で表示した地図に配置する Marker について書きました。基本機能だけでも色々できますが、Google Maps Android API Utility Library を導入すると更にできることが広がります。
ここでは Utility Library の導入と使用法についてまとめてみました。

  1. 基本機能だけでできること(前の記事)
  2. Utility Library を使ってできること(この記事)

この記事でやること

  • Utility Library の導入
  • Marker をクラスタにまとめる
  • クラスタ化した Marker をカスタマイズする
  • Icon Generator を使って地図に地名を表示する

注意事項

Android Studio で Maps SDK for Android が使えることを前提にしています。そのための手順は以前まとめましたので、そちらご確認下さい。

環境

このページに書かれているコード等は以下の環境下で動作・検証しています。

macOS Mojave バージョン10.14.6
Android Studio 3.5
Pixel3a + Android 9

また、ここに書いた内容をもとにしたサンプルコードを GitHub で公開しています。

Utility Library の導入

モジュールの build.gradle に以下の dependencies を追加します。

dependencies {
    compile 'com.google.maps.android:android-maps-utils:0.5+'
}

Marker をクラスタにまとめる

地図のズームレベルが下がって複数の Marker が一箇所に集まったとき、「クラスタ」にまとまるようにすることができます。
下の図だと、左が全ての Marker がバラバラに表示されている状態、右がクラスタになって表示されている状態です。

util_1.png util_2.png

以下の手順で実装することができます。

  1. ClusterItem を実装したクラスを作成
  2. ClusterManager オブジェクトを生成
  3. ClusterItem を ClusterManager に追加

ClusterItem を実装したクラスを作成

Marker ひとつひとつに相当するクラスです。 com.google.maps.android.clustering.ClusterItem を実装して、必要なメソッドを定義すればOKです。
getSnippet / getTitle / getPosition では、それぞれ Marker の snippet / titie / position に相当するものを返すようにします。以下は実装例です。

class SegmentClusterItem(val segment: Segment) : ClusterItem {
    override fun getSnippet(): String = segment.flowerName
    override fun getTitle(): String = segment.title
    override fun getPosition(): LatLng = segment.coordinate
}

ClusterManager オブジェクトを生成

com.google.maps.android.clustering.ClusterManager クラスをインスタンス化します。
このとき GoogleMap の以下のメソッドに ClusterManager インスタンスを設定するようにします。

  • setOnCameraIdleListener
  • setOnMarkerClickListener

以下は ClusterManager をインスタンス化しているところです。

val manager = ClusterManager<SegmentClusterItem>(this@ClusteringActivity, this).apply {
    setOnCameraIdleListener(this)
    setOnMarkerClickListener(this)
}

ClusterItem を ClusterManager に追加

あとは ClusterManager#addItem で ClusterManager に ClusterItem を追加していくだけです。これで Marker がある程度まで接近するとクラスタにまとまるようになります。

Segment.values().forEach {
    manager.addItem(SegmentClusterItem(it))
}

クラスタにならない場合

上記の実装をしても Marker がクラスタにまとまらない場合は、Marker の数が少ないことが考えられます。
Marker がクラスタにまとまるかどうかは、一定の領域内に含まれる Marker(=ClusterItem)の数で判定されます。デフォルトでは、5個以上の Marker が領域内に含まれないとクラスタになってくれません。これを変更するには、クラスタにする Marker の数を定義した ClusterRenderer オブジェクトを ClusterManager#setRenderer にセットする必要があります。

以下は、DefaultClusterRenderer を継承した ClusterRenderer クラスの実装例です。shouldRenderAsCluster で「何個以上の Marker が集まったらクラスタにするか」を判定します。この例では、2つ以上でクラスタになるようにしています。

class SegmentClusterRenderer(context: Context, map: GoogleMap, manager: ClusterManager<SegmentClusterItem>) : DefaultClusterRenderer<SegmentClusterItem>(context, map, manager) {
    override fun shouldRenderAsCluster(cluster: Cluster<SegmentClusterItem>?): Boolean {
        // ClusterItemが一定距離内にいくつ集まったらクラスタ化するかをBooleanで返す
        return cluster?.size ?: 0 >= 2
    }
}

以下でこの Renderer を Manager にセットしています。

manager.renderer = SegmentClusterRenderer(this@ClusteringActivity, map, manager)

クラスタ化した Marker をカスタマイズする

ClusterRenderer と IconGenerator を併用することで、Marker のアイコンやクラスタのアイコンを変更することができます。
下の図は、大阪市の各区役所に Marker を立てて、区の花をアイコンで表示しています。クラスタになると、そこに含まれる Marker の数が女性のイラストに表示されるようにしています。
util_3.png util_4.png

ClusterRenderer で継承すべきメソッドは以下の通りです。
両方とも実装すべき処理は同じで、Marker やクラスタが描画されるときの MarkerOptions を変更することになります。

メソッド 実装内容
onBeforeClusterItemRendered Marker(=ClusterItem) のカスタマイズ
onBeforeClusterRendered クラスタのカスタマイズ

また com.google.maps.android.ui.IconGenerator を使えば任意のレイアウトで Bitmap を生成することが可能です。生成した Bitmap を、BitmapDescriptorFactory#fromBitmap を使って MarkerOptions#icon にセットすることで、Marker やクラスタのアイコンを任意に変更しています。
以下は DefaultClusterRenderer を継承した ClusterRenderer の例で、 res/layout/icon_segment.xml のレイアウトを使って Marker にアイコンを設定しています。icon_segment.xml には imageIcon というIDが付与された ImageView だけが配置されています。

private val itemImageView: ImageView
private val itemIconGenerator: IconGenerator = IconGenerator(context).apply {
    val iconView = LayoutInflater.from(context).inflate(R.layout.icon_segment, null, false).apply {
        itemImageView = findViewById(R.id.imageIcon)
    }
    setContentView(iconView)
}

override fun onBeforeClusterItemRendered(item: SegmentClusterItem, markerOptions: MarkerOptions) {
    itemImageView.setImageResource(item.segment.imageResId)
    val icon = itemIconGenerator.makeIcon()
    markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon))
}

ざっくり以下のような手順です。

  1. レイアウトから View をインフレート
  2. インフレートした View から、操作したい View を findViewById で取得
  3. IconGenerator#setContentView で、1の View をセット
  4. 2に任意の文字や画像をセット
  5. IconGenerator#makeIcon で Bitmap を生成
  6. MarkerOptions#icon に生成した Bitmap をセット

Icon Generator を使って地図に地名を表示する

地図上に地名などを表示したいという要件もあると思いますが、Marker に title や snippet をセットするだけでは Info Window のコンテンツとしてしか表示できません。
なのでここでは IconGenerator を使って、マーカーとして地名を強引に表示してみました。
util_5.png

IconGenerator で文字画像を作成するのは、前項と同じやり方で可能です。TextView を含んだレイアウト(もしくは TextView そのもの)を IconGenerator のコンテンツとして設定し、任意の文字列を TextView にセットしてから IconGenerator#makeIcon するという手順になります。
これで Bitmap ができますので、あとは Marker を生成して icon にセットするということになります。

val textView: TextView
val iconGenerator = IconGenerator(this@CustomIconActivity).apply {
    setBackground(null)    // nullにしないとデフォルトの背景が表示されてしまう(文字だけにならない)
    textView = layoutInflater.inflate(R.layout.icon_station_name, null, false) as TextView
    setContentView(textView)
}

textView.text = station.title

addMarker(MarkerOptions()
    .icon(BitmapDescriptorFactory.fromBitmap(iconGenerator.makeIcon()))
    .position(station.position)
    .anchor(0.5f, 0.0f)
    .zIndex(2.1f))

まとめ

以上、Utility Library を使った Marker のカスタマイズについてまとめてみました。
Utility Library には KML を扱ったりヒートマップを追加したりと、他にも多彩な機能が用意されています。
「参考」に公式ドキュメントへのリンクを貼っておきましたので、実際に読んで色々触ってみるのをお勧めします。この記事がそのきっかけになれば、大変嬉しく思います。

参考

Google Maps Android API Utility Library
googlemaps/android-maps-utils (GitHub)

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

Github Actions (v2) でDanger + ktlintを実行させる

はじめに

自分の環境でもようやくGithub ActionsのBetaが来たのでさっそく触ってみました。
Dangerを利用してプルリクエスト時に自動コードチェック出来るようにしていきます。

Github Actionsの設定ファイル

いきなり本題ですがGithub Actionsのymlファイルです。
Danger自体の設定ktlint自体の設定は今回省略するので、知りたい方はそれぞれ左記のリンクを参照してください。

.github/workflows/ktlint.yml
name: CI
on:
  pull_request:
    branches:
    - master
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Setup ruby
        uses: actions/setup-ruby@v1
        with:
          ruby-version: '2.6'
          architecture: 'x64'
      - name: install danger
        run: |
          gem install bundler
          bundle install
      - name: run ktlintCheck
        run: ./gradlew ktlintCheck
      - name: run danger
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: danger

それでは設定ファイルの内容を順番に説明していきます。
まず、onpull_requestを指定することでターゲットブランチがmasterのプルリクエストを作成することでCIが走るようになっています。

on:
  pull_request: # ここをpushにするとpush時にCIが走る
    branches:
    - master

続いてruns-onでコンテナのイメージを選択できます。現時点では下記の表のVirtual environmentにあるようにWindows、Ubuntu、macOSと選択できます。

jobs:
  build:
    runs-on: ubuntu-latest

スクリーンショット

最後にstepsです。処理の塊をActionと呼び、Actionはusesに設定することが出来ます。既に用意されているActionの一覧はこちらを参照してください。

    steps:
      - uses: actions/checkout@v1 # git checkoutしてくれるAction。
      - name: Setup ruby
        uses: actions/setup-ruby@v1 # ruby設定用のAction
        with:
          ruby-version: '2.6'
          architecture: 'x64'
      - name: install danger # dangerはrunでスクリプトを記述する
        run: |
          gem install bundler
          bundle install
      - name: run ktlintCheck # ktlintCheckを実行させる
        run: ./gradlew ktlintCheck
      - name: run danger
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # GITHUB_TOKENがデフォで発行されるのでそれをdangerで利用する。
        # 環境変数についての詳しい話は↓参照
        # https://help.github.com/en/articles/virtual-environments-for-github-actions
        run: danger # dangerはgithub actionsに対応しているため実行するだけでOK!

なんと、、たったこれだけでプルリクで自動コードチェックが走りました。
自前でのGithubトークンの発行やbotアカウントの用意も不要です!

スクリーンショット

非常に便利!
コードはこちらに置いてあるので参考にどうぞ。

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

Activityのライフサイクルについて確認、ついでにアプリ作った

Activityのライフサイクルとは

Androidでなんか作ろうとするときにぶつかる1stステージの反り立つ壁(大げさ)
ここで時間をかけたり、開発をリタイアしないように頑張る。

ライフサイクルって?

activity_lifecycle.png

詳しいことは下記の公式リファレンスや先人が書いた有用な記事を読んでみよう。
Activity Lifecycle
アクティビティ
Activity のライフサイクル再確認

実際に確認してみる

これがやりたかった。
アプリを作り、アプリを動かして確認してみた。
onCreate()、onResume()などが呼び出されるタイミングで音を鳴らして確認してみる。

アプリ起動

聞き取れなくもないが、やっぱ聞き取れない

方針変更

音を鳴らすのではなく、画面にテキストを表示する方針に変更した
できたものがこれ

せっかく作ったからアプリを公開

Activityろがー
画面回転させたり、マルチウィンドウ機能使ったりして遊んでみてください。
やってることは該当するメソッドが呼び出されたときテキスト表示、onDestroy()のときは音声出力。

おわり\(^o^)/

蛇足ライフサイクルという言葉の意味について調べてると
”誕生から死にいたる人の一生。人生の周期”
とあった。こう書かれるとユーザーの操作によって簡単に破棄されたりするアクチビチーに変に感情移入してしまった。。がその5秒後には擬人化して、かわいいボイスで「私はたぶん3人目だと思うから...」とか「メモリから圧迫されます!!」とか言わせたら面白そうとか全く別のことを考えてた。

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

Activityのライフサイクルについて確認。ついでにアプリ作った

ライフサイクルとは

誕生から死にいたる人の一生。人生の周期。

Activityのライフサイクルって?

activity_lifecycle.png

詳しいことは下記の公式ドキュメントや先人が書いた有用な記事を読んでみよう。
Activity Lifecycle
アクティビティ
Activity のライフサイクル再確認

実際に確認してみる

これがやりたかった。
アプリを作り、アプリを動かして確認してみた。
onCreate()、onResume()などが呼び出されるタイミングで音を鳴らして確認してみる。

アプリ起動

聞き取れなくもないが、やっぱ聞き取れない

方針変更

音を鳴らすのではなく、画面にテキストを表示する方針に変更した
できたものがこれ

せっかく作ったからアプリを公開

Activityろがー
画面回転させたり、マルチウィンドウ機能使ったりして遊んでみてください。
これすると

おわり\(^o^)/

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

Activityのライフサイクルについて確認するアプリ作った

ライフサイクルとは

誕生から死にいたる人の一生。人生の周期。

Activityのライフサイクルって?

activity_lifecycle.png

詳しいことは下記の公式ドキュメントや先人が書いた有用な記事を読んでみよう。
Activity Lifecycle
アクティビティ
Activity のライフサイクル再確認

実際に確認してみる

これがやりたかった。
アプリを作り、それを動かしてonCreate()、onResume()などが呼び出されるタイミングで音を鳴らして
アプリを起動してから、ユーザーの操作に応じて呼び出されるメソッドを確認してみる。

アプリ起動

聞き取れなくもないが、やっぱ聞き取れない

方針変更

音を鳴らすのではなく、画面にテキストを表示する方針に変更した
できたものがこれ

せっかく作ったからアプリを公開

Activityろがー
画面回転させたり、マルチウィンドウ機能使ったりして遊んでみてください。

おわり\(^o^)/

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

【ReactNative】native_modules.gradle line 191でハマった話

概要

新任者の端末内に研修用の簡単なReactNativeのプロジェクトを作った時にハマった話。

発生経緯

デスクトップに「新任研修」というフォルダを作り、既に他端末で動作確認済みのReactNativeプロジェクトをまるっとコピー。
念のため動作確認のためrun-androidさせてみる

react-native run-android

すると下記のエラーが発生

/Users/lukaskurucz/Git/app-name/node_modules/@react-native-community/cli-platform-android/native_modules.gradle' line: 191

なんじゃこりゃ。native_modules.gradleなんて見たこともないモジュールがエラーを吐いている。

ゴミが残ってるのかもと、一度クリーンしてみる。

cd android && gradlew clean

が、そもそもcleanが同じエラーで通らない。

原因

仕方がないのでnative_modules.gradleとやらを見てみることにする。
どうやら下記の行あたりでエラーが出ているようだ。

native_modules.gradle
def json = new JsonSlurper().parseText(reactNativeConfigOutput)
this.packageName = json["project"]["android"]["packageName"]

これだけ見ていても訳が分からないので、とりあえずJSONにパースする前のreactNativeConfigOutputをログに吐かせてみる。

native_modules.gradle
// reactNativeConfigOutputをログに書き出し
this.logger.info(reactNativeConfigOutput)
def json = new JsonSlurper().parseText(reactNativeConfigOutput)
this.packageName = json["project"]["android"]["packageName"]

するとreactNativeConfigOutputが見事に文字化けしているではないか!!
しかも化けているのはプロジェクトのパス部分。

あ、そういえばさっき・・・

デスクトップに「新任研修」というフォルダを作り...

絶対それが原因だ!!
と思い、試しに「新任研修」を「work」にリネームしてみると問題なく動作するようになった。

パスに日本語を含めるという初歩的なミスでしたとさ。

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

THETA で OpenCV を使って色検知してみた

Maker Faire Tokyo 2019 に THETA プラグインのネタで参加

こんにちは。リコーの@ueue です。

8 月 3(土)4(日)に東京ビッグサイトで行われた Maker Faire Tokyo(MFT)に THETA プラグインネタで参加しました。
僕は、「だるまさんがころんだ」というネタを持ち込みました。
この記事では「だるまさんがころんだ」の中身について説明します。
メインは OpenCV による特定の色の動き検知になります。

ちなみに他に MFT に持ち込んだネタは以下に詳細な記事があります。

THETA V、THETA Z1 は OS に Android を採用していて、本体内部で Android アプリを動かすことができます。
THETA 本体にインストールできる Android アプリのことを THETA プラグインと呼んでいて、開発したプラグインは公式プラグインストアにて配布することができます。

インスタントカメラもライントレーサーも、そしてこのだるまさんがころんだもすべて THETA プラグインでつくられています。
これらのコードはGithub で公開されており、
今回の「だるまさんがころんだ」プラグインもこちらで公開しています。

THETA で「だるまさんがころんだ」とは?

gainenzu.png

普通のだるまさんがころんだは鬼が「だるまさんがころんだ」といい終わったタイミングでふりかえり、
鬼が見ている間は参加者は動いてはいけません。参加者は「だるまさんがころんだ」と言っている間に
ちょっとずつ鬼に近づいて鬼にタッチできたら勝ちというゲームです。

ただ、人通りが多い MFT の会場内で特定の人の動きを検知するのは至難の業です。
なので、今回は「赤、青、緑の大きなかたまり」が動いたことを検知することにしました。
MFT 当日はブースの位置や大きさなどを加味し、青と緑 2 色の大きな風船(直径 90㎝)を参加者に持ってもらい、風船が動いたことを検知するデモンストレーションにしました。

また、「近づいてタッチ」を THETA で検知するのはむずかしいので
かわりに「だるまさんがころんだ → 一定時間動き検知」を 3 回繰り返して、どの色もうごかなかったら
鬼の負け(参加者の勝ち)というルールにしました。

もう一つ普通のだるまさんがころんだと違う点は、鬼が 360 度カメラである点です。
すなわち、THETA を中心にして 360 度のどこにいても検知することができます。
僕はプラグインを利用することで、THETA を 360 度カメラから 360 度画像認識装置に進化させられると
思っていて、このだるまさんがころんだプラグインはその第一弾です。

話を戻して、だるまさんがころんだプラグインの動作を整理すると以下のような流れとなります。

  1. だるまさんがころんだプラグインを起動
  2. シャッターボタンを押下
  3. 「だるまさんがころんだ」という音声ファイルがながれる(音声ファイルは 4 種類の中からランダムに起動)
  4. 一定期間、赤、青、緑のかたまりの動きを検知
  5. 動きを検知したら「**色が動いた」という音声ファイルを流して終了 動きがない場合は 3 に戻る。
  6. 3 から 5 を 3 回繰り返し、どの色も動かない場合は「みんなのかち」という音声ファイルを流して終了。

上記に加えて、THETA が見ている特定色のフィルター結果画像を Vysor を通して見る仕組みも実装しています。
Wi-Fi ボタンをおすことで、
順に緑フィルター結果、青フィルター結果、赤フィルター結果、フィルターなしの画像をみることができます。
これは色抽出のしきい値をきめるところで利用します。詳細は後述します。

全体構成

処理は大きく 2 つの部分にわかれており、それぞれ別スレッドで動作します。
一つは OpenCV を使った画像処理部分です。
カメラが撮影したフレームごとに色抽出し、重心位置の計算を行います。

もう一つは鬼の処理です。
鬼は上記の重心位置を必要なタイミングで取り出して、動き(位置の変化量)を計算します。

この 2 つのスレッドで各色の重心位置の情報を共有します。

画像処理

まずは OpenCV による画像処理部分を説明します。
ここは以下の記事をベースに作っています。
THETA の中で OpenCV を動かす【プレビューフレーム応用編】

コードはGithubから
もってきました。これをベースにすれば OpenCV がすぐ動きます。

ここの処理は MainActivity の onCameraFrame 内で処理しています。
コードはこちらになります。

大まかな流れは以下になります。

  • 画像処理 1. RGB 情報のフレームを取得
  • 画像処理 2. フレームをリサイズ
  • 画像処理 3. RGB から HSV に変換
  • 画像処理 4. 各色を抽出
  • 画像処理 5. 抽出した対象物の中から最大のものの重心のポジションを計算

画像処理のために OpenCV のImgproc クラスのメソッドを使います。
Imagproc クラスのメソッドでフレームに相当する Mat オブジェクトを順に変換していく、という流れになります。

画像処理 1. RGB 情報のフレームを取得

対応するのは以下の部分です。
この Mat オブジェクト frameOrg が一つのフレームの情報に対応しています。
この時点では色情報は RGB で持っています。

// RGBのフレーム
Mat frameOrg = inputFrame.rgba();

以下で、この frameOrg に対して処理を施して
新しい Mat オブジェクトを取得する、という流れになっています。

画像処理 2. フレームをリサイズ

それなりの大きさの色の塊を抽出するのに大きな画像である必要はないので
リサイズして小さくします。
これで必要なメモリ量や CPU パワーを軽減させることが狙いです。

対応するコードは以下の部分です。

//リサイズ
Mat frameMini = frameOrg.clone();
Imgproc.resize(frameOrg, frameMini, new Size(160, 80), 0, 0, Imgproc.INTER_LINEAR);

Imgproc.resize メソッドを使います。
リサイズされた結果として frameMini オブジェクトができます。

画像処理 3. RGB から HSV に変換

色情報を RGB から HSV に変換します。
HSV は色を色相(Hue)、彩度(Saturation)、明度(Value)の 3 値で表現するカラーモデルです。
Imgproc.cvtColor メソッドを使います。
第三引数の指定で変換方法をコントロールできます。frameMini オブジェクトが HSV 情報を持ったオブジェクトとして上書きされます。

 // HSVに変換
 Imgproc.cvtColor(frameMini.clone(), frameMini, Imgproc.COLOR_RGB2HSV);

画像処理 4. 色を抽出

OnCameraFrame メソッドでは以下に対応します。

// 色を抽出
final Scalar GreenMin = new Scalar(50,50,10);
final Scalar GreenMax = new Scalar(90,255,255);
final Scalar BlueMin = new Scalar(100, 100, 10);
final Scalar BlueMax = new Scalar(130, 255, 255);
final Scalar RedMin = new  Scalar(0, 100, 30);
final Scalar RedMax = new Scalar(5, 255, 255);

final Mat frameGreen = getColorFrame(frameMini, GreenMin, GreenMax);
final Mat frameBlue = getColorFrame(frameMini, BlueMin, BlueMax);
final Mat frameRed = getColorFrame(frameMini, RedMin,RedMax);

Scalar オブジェクトが色の範囲を決めています。
例えば緑の範囲を以下のように決めています。

final Scalar GreenMin = new Scalar(50,50,10);
final Scalar GreenMax = new Scalar(90,255,255);

Scalar の引数が順に HSV の値です。
すなわち色相、彩度、明度が 50、50、10 から 90、255、255 の間に含まれるものを緑と定義しています。
この値が色抽出のインプットになります。
この数値の決め方は後述します。

getColorFrame メソッドで色の抽出とノイズ除去を行っています。

  private Mat getColorFrame(Mat frame, Scalar min, Scalar max) {
        Mat frameColor = frame.clone();

        // color抽出
        Core.inRange(frameColor.clone(), min, max, frameColor);
        Imgproc.threshold(frameColor.clone(), frameColor, 100, 255, THRESH_BINARY);

        //noize除去
        Imgproc.morphologyEx(frameColor.clone(), frameColor, Imgproc.MORPH_OPEN, mStructuringElement);

        return frameColor;
    }

Core.inRange で色の抽出をおこなっています。第 4 引数が変換後の画像です。
このあとはImgproc.thresholdで 2 値化したのち、Imgproc.morphologyでモルフォロジー変換によりノイズを除去します。
モルフォロジー変換についてはこちらのサイトを参照ください。

画像処理 5. 抽出した対象物の中から最大のものの重心のポジションを計算

この処理は getMaxContourPosition メソッドで行っています。

 // 最大の輪郭の中心位置を取得
        int[] positionGreen = getMaxContourPosition(frameGreen);

getMaxContourPosition は以下のとおりです。
Imgproc.findContoursメソッドで
画像の輪郭を抽出します。

今回、一番困ったのはここです。
さきほど、ノイズ除去をおこないましたが、例えば、赤い服を着た人が近くを通ったら、それは
物体として検出してしまいます。

findContors の結果にはすべての物体の輪郭が含まれるため、
結果に複数の輪郭が含まれる場合、どうやって風船のものだけを抽出するのかというところが問題です。

結論としては「輪郭の面積が最大値のものを抽出する」という方法を採用しました。

  • THETA は超広角レンズのため、距離が離れると急激に被写体の画像が小さくなる。
  • MFT の会場では風船を持った人が THETA から、それほど離れることはできない。
  • 今回の風船はかなり大きい(直径 90cm くらい)
  • 風船より前方に、同じ色の物体が入る可能性は低い
  • 風船より後方で、直径 90cm よりも更に大きい物体が入る可能性は低い

つまり、かなり状況依存な抽出法になっています。

 private int[] getMaxContourPosition(Mat frame) {
        List<MatOfPoint> contours = new ArrayList<>();
        Imgproc.findContours(frame, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

        double maxArea = 0;
        MatOfPoint maxContour = new MatOfPoint();
        for (MatOfPoint contour : contours) {
            double area = Imgproc.contourArea(contour);
            if (maxArea < area) {
                maxArea = area;
                maxContour = contour;
            }
        }

        Moments p = Imgproc.moments(maxContour);
        int[] position = new int[2];

        position[0] = (int) (p.get_m10() / p.get_m00());
        position[1] = (int) (p.get_m01() / p.get_m00());

        return position;
    }

こうやって計算した値をメンバ変数である positionNowGreen、positionNowBlue、positionNowRed にいれます。

synchronized (lockGreen) {
    positionNowGreen = positionGreen;
}

positonNow は別スレッドの Oni クラスで読み込むために syncronized を使っています。

色範囲の決め方

色抽出処理のところで決めた色範囲の決め方について説明します。

final Scalar GreenMin = new Scalar(50,50,10);
final Scalar GreenMax = new Scalar(90,255,255);

この値の決め方について。今回は以下のようにしました。
こういうサイトなどで
まずは RGB と HSV 変換サイトで、HSV の値の当たりをつけます。
あとは、対象の風船を THETA の近くにおいて、
その領域をうまく区切れているか、他の物体ははいっていないかというのを
画像を見ながら、ひたすら試行錯誤します。

画面を見るためには Vysor を使います。
下の画面は緑を抽出した画面です。THETA の前においている緑のボールを検知していることが
わかります。あと、ディスプレイの下にちょこっと写っている緑の風船も検知しています。

nofilter.png midori.jpg

この画面をみながら、Scalar の引数の HSV 値を最適化していきます。
ちなみに、Wi-Fi ボタンを押すことで、緑、青、赤、フィルターなしの画像が表示されます。
そのように切り替える仕組みは OnCameraFrame メソッドの最後にあります

// Vysorを使って、色抽出した画面をチェックする
// Wi-Fiボタンで表示切り替え
switch (showFrameNumber % 4) {
    case 0:
        Imgproc.resize(frameGreen.clone(), frameOrg, new Size(640, 320), 0, 0, Imgproc.INTER_LINEAR);
        break;
    case 1:
        Imgproc.resize(frameBlue.clone(), frameOrg, new Size(640, 320), 0, 0, Imgproc.INTER_LINEAR);
        break;
    case 2:
        Imgproc.resize(frameRed.clone(), frameOrg, new Size(640, 320), 0, 0, Imgproc.INTER_LINEAR);
        break;
    case 3:
        //do nothing. return original frame
}

return frameOrg;

Wi-Fi ボタンを押すと showFrameNumber の値がインクリメントされます。
その数に対応した色の Frame サイズをもとに戻して return しています。

なお、Wi-Fi ボタンの押下の処理は OnCreate の OnKeyDown の中で行っています。

setKeyCallback(new KeyCallback() {
@Override
public void onKeyDown(int keyCode, KeyEvent keyEvent) {
    if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
        if (!oniAlive) {
            Oni oni = new Oni();
            oni.start();
        }
    }
    if (keyCode == KeyReceiver.KEYCODE_WLAN_ON_OFF) {
        showFrameNumber += 1;
    }
}

ちなみに、同じ onKeyDown の中でシャッターボタンを押すと Oni クラスの処理が走るように設定されています。

鬼クラス

Oni クラスがだるまさんがころんだの処理のメインです。
ここは以下の処理をしているだけなのでソースを見てもらえればわかると思います。

  1. シャッターボタンが押されたら処理をスタート
  2. 4 種類の「だるまさんがころんだ」音声ファイルからランダムに一つを選択して、流す。
  3. 音声ファイル終了直後の各色の位置を記録
  4. 動きを検知するフレームの枚数を 6 から 16 の幅でランダムに決定
  5. 500msec まって、そのタイミングの位置を検知
  6. 4 と 5 の距離を算出して、しきい値と比較 7-1. しきい値を超えていれば「**色がうごいた」音声ファイルを流して終了 7-2. しきい値を超えていなければ 2-6 を 3 回繰り返す
  7. 3 回繰り返して、どの色の動きも検知しなかった場合、「みんなの勝ち」音声ファイルをながして終了

Oni クラスの全体はこちらです

2 つだけポイントを説明します。

まず、1 つ目のポイント。
シャッターが連続で押された場合、2 回目以降を無視するための仕組みとして
run メソッドの最初に oniAlive というフラグを設定しています。
これは run メソッドが起動中であることを別のスレッドに伝えるためです。

oniAlive = true;

oniAlive をチェックしているのはシャッターボタンがおされたタイミングです。
oniAlive が false のときのみ run メソッドが起動されます。

public void onKeyDown(int keyCode, KeyEvent keyEvent) {
    if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
        if (!oniAlive) {
            Oni oni = new Oni();
            oni.start();
        }
    }

もう一つのポイントは positionNowGreen、positionNowBlue、positionNowRed の読み込みです。
OpenCV のスレッドで値が入力されているため、その値を読み込むときには syncronized を使っています。

synchronized (lockGreen) {
    positionInitGreen = positionNowGreen;
}

まとめ

OpenCV の色検知を利用した「だるまさんがころんだ」プラグインの中身を説明しました。

RICOH THETA プラグインパートナープログラムについて

THETA プラグインに興味を持たれた方がいれば、以下の記事もぜひご覧ください。

RICOH THETA プラグイン開発者コミュニティでは、他にも記事を書いています。
RICOH THETA プラグインについてはこちらに情報がまとまっています。興味を持たれた方はtwitterのフォローとTHETA プラグイン開発コミュニティ(slack)への参加もぜひどうぞ。

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

Flutterウィークリー #72

Flutterウィークリーとは?

FlutterファンによるFlutterファンのためのニュースレター
https://flutterweekly.net/

この記事は#72の日本語訳です
https://mailchi.mp/flutterweekly/flutter-weekly-72

※Google翻訳を使って自動翻訳を行っています。翻訳に問題がある箇所を発見しましたら編集リクエストを送っていただければ幸いです。

読み物&チュートリアル

Flutterオンライン調査ソフトウェア

https://google.qualtrics.com/jfe/form/SV_3kiGXYfYOfXUjB3?Source=Twitter&linkId=71914718

Flutterチームが簡単なアンケートに回答して製品を改善できるように支援する

60 Days Of Flutter : Flutterからのメッセンジャーの構築

https://medium.com/@adityadroid/60-days-of-flutter-building-a-messenger-from-scratch-ab2c89e1fd0f


Aditya Gurjarは60日間、完全なFlutterアプリを開発し、プロセスを文書化します。最初の記事はすでに作成されています。

FlutterとCloudinaryを使用して写真日記アプリを作成する9つの最も簡単な手順

https://medium.com/flutter-community/9-easiest-steps-to-build-photo-diary-app-using-flutter-and-cloudinary-8f190faa1441


Promise Nzubechi Amadiによるこの記事に従って、写真ダイアリーアプリを段階的に作成してください。

Flutter CLI:52の非表示サンプル

https://medium.com/flutter-community/flutter-cli-52-hidden-samples-fc90f218a9d0


Nitish Kumar Singhは、 Flutter CLIツールで52の非表示のサンプルプロジェクトを示しています。

Flutterウィジェットのbuild()メソッドが「すべてがウィジェット」であることを証明する方法

https://medium.com/fluttervn/how-build-method-of-flutter-widget-proves-everythings-a-widget-a49b5efc5cbb


Bui Minh Trietは、Containerウィジェットの構築方法の分析を使用して、テーブルの下でウィジェットがどのように機能するかを学習します。

Flutter依存性注入

https://medium.com/flutter-community/dependency-injection-in-flutter-f19fb66a0740


Flutterアプリに依存性注入を実装する方法に関するZubair Rehmanによる優れた記事

思考のFlutter

https://medium.com/flutter-community/thinking-flutter-22f38961f9ba


Pratik Jainは、いくつかの例を使用して新しいウィジェットの設計に取り組む前に、どのように考えるかを分析します。

ビデオ&メディア

カスタムドロワー、ファイリングバグ、データベースの操作(The Boring Flutter Development Show、Ep。27)

https://www.youtube.com/watch?v=SJKrtx759Xk&feature=youtu.be


ボーリングショーのこのエピソードでは、エミリーとエミリーが協力してデータベースをFlutterアプリに追加し、ユーザーがお気に入りの記事を保存したり、パッケージにバグを登録したり、カスタムドロワーメニューの作成を開始できるようにします。

フレアベクトルアニメーション:Zero to Hero

https://www.youtube.com/playlist?list=PLjr4ufdmNA4Jt19RyRQPD7cMF0r0juDs8


Flareを使用してアニメーションを作成する方法を学ぶためのビデオチュートリアルの全リスト

Flutterアニメーション-AnimatedSwitcherを使用した放射状メニュー

https://www.youtube.com/watch?v=1Bz06bLeWHY&feature=youtu.be


Flutter放射状メニューを作成する方法を学ぶ

Flutterカスタムアイコン-自動および手動の方法(アイコンフォントまたはSVG)

https://www.youtube.com/watch?v=qZYqmM3daO0&feature=youtu.be


カスタムアイコンを使用してFlutterにベクターフォントベースのアイコンの新しいセットを追加する方法に関するビデオチュートリアル。

今週のパッケージ

https://www.youtube.com/playlist?list=PL163X1AUpNTGyw4eNVnvIfPl3chAXsReW


「週のパッケージ」を示す毎週成長するプレイリスト、またはさまざまな利用可能なFlutterパッケージの使用方法。

抽象化により開発時間を節約| Flutter抽象化

https://www.youtube.com/watch?v=n2yGl7vJJGM&feature=youtu.be


この抽象化に関するFlutterチュートリアルでは、抽象化が必要な理由、抽象化がどのように役立つか、偽の実装を使用して実際に何時間もの開発時間を節約できるかがわかります。

ライブラリ&コード

aliyigitbireroglu / flutter-peek-and-pop

https://github.com/aliyigitbireroglu/flutter-peek-and-pop

同じ名前のiOS機能に基づくFlutter Peek&Pop実装。

Almoullim / background_location

https://github.com/Almoullim/background_location

AndroidおよびiOS用のFlutterバックグラウンドロケーションプラグイン

form_bloc / packages / flutter_form_bloc

https://github.com/GiancarloCode/form_bloc/tree/master/packages/flutter_form_bloc

Flutter form_blocパッケージでフォームを作成するのに役立ちますフラッターウィジェットを持つパッケージ。

mannprerak2 / nearby_connections

https://github.com/mannprerak2/nearby_connections

近くの接続API用のFlutterプラグイン(Android)

schibsted / tweet_ui

https://github.com/schibsted/tweet_ui

Tweet UIのFlutterパッケージ

fluttercommunity / get_it

https://github.com/fluttercommunity/get_it

インターフェースを具体的な実装から切り離すことができる単純な直接サービスロケーター。

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

Flutterウィークリー #71

Flutterウィークリーとは?

FlutterファンによるFlutterファンのためのニュースレター
https://flutterweekly.net/

この記事は#71の日本語訳です
https://mailchi.mp/flutterweekly/flutter-weekly-71

※Google翻訳を使って自動翻訳を行っています。翻訳に問題がある箇所を発見しましたら編集リクエストを送っていただければ幸いです。

読み物&チュートリアル

Flutter and Graphql-ファイルをアップロードする方法

https://codinglatte.com/posts/flutter/flutter-and-graphql-how-to-upload-files/


GraphQLとFlutterを使用してファイルをアップロードする方法に関するチュートリアル。

Flutterチュートリアル:習慣トラッカーUI

https://cybdom.tech/flutter-tutorial-habit-tracker-ui/


カスタムペインタとベジェカーブを含むUIの作成に関するチュートリアル。

Flutterサノススナップ効果

https://fidev.io/thanos-snap-effect-in-flutter/


MarcinSzałekが説明した画像に適用する楽しい効果。効果の最高の名前:)

Flutter非同期プログラミングのガイド

https://medium.com/@gaspard.merten/a-simple-guide-about-flutter-dart-and-asynchronous-programming-a96d9930136c


Gaspard MertenによるFlutter先物、ストリーム、コンプリーターの紹介。

Flutter Androidの文字Flutter

https://medium.com/flutter-community/android-strings-to-flutter-12a5c307147c


ŁukaszWiśniewskiは、作成する理由と、ライブラリを使用してAndroid xml文字列ファイルをi18n Flutterクラスに変換する方法について説明します。

FlutterとGraphQLを使用したシンプルなアプリケーションの構築

https://medium.com/flutter-community/building-a-simple-application-with-flutter-and-graphql-5786764df102


DanielBerríoBarreraがFlutterでのGraphQLの使用について紹介します。

Flutter —プロバイダーを使用したFirebase Realtime Database CRUD操作

https://medium.com/flutter-community/flutter-firebase-realtime-database-crud-operations-using-provider-c242a01f6a10


Mohamed Hassan Kadriによるプロバイダーライブラリを使用して、Firebaseでcrud操作を処理する方法。

BLoCパターン付きのFlutter BottomNavigationBar

https://medium.com/@lovnicki.sandro/flutter-bottomnavigationbar-with-bloc-pattern-bba6f13d49f3


SandroLovničkiは、BLoCデザインパターンでBottomNavigationBarウィジェットを使用する方法を示します。

バックグラウンドでFlutterを実行するには?

https://medium.com/vrt-digital-studio/flutter-workmanager-81e0cfbd6f6e


Tim Rijckaertは、 Flutterバックグラウンドジョブを定期的にトリガーする必要性をどのように解決したかについて説明します。

Flutterための必須のVSコード拡張機能

https://medium.com/flutter-community/must-have-vs-code-extensions-for-working-with-flutter-e31a421b9c68


Daniele CambiによるFlutter動作するために推奨されるVSコード拡張のキュレーションリスト。

FlutterアプリでColorTweenを使用する

https://medium.com/flutterpub/using-colortween-in-flutter-app-d05a3ebe83f5


Tyagi Priyankaによるこの記事で、ColorTweenを使用してアニメーションを実装する方法を学びます。

Flutter PCゲームをモバイルに

https://rainway.com/blog/2019/08/06/flutter/


Rainwayチームは、 Flutterを使用してモバイルにゲームをストリーミングする方法を説明し、驚くべき結果を出しました。

ビデオ&メディア

CustomPaintを使用したFlutterアニメーションノイズ

https://www.youtube.com/watch?v=-3bRoZ2kbW8&feature=youtu.be


CustomPaintウィジェットを使用してFlutterノイズアニメーションを作成する方法を学びます。

AnimatedPadding(今週のFlutterウィジェット)

https://www.youtube.com/watch?v=PY2m0fhGNz4&list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&index=46


AnimatedPaddingを使用すると、ウィジェットのパディングを動的に変更し、2つの値の間でアニメーション化できます。

より良いコードのためのDart機能:スプレッド、Collection-If、Collection-For

https://www.youtube.com/watch?v=mnaN_6465Gk&feature=youtu.be


このチュートリアルでは、Spreads、Collection-If、およびCollection-Forを実際に使用する方法を示します。

Flutter Animation Tutorial – AnimatedWidgetとAnimatedBuilderによるリファクタリング

https://www.youtube.com/watch?v=o-h_e4b71o8&feature=youtu.be&app=desktop


AnimatedWidgetとAnimatedBuilderを手動アニメーションに適用して、コードをよりクリーンで再利用しやすくします。

ライブラリ&コード

アリアミン/ shopx

https://github.com/Ali-Amin/shopx

MobX状態管理を使用したEコマースFlutterアプリ

asjqkkkk / flutter-todos

https://github.com/asjqkkkk/flutter-todos

包括的で美しいFlutter Todo-Listアプリは、ベストプラクティスプロジェクトでもあります。印象的なドキュメントの栄誉。

dmh2000 / Flutter -GraphqlX

https://github.com/dmh2000/Flutter-GraphqlX

DartとFlutter GraphQLを使用する例

fayeed / flutter_parsed_text:

https://github.com/fayeed/flutter_parsed_text

テキストを解析し、リンクされたテキストウィジェットにするFlutterパッケージ

flutter-ui-challenges / flutter_web_challenge_googlemaps

https://github.com/flutter-ui-challenges/flutter_web_challenge_googlemaps

Google Maps UIの再設計のFlutterチャレンジ。

fluttervn / super_widgets

https://github.com/fluttervn/super_widgets

一般的に一緒に使用される複数のウィジェットを単一のウィジェットに結合する一連のウィジェット。

JideGuru / Flutter FurnitureAppUI:

https://github.com/JideGuru/FlutterFurnitureAppUI


家具アプリUIのFlutter表現

khalidfsh / flutter_localizations_tale

https://github.com/khalidfsh/flutter_localizations_tale


FlutterのI18N機能を示すアプリケーション。

sarbagyastha / flutter_rating_bar

https://github.com/sarbagyastha/flutter_rating_bar

フラッター用のシンプルなレーティングバー。レーティングバーインジケーターも含まれ、レーティングの一部をサポートします。

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

Flutterウィークリー #70

Flutterウィークリーとは?

FlutterファンによるFlutterファンのためのニュースレター
https://flutterweekly.net/

この記事は#70の日本語訳です
https://mailchi.mp/flutterweekly/flutter-weekly-70

※Google翻訳を使って自動翻訳を行っています。翻訳に問題がある箇所を発見しましたら編集リクエストを送っていただければ幸いです。

読み物&チュートリアル

Flutter –ポートレートとランドスケープのレイアウト

https://codingwithjoe.com/flutter-portrait-and-landscape-layouts/


縦向きでも横向きでも、美しく表示できるFlutterフォームの作成方法を調べてください。

「刷新」 Flutterボトムシート

https://medium.com/flutter-community/revamped-flutter-bottom-sheet-61662dc2983


Sumeet Rukejaは、独自のボトムシートをゼロから作成して、さらにカスタマイズできるようにする方法を説明します。

Flutterはよさそうですが、痛いです。ここに私の不満があります。

https://medium.com/@bernaferrari/from-android-dev-flutter-looks-good-but-is-painful-here-are-my-frustrations-with-it-81b4bbe739f8


ベルナルド・フェラーリはFlutterを試し、その過程で発見したいくつかの問題を共有しています。

プロバイダーパッケージの詳細

https://medium.com/flutter-nyc/a-closer-look-at-the-provider-package-993922d3a5a5


プロバイダーパッケージをよりよく理解するためのMartin Rybakの記事

Flare& Flutterした水追跡アプリの構築

https://medium.com/2dimensions/building-a-water-tracking-app-with-flare-flutter-f03de436dba3


FlutterアプリからFlareアニメーションを処理する方法を理解するための優れたチュートリアル。

Flutter —デバッグUIチートシート

https://medium.com/@louagejulien/flutter-debugging-ui-cheat-sheet-18a7b09dd468


Julien LouageによってキュレーションされたUIのデバッグフラグのリスト

Flutterとムーア

https://medium.com/flutterpub/flutter-and-moor-c2b2e03b1fbe


Aseem Wangooによるこの記事では、Moorを使用してFlutterデータベースの操作を簡素化する方法を学びます。

Flutterとコマンドライン—ラブストーリー

https://medium.com/flutter-community/flutter-and-the-command-line-a-love-story-a3648ef2411


GonçaloPalmaは、コマンドラインオプションをレビューしてFlutterます。

Flutter Rx Dartを使用したインスタント画像検索

https://medium.com/@sagarsuri56/instant-image-search-using-rxdart-in-flutter-9eedd4b53364


Rx Dartを使用して画像検索アプリを作成する方法を説明するSagar Suri。

Dialog ManagerでFlutterダイアログを管理する

https://medium.com/flutter-community/manager-your-flutter-dialogs-with-a-dialog-manager-1e862529523a


Dane Mackierは、中央のダイアログマネージャーを使用してアプリでダイアログを処理する方法を示します。

プロバイダー—およびそのタイプ

https://medium.com/flutterpub/provider-and-its-types-dda8463586e7


Aseem Wangooによるプロバイダーパッケージの紹介

Flutterを使用したテキスト読み上げ

https://medium.com/@arend.kuehle/text-to-speech-with-flutter-8d959dad40a8


FlutterでTTSを使用する際のArendKühleによるヒント。

レンタル代理店向けのFlutterデータ入力フォームアプリの作成

https://medium.com/flutter-community/writing-a-flutter-data-entry-form-app-for-a-rental-agency-e5a7dab20596


Steve Zwartによるこの記事でFlutterフォームの取り扱いを学ぶ

カールとDart楽しみ-マーキーデセルス:クリスの洞察力

http://sellsbrothers.com/fun-with-curl-and-dart


Chris Sellは、Curlでリクエストを生成し、それらをDartコードに変換する方法を説明します。

Flutter次のアプリを作成する理由

https://skillsmatter.com/skillscasts/13223-why-i-m-building-my-next-app-in-flutter


アンジェラ・ユーによるFlutterの紹介

ビデオ&メディア

Flutterフォーム検証を追加し、Firebase Authエラーメッセージのユーザーに警告する方法

https://www.youtube.com/watch?v=bSUdYUw4Jjs


サインアップとサインインフォームにカスタムフォーム検証を追加し、エラーメッセージを処理する方法

AnimatedPositioned( Flutter Widget of the Week)

https://www.youtube.com/watch?v=hC3s2YdtWt8&list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&index=45


AnimatedPositionedは、指定された位置が変更されるたびに、指定された期間にわたって子の位置を自動的に移行します

Flutterグリッターアニメーションを作成する(The Boring Flutter Development Show、Ep。27)

https://www.youtube.com/watch?v=OTwIfUFZmVw&list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl&index=36&t=12s&linkId=71420748


この退屈なショーのエピソードでは、アンドリューはゲストメアリーと一緒に、 Flutterキラキラと輝くアニメーションを作成するというアイデアを探求します。

Flutterアニメーションチュートリアル-基本を理解し、簡単にアニメーション化する

https://www.youtube.com/watch?v=txLvvlooT20&feature=youtu.be


カスタムアニメーションの作成方法を学ぶ

Flutter Bloc-自動ルックアップ-v0.20(以降)、更新されたチュートリアル

https://www.youtube.com/watch?v=_vOpPuVfmiU&feature=youtu.be


このチュートリアルでは、BLoCライブラリを使用して状態を管理する最新のベストプラクティスと方法を学習します。

Flutter File Upload-画像を選択、切り取り、クラウドに保存

https://www.youtube.com/watch?v=HCmAwk2fnZc&feature=youtu.be


FlutterとFirebase Cloud Storageでファイルをアップロードしてください。画像を選択、切り取り、クラウドストレージバケットにアップロードする方法を学ぶ

Flutter Live: Flutter Blocパッケージの学習|私と一緒に学びましょう!

https://www.youtube.com/watch?v=eAiCPl3yk9A&feature=youtu.be


BLoCの使用方法を示すロバートブランヘイジのキャスト

Flutterミニマルデザイン-栄養アプリ

https://www.youtube.com/watch?v=K1uH_SN4X0w


別のデザインチャレンジ、今回は栄養アプリ

エリック・ザイデル( Flutter共同設立者)へのインタビュー

https://www.youtube.com/watch?time_continue=287&v=AMOs1cuNhDY


フルテランドのメンバーがエリック・ザイデルにインタビューします(ポルトガル語での紹介ですが、インタビューは英語です)

Material Themer- Flutter開発ツールの情報ビデオ

https://www.youtube.com/watch?v=o_xs5sXEaqg&feature=youtu.be


Material Themerアプリを使用すると、ThemeDataを視覚的に作成し、ワンクリックでコードを生成できます

ライブラリ&コード

Flutter Enhancement Suite

https://plugins.jetbrains.com/plugin/12693-flutter-enhancement-suite


Flutter作業をこれまで以上に簡単にするための重要なプラグイン!

AubergineDevelopers / flutter_scale

https://github.com/AubergineDevelopers/flutter_scale

Flutterパッケージを使用すると、Scaleウィジェットを簡単に統合できます。

csells / flutter_mazegen

https://github.com/csells/flutter_mazegen

Flutter迷路を生成するために古いC ++を移動する

florent37 / Flutter -ShapeOfView

https://github.com/florent37/Flutter-ShapeOfView

フラッターウィジェットにカスタムシェイプを追加し、Material Design 2を準備

mjohnsullivan / ffi

https://github.com/mjohnsullivan/ffi

DartからCライブラリを呼び出す方法を示す一連の簡単な例。

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

Androidアプリ配信に関してまとめ(TWAアプリ)

Androidアプリリリースに必要な作業

署名済APKファイルの作成

1. リリース用の設定を行う

こちらを参考にリリースに必要な準備を行う
- ログ記録とデバッグをオフにする
- プロジェクト ディレクトリをクリーンアップする
- マニフェストおよび Gradle ビルド設定を確認およびアップデート
など

2. アプリの署名鍵とアップロード鍵の生成を行う

AndroidStudioで鍵とキーストアの生成を行う

鍵の管理に関してはApp Signingがよい

3. サーバーに配置する関連付け用jsonを生成する

以下のコマンドでSHA-256フィンガープリントを抽出

keytool -list -v -keystore  エイリアス  -storepass  キーストアのパス 

実効例サンプル

$keytool -list -v -keystore ./mykeystore.ks -alias test -storepass password -keypass password

Alias name: key0
Creation date: 28 Jan 2019
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Test Test, OU=Test, O=Test, L=London, ST=London, C=GB
Issuer: CN=Test Test, OU=Test, O=Test, L=London, ST=London, C=GB
Serial number: ea67d3d
Valid from: Mon Jan 28 14:58:00 GMT 2019 until: Fri Jan 22 14:58:00 GMT 2044
Certificate fingerprints:
   SHA1: 38:03:D6:95:91:7C:9C:EE:4A:A0:58:43:A7:43:A5:D2:76:52:EF:9B
   SHA256: F5:08:9F:8A:D4:C8:4A:15:6D:0A:B1:3F:61:96:BE:C7:87:8C:DE:05:59:92:B2:A3:2D:05:05:A5:62:A5:2F:34
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3

表示された情報をもとにこちらでステートメントを生成する

実効例サンプル

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target" : { "namespace": "android_app", "package_name": "com.example",
               "sha256_cert_fingerprints": ["aaaa"] }
}]

作成されたステートメントをwebアプリケーション側の /.well-known/assetlinks.json に配置する

4. アプリの署名を行う

App Signing方式でアップロード鍵を使用してリリース用としてアプリに署名をする

Google Playで配信

最初にGoogle Play Consoleでデベロッパーズアカウントを作成する必要があります

※ アカウント作成には$25必要です

※ ストアに掲載するにあたってスクショやヘッダー画像が必要になるため準備が必要です
ストア配信にあたって必要素材や情報

登録後の手順についてはこちらの記事がわかりやすい
Google Play での Android アプリの配信方法(インストールとアップデート)を試してみる - Qiita

公式手順

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