Edge-to-Edgeを無効化する

2025年9月21日

開発環境
OS:Windows 11
SDK:Android Studio Narwhal Feature Drop | 2025.1.2

概要

Android15以降、強制的に適用されてしまうEdge-to-Edge。
ステータスバーやナビゲーションバーなどシステムバー付近にボタンを配置している場合、調整が必要になります。

Edge-to-Edge

アプリユーザーの没入感を高めるため、Android 15以降、アプリに強制的に適用されるようになったのがEdge-to-Edge。

この機能によってスタータスバーやナビゲーションバーなどのシステムバーが透過となり、その裏までアプリが表示できるようになりました。

詳細はこちらを参照ください。

その弊害

しかしながら、全てのアプリでEdge-to-Edgeが歓迎されるかと言うとそうではなく、Edge-to-Edgeが適用された結果、システムバーがUIの邪魔になるケースが発生。

まさに自作アプリ「サムズアップ?」がそうで

ステータスバーとボタンが重なり、ナビゲーションバーと広告が重なるという惨状・・・
せっかくならUIの調整を行い、Edge-to-Egdeを有効に生かしたいところですが、ちょっとUIを調整する暇はない、ということでEdge-to-Edgeを無効化することにしました。

Style.xmlで無効化する

Edge-to-Edgeを無効化するにはStyle.xmlのスタイル定義に1行追加するだけです。

 <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item>

そして、動作確認してみた結果がこちら。

ボタン、広告ともにシステムバーと重ならないようになりました!

結局、使いどころは・・・

アプリ「サムズアップ?」においてはEdge-to-Edgeの使いどころはなさそうでした。

Edge-to-Edgeを前提に画面構成を考えるって意外と難しい。。。

全ての画面にEdge-to-Edgeを効かせる、というよりピンポイントで適用させるようなスタイルの使い分けがベターなのかもしれませんね。

今後、アプリを自作するときはそのあたりも考慮したいと思いました。

それでは以上です。

お疲れさまでした。

多言語化におけるインドネシア語の対応について

2025年8月17日

開発環境
OS:Windows 11
SDK:Android Studio Narwhal Feature Drop | 2025.1.2

概要

多言語化としてインドネシア語に対応したつもりでいたら、対応できていませんでした・・・

アプリをインドネシア語に対応させてみる

配信中の投稿系アプリ「 みてみて 」がインドネシアで伸びているということでインドネシア語に対応しようと思い、2か国語(日本語、英語)対応済みのアプリにインドネシア語の追加対応を行いました。

多言語化対応

多言語化の対応は簡単でリソースフォルダ(res)配下に対応したい言語のフォルダを作成した後、すでに格納されている文字列定義ファイル(string.xml)をそのフォルダに複製し、対応言語で定義しなおすだけです。(定義が多いと翻訳が大変ですが)

※アプリで表示する文字列をstring.xmlで一括管理している前提の話です。

2か国語の対応では、英語をデフォルト言語とするためフォルダ「values」に英語の定義ファイル(string.xml)を格納し、フォルダ「values-ja」を新たに作成し、日本語の定義ファイル(string.xml)を格納しました。

新たにインドネシア語に対応するには、フォルダ「values-id」を作成し、このフォルダにインドネシア語に変換した定義ファイル(string.xml)を格納するだけです。

新規に作成するフォルダ名は「values」とIOS639-1に準拠した言語コードをハイフン(-)で連結すればよいということでインドネシア語の言語コード「id」を連結し、「values-id」としました。

これで対応完了、ということでそのままリリース。

新たな機能を追加するつもりが・・・

それからしばらくして新たな機能を追加するために下調べでネット検索を行ったら、思わぬ情報が目に入りました。

えっ、インドネシア語のリソースフォルダは「values-in」・・・

慌ててスマホの言語設定をインドネシア語に切り替えてアプリを立ち上げてみると。

インドネシア語は表示されず、英語のままでした。。。

インドネシア語フォルダ修正

インドネシア語の定義ファイルを格納したフォルダの名前を「values-id」→「values-in」に変更します。

そして、スマホの言語設定をインドネシア語にして検証。

インドネシア語が表示されたことを確認してリリース。

ようやくインドネシア語に対応できました。

それにしても、なぜ?

インドネシア語の言語コードを確かめる

対応言語のPlayConsoleヘルプで確認した結果、インドネシア語の言語コードは、「id / in」となっていました。

これはつまり・・・

どういうこと?

少なくともXperia(SO-53C)でシステム言語をインドネシア語に切り替えて、アプリの表示を確認すると「values-id」ではデフォルト(英語)になり、「values-in」で定義したインドネシア語が表示される結果となりました。

対応するならば、
インドネシア語としては、「id」と「in」、2つの定義を共存させておくのがベターな対応かもしれません。

そして反省・・・

リリースするときはしっかり検証しましょう。。。

お疲れさまでした。

今月のみてみて(2024年12月)

Firebase連携アプリ、みてみてエストーク伊勢125社巡り

このうち、みてみてにフォーカスを当て、Firebaseのアクセス動向やアプリの改善を考察する開発記事です。

Firebase
Realtime Database+Storage構成の投稿アプリ

まずはアプリのユーザー数の変動とともにFirebaseのアクセス動向を確認します。

2024年12月1日~2024年12月31日のユーザー数の変動

(ユーザー数の変動)

ユーザー数変動

約8ヶ月ぶりのデータ解析。

ここにきて新規ユーザー獲得数に若干の伸び。

累計インストール数は3000突破。

(インストールの上位国)

東南アジアのトレンドが終わって中東エリアで伸び。

2024年12月31日直近のFirebaseの変動

(Firebaseの変動)

直近のfirebaseの動き
1ヶ月のfirebase使用量

大きな動きはなし。

Cloud Strageが増えず、Realtime Databaseに動きがあるので、投稿がユーザーに受けているわけじゃなく、ユーザー間メッセージに需要がある感じ。

安定の無料枠です。

2024年12月1日~2024年12月31日のAdMob

今月はAdMob広告収入:52円、 Firebase使用料:0円となりました。

今月のみてみて

(今月の投稿ピックアップ)

久しぶりにアウトレット行きました。

今後の改善案

(未消化の課題)

・投稿の編集機能追加

・写真を複数投稿可能にする

・手持ち画像も投稿できるようにする

・UIブラッシュアップ

・チャット機能強化

・フレンドグループ化と共有機能の追加

・ビジュアル強化

・アプリ名変更

ASUSノートPCバッテリー交換

2024年10月20日

長年使ってきたASUSのノートPC(X202E)が壊れました。

壊れた、と言っても基盤が破損した、とかではなく、まったく充電できなくなったわけです。

充電ランプがオレンジの点滅を繰り返し、一向に充電できません。

原因としてまず真っ先に疑うのがバッテリー。

使用年数を考えるとこれしか考えられない。

ということでバッテリーを購入します。

amazonでASUSノートPC(X202E)のバッテリーを探してみると純正ではないですが、いくつか候補が見つかります。

その中からバッテリーを専用で扱っている店をチョイスして購入しました。

税込み、6980円。

ノートPCの裏面のネジをはずします。

そして、カバーをはずします。

はめ込まれているので意外と固いですが、マイナスドライバーを差し込んでゆっくりと隙間をひろげていきました。

バッテリーの周辺のネジをはずし、最後にコネクターを抜きます。

コネクターはスライドして差し込まれているのでスライドさせるよう少し力を入れるとすんなりと抜けました。

バッテリー交換が終わったので充電してみたところ・・・

オレンジのランプが点滅したまま。

なんだと!?

バッテリー交換で復旧しない。

となるとアダプターなのか!?

ということでアダプターを購入。

こちらも純正ではないですが、型にあうものをチョイス。

税込み、2328円。

新しいアダプターが届いたのでもう一度充電してみます・・・

ついに充電ができました!

まさか根本原因はアダプターだったのか。

これであと2~3年は使えそうです。

ファイル・ディレクトリのアクセス権限変更

chmod -R 777 (指定ディレクトリ名)

777は、(オーナー)(グループ)(他ユーザー)の権限状態。

オーナー、グループ、他ユーザーそれぞれが下記の権限に基づきアクセスの制御を受ける。

Read(読み取り):4、Write(書き込み):2、eXcute(実行):1

700なら、オーバーのみフルアクセス。

-Rは、オプションで指定したディレクトリ配下に適用される。

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日

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