HTMLファイルを表示する

2024年7月15日

開発環境
OS:Windows 11
SDK:VS Code + Flutter 3.7.12

概要

アプリ内のローカルストレージに格納したhtmlファイルを画面に表示します。

WebViewウィジェットを準備する

HTMLファイルはWebViewウィジェットを通して表示します。

そのため、WebViewパッケージをインストールする必要があります。

サンプルプロジェクトを生成して動作確認してみます。

webview_flutterをインストールします。

HTMLファイルを準備する

次にHTMLファイルを準備します。

<!DOCTYPE html>
<html lang="ja">

  <head>
    <meta charset="UTF-8">
    <title>Top Page</title>
  </head>

  <script>
    function sendData1() {
      // Flutterへデータ送信
      sample.postMessage("1");
    }
    function sendData2() {
      // Flutterへデータ送信
      sample.postMessage("2");
    }
  </script>

  <body>
    <h1>Please tap a button.</h1>
    <br>
    <br>
    <button onclick="sendData1()">Send Data 1</button>
    <br>
    <br>
    <button onclick="sendData2()">Send Data 2</button>
  </body>
</html>

ボタンが2つあるHTMLファイル(top.html)を作成しました。

プロジェクトフォルダ内に「html」フォルダを作成し、その中に作成したhtmlファイルを格納します。

次にpubspec.yamlのassets項目にhtmlファイルを定義します。

  assets:
    - html/top.html

WebViewウィジェット実装

main.dartにWebViewウィジェットを追加します。

ネットワーク上のurlにアクセスして表示するわけではないため、htmlのロード処理もあわせて追加する必要があります。

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late WebViewController controller;
  late Future<void> controllerInitialization;

  @override
  void initState() {
    super.initState();
    controllerInitialization = initController();
  }

  Future<void> initController() async {
    final html = await rootBundle.loadString('html/top.html');
    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel('sample', onMessageReceived: (result) async {
          switch (result.message) {
            case "1":
              print("call 1.");
              break;
            case "2":
              print("call 2.");
              break;
          }
        })
      ..loadRequest(
        Uri.dataFromString(
          html,
          mimeType: "text/html",
          encoding: Encoding.getByName("utf-8"),
        ),
      );
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<void>(
      future: controllerInitialization,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return Scaffold(
            appBar: AppBar(title: const Text("Test Page")),
            body: WebViewWidget(controller: controller),
          );
        } else {
          return const CircularProgressIndicator();
        }
      },
    );
  }
}

※Android向けのビルドが失敗した場合はこちら↓をチェックしてみてください。

動作確認

デバッグモードで起動しました。

HTMLのテキストと2つのボタンは期待通りに表示されています。

次にボタンを押し分けてみると・・・

ターミナルに「call 1.」、「call 2.」が表示されました。

ボタン操作も処理できることが確認できました。

今回は以上です。

ビルドエラー解消(JAVA_HOME)

2024年7月15日

開発環境
OS:Windows 11
SDK:VS Code + Flutter 3.7.12

概要

久しぶりにFlutterの動作確認をしようとサンプルプロジェクトを生成し、Android向けにビルドしたら失敗しました。

Android向けビルド時に発生したビルドエラーの解消方法についての話。

Flutter ビルドエラーの発生

サンプルプロジェクトを生成。

AndroidスマホをPCにUSB接続し、デバイスをチェック。

スマホのデバイスIDを取得し、デバイスIDを指定してデバッグ起動。

たったこれだけの手順でビルドエラーが発生してしまいました。

以前はビルドできたはずなんですが。。。

エラー内容は、以下。

環境変数「JAVA_HOME」に指定したディレクトリが存在しないってことのようです。

ということでディレクトリを見てみると・・・

ディレクトリ「jre」の直下はディレクトリ「bin」のみ。

そのディレクトリ「bin」の直下も.makerファイルのみと見事にもぬけの殻になっていました。

そういえば、最近、Android StudioのバージョンをFlamingoからIguanaにバージョンアップしたんだったっけ。。。

環境変数を編集する

ということでAndroid Studioの実体を探してみると・・・

環境変数を編集します。

Flutterビルドエラーの解消

ターミナルを立ち上げなおして再度、ビルドしてみます。

ビルドが成功し、スマホ上でサンプルアプリが起動しました。

今回は以上です。

com.google.android.play.coreの警告に対処する

2024年7月15日

開発環境
OS:Windows 11
SDK:Android Studio Iguana | 2023.2.1 Patch 1

概要

Google Play Consoleの受信トレイにcom.google.android.playに関する警告がきており、8月31日までに対処が必要とのことで対応しました。

警告を確認する

Google Play Consoleの受信トレイにcom.google.android.playに関する警告が届きました。

警告の内容を確認すると、

com.google.android.play.core(1.8.0)がAndroid 14(sdk 34)に対応できなくてアプリがクラッシュするため、それを回避するために8月31日までにライブラリの参照を変更しなさい。

とのことのようです。

com.google.android.play.coreって何に使ってたんだろうか?

com.google.android.play.coreの使用状況を確かめる

まずは警告が届いたアプリのソースコードを確認します。

Android Studioで立ち上げ、「com.google.android.play」で検索してみると・・・

検索の結果、In-App Reviewのモジュールがヒットしました。

import com.google.android.play.core.review.ReviewManagerFactory
import com.google.android.play.core.review.ReviewManager

build.gradleのライブラリを確認すると

今回の警告対象である「com.google.android.play:core-kt」が見つかりました。

 dependencies {

    implementation("com.google.android.play:core-ktx:1.8.1")

}

「com.google.android.play:core-kt」を削除してビルドしなおしてみるとReviewManagerのインポートで参照エラーがでました。

「com.google.android.play:core-kt」の代替ライブラリが必要のようです。

ライブラリを差し替える

改めてIn-App Reviewの実装方法を公式サイトで確認するとライブラリが変わっていました。

 dependencies {

    implementation 'com.google.android.play:review-ktx:2.0.1'

}

ということで「com.google.android.play:core-ktx」を「com.google.android.play:review-ktx」に変更し、ビルドしてみると・・・

これで警告の対応が完了です。

お疲れさまでした。

アプリにIn-App Reviewを組み込む

2024年7月15日

開発環境
OS:Windows 11
SDK:Android Studio Iguana | 2023.2.1 Patch 1

概要

AndroidアプリにIn-App Reviewを組み込みます。

In-App Review

In-App Reviewとはアプリの評価やレビューの入力を促すポップアップ機能です。

アプリの操作中に意図したタイミングでポップアップさせることができます。

アプリの評価やレビューはアプリの改善に役に立つため、In-App Reviewの実装はおススメです。

In-App Review実装の流れ

実装していきます。

まずはbuild.gradleにライブラリを組み込みます。

 dependencies {

    implementation 'com.google.android.play:review-ktx:2.0.1'

}

次に必要なモジュールをインポートします。

import com.google.android.play.core.review.ReviewManagerFactory
import com.google.android.play.core.review.ReviewManager

Activityの起動時にレビューマネージャーを生成します。

    private lateinit var reviewmanager : ReviewManager

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

        reviewmanager = ReviewManagerFactory.create(this)

    }

あとは適当な場所(画面起動時やイベント終了後など)でポップアップ処理をコールします。

    val request = reviewmanager.requestReviewFlow()
    request.addOnCompleteListener { task ->
        if (task.isSuccessful) {
            val reviewInfo = task.result
            val flow = reviewmanager.launchReviewFlow(this@MapsActivity, reviewInfo)
            flow.addOnCompleteListener { _ ->

            }
        }
    }

In-App Reivewの実装は以上です。

デバッグ方法

デバッグをしたい場合、FakeReviewManagerを使用します。

FakeReviewManagerをインポートします。

import com.google.android.play.core.review.ReviewManagerFactory
import com.google.android.play.core.review.ReviewManager
import com.google.android.play.core.review.testing.FakeReviewManager

FakeReviewManagerを生成します。

    private lateinit var reviewmanager : ReviewManager

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

//        reviewmanager = ReviewManagerFactory.create(this)
        reviewmanager = FakeReviewManager(this)

    }

ポップアップ処理は変更不要です。

FakeReviewManagerのデバッグではポップアップは表示されず、正常系のシーケンスのみ確認できます。

ステップ実行してみるとrequestReviewFlowに成功し、launchReviewFlowがコンプリートします。

これでIn-App Reviewの組み込みは終了です。

お疲れさまでした。

引継ぎ機能のお知らせ

執筆:2024年6月23日

新機能のお知らせです。
この度、引継ぎ機能を追加致しました。
機種変更などによって新しいスマートフォンにアプリをインストールし直した際に今までの参拝記録を引継ぐことが可能になりました。
これからも末永くご利用ください。
手順等詳細につきましては下記サイトをご参照ください。
参拝記録引継ぎ手順について

夫婦岩の日の出

執筆:2024年6月8日

金曜日の夜、夜更かしついでに二見浦のコンビニに出かけたところ、夜が明けだしました。せっかくの機会ということで二見興玉神社に向かい、日の出を拝んでまいりました。
神社前の駐車場は予想以上に混んでおり、たくさんの人々が早朝の参拝、散歩を楽しまれておりました。
コロナ明けを感じる今日この頃。
6月、伊勢神宮では月次祭が行われます。

日本酒の旅 黄桜カッパカントリー

2024年6月1日

この世に天国なんてあるのか?

日本酒が堪能できる施設があるよ、という情報をキャッチしたので近鉄電車で京都伏見まで。

桃山御陵前駅で降り、徒歩10分程。

伏見大手筋商店街を通り抜けた先にあるのが黄桜カッパカントリーです。

お昼ご飯と一緒に日本酒を嗜もうと

12時30分頃に到着。

地ビールとお酒の天国。

入り口だけで期待感高し。

入り口左手が黄桜記念館。

黄桜のトレードマークでもある河童を中心とした企業展示を見ることができます。

そして、右手から施設に入ると売店とレストラン。

早速、レストランのレジカウンターに。

幸い、待つことなく席を案内してもらうことができました。

案内してもらったのは
掘りごたつの席。

レストランは広々としており、
リラックスした雰囲気が漂います。

これぞ、黄桜。

さあ、何を頼もうか。

1品料理から定食までメニューも豊富なので悩みます。

そして、選んだのがこちら。

出し巻き和定食。

税込み、1050円

濃厚な酒粕汁に焼き鮭。

この組み合わせは無限にいけてしまいます。

そして、日本酒は。

黄桜9種飲み比べ。

税込み、1950円。

どぶろくから大吟醸まで多彩なセット。

あとは1品料理を適当に頼みつつ日本酒を堪能♪

豊富な食事メニュー。

そして、地ビール、日本酒。

写真が一部、ブレてしまってます。

酔っていたわけではないのですが。

家が近ければ、仕事終わりに来たかった。。。

これは何度来ても楽しめる究極のエンターテイメント。

天国、ここにありました。

お店情報

黄桜カッパカントリー

営業時間:11:00(平日11:30)~14:00、17:00~20:50

定休日:火曜日

住所:京都府京都市伏見区塩屋町228

阿曽温泉

2024年5月26日

以前、瀧原宮を参拝した後、周辺を散策するなかで見つけた阿曽温泉。

気になってはいたもののなかなか行けずにいたんですが、ようやく訪れることができました。

18時30分頃に到着。

日が暮れかかってきました。

施設前の駐車場は県外ナンバー含め、10台ほど停まっていました。

施設に入るとすぐ目の前がカウンター。

カウンターを左に進むと浴場、右に進むと休憩スペースです。

右手奥に食堂があったようですが現在は休業中。

食事も期待していただけに残念です。。。

入浴料金は大人500円。

廃校になった小学校を温泉施設に改築したのが特徴でもある阿曽温泉。

休憩スペースに置かれた学習机の上に入浴チケットを置いて撮影してみた。

廊下は小学校のときのままだろうか。

内湯1、洗い場6にサウナ(男湯のみ)とこぢんまりとした温泉です。

ですが、足を伸ばしてもゆとりのある浴槽に沈み、チョロチョロと流れ足される天然温泉の音を聞き、日暮れの空を眺める。

周りの静けさも相まって最高のリラックス空間でした。

湯加減は熱め、浴槽の淵に湯の花が見られたので効能は期待できそうです。

温泉情報

阿曽温泉

営業時間:10:00~21:00

定休日:水曜日

住所:三重県度会郡大紀町阿曽429

伊勢の昼ご飯(4)台湾料理 福亭

2024年5月24日

リモートワーカーの昼ご飯日記

伊勢の飲食店を巡ります。

近くまで行く用事があったのでお昼ご飯は福亭に決定。

伊勢市内の高校が密集した地域にあるお店、ということで予想通りのそういうお店です。

高校生が多く立ち寄るお店ですが、もちろん社会人にも人気です。

それゆえお店横の駐車場は出入りも多く、停めにくいため、お店向かいの広い駐車場にいつも停めています。

店内はテーブル4卓と座敷3卓、カウンター3席。

ランチ帯はセットメニューがお得!

ランチメニューだけ見ても豊富なラインナップです。

酢豚定食。
税込み、650円。

ご飯のおかわりOK。

いつもお腹一杯になります。

ちなみに以前、天津飯を食べたことがあるのですが、予想外の甘だれでした(笑

人によって好みがあると思うのでその点だけご注意を(^^;

ごちそうさまでした。

お店情報

台湾料理 福亭

営業時間:11時~15時、17時30分~22時

定休日:水曜日

住所:三重県伊勢市神田久志本町1466

ポリシー違反「サイトの仕様:ナビゲーション」を解決した話

2024年4月10日

開発環境
OS:Windows 11
SDK:Android Studio Flamingo | 2022.2.1

概要

ポリシー違反「サイトの仕様:ナビゲーション」がなかなか解決せず、解決まで1ヶ月半かかった話。

最初の警告メールの指摘はアプリのクラッシュ

2月29日、ポリシー違反「サイトの仕様:ナビゲーション」の警告メールが届きました。

指摘を受けたアプリは「WatchOverMe」。

早速、AdMobコンソールのポリシーセンターを確認します。

指摘されたポリシー違反は、「サイトの仕様:ナビゲーション」。

添付されていた画像を確認するとアプリがクラッシュした画面のスクリーンショットでした。

「サイトの仕様:ナビゲーション」とはどんな違反なのか?

どうやら「ユーザーにとって有益ではないページを表示している」違反のようです。

添付画像から察するとアプリがクラッシュして使えないからポリシー違反ということか?

と、いうことで「正常に動作します。」という旨を記載し、再審査の申請をしました。

次の警告メールの指摘はマップ画面?

再審査の申請から2日後、またしても警告メールが届きました。

やはり違反内容は、「サイトの仕様:ナビゲーション」。

そして、添付されていた画像はマップ画面。

アプリはやっぱりクラッシュしなかったのか。。。

次の指摘は、アプリ起動後に表示されるマップ画面が真っ青だという指摘のようです。

これは現在地座標が取得できなくて(0,0)地点を表示しているためだと思われるため、

「アプリは位置情報の取得を許可して使います」という旨を記載し、再度、再審査を申請。

次の警告メールはアプリ起動時広告

再審査を申請した翌日、警告メールが届きました。

違反内容は変わらず、「サイトの仕様:ナビゲーション」。

そして、添付されていた画像はアプリ起動時広告。

えっ、アプリ起動時広告が指摘されるのか!?

AdMobの標準機能であるアプリ起動時広告が指摘されてしまいました。

そして、なんとGoogle Playからも広告表示に関するポリシー違反の指摘が届きました。

AdMobだけではなく、アプリ自体にポリシー違反が!?

まずはGooglePlayに対し、「広告表示にはAdMobの標準機能を使っている」という旨で異議申し立てを行います。

違反してました

異議申し立てから10日後、異議申し立て却下の通知がきました。

どういうことか?

改めてアプリ起動時広告のガイドラインを確認してみると下記の記載を発見。

思いっきり違反していました。

そこでアプリ起動時広告を削除してアプリを再リリースします。

まだ続くポリシー違反

GooglePlayのポリシー違反は解除されました。

が、AdMobのポリシー違反は相変わらず解除されません。

次はスプラッシュ画面を指摘されました。

スプラッシュ画面の表示を7秒→3秒に変更してアプリを再リリースします。

まだまだ続くポリシー違反

次はホーム画面を指摘されました。。。

位置情報の許可をしないと表示されないんだが。。。

対応する

ニッチもサッチも展開になってしまったので思い切って起動周りの処理を変えます。

プッシュ通知の表示でコールするstartForegroundの引数追加に対応できていませんでした。

最初の指摘はこれだったのか・・・

最終的に以下の改修を実施。

・対象年齢を8歳から13歳以上に引き上げ、ファミリー向けプログラムを回避

・それに伴い、アプリ起動時広告を再度、表示させるよう改修

・イニシャル画面を追加し、位置情報を許可させるよう改修(許可しない場合はアプリ強制終了)

・Android14対応(アプリクラッシュ回避)

そして、修正版アプリをリリース。

リリースが反映されたのを確認し、審査をリクエストします。

ポリシー違反解除

ついにようやくポリシー違反が解除されました。

上記について押させておく必要がありました。