スマホアプリを作らせよう委員会、第三弾です。
第一弾は、Windows用に作ったマインスイーパーをAndroidでも遊べるようにする移植、第二弾は軍人将棋のスマートフォン版への移植でした。今回は移植ではありません。最初からAndroid向けに、スマートフォンの中にあるセンサーを覗くアプリを作ります。
作ったのは「AF-SensorMonitor」です。Androidが公開しているセンサーを全部並べ、スマートフォンを傾けたり、振ったり、磁石を近づけたりしたときに、どのセンサーが反応したかを一目で見られます。
手元の4台に同じアプリを入れた画面です。センサーの数は、Galaxy S26 Ultra(以下、S26 Ultra)が45個、Pixel 7 Proが39個、タブレットのTECLAST P40HDが32個、SHARP SHG03が18個でした。数字はきれいな階段ですが、中身を見ると、その順番に「高機能」とはなっていません。それは後半で書きます。
開発を任せたのは、AnthropicのAI開発ツール「Claude Code」です。サイトのキャラクターでいうと「クロさん」の担当作品で、今回はそのクロさんが、アプリの中にマスコットとしても登場します。
※Claude Codeは、ファイルの作成やコードの実装・テストなどをAIに任せられるAnthropicの開発ツールです。今回はモデルにClaude Opus 5.5を使いました。詳しくはClaude Codeとはをご覧ください。
移植ではないネタを探した
企画は、いつものように外部チャット(ChatGPT)への相談から始まりました。この委員会は、マインスイーパー、軍人将棋と移植が続いています。そこで、Androidならではの企画を聞いたところ、候補のひとつがセンサーを見るアプリでした。
加速度、ジャイロ、地磁気、気圧、照度、近接。スマートフォンにはセンサーがいくつも入っていますが、普段その値を見ることはありません。しかも、同じAndroidでも機種によって載っているセンサーは違うはずです。
面白いと思ったのは、外部チャットのこの整理でした。
仕様表ではなくAndroidに聞く
メーカーの仕様表を調べるのではなく、Androidが実際にアプリへ公開しているセンサーを、アプリ自身に列挙させます。何が出てくるかは、動かしてみるまで分かりません。
取扱説明書なしで「こいつが反応した!」
外部チャットと仕様を詰め、Claude Codeへ渡しました。目的は、精密な測定器を作ることではありません。依頼文には、こう書いています。
最大の目的は、「スマホを傾けた」「振った」「回した」「光を当てた」「手で塞いだ」「磁石を近づけた」など、人間が何かしたときに、「このセンサーが反応した!」と視覚的に分かることです。
そのための決めごとは、次のとおりです。
- 起動したら、すぐにセンサーの一覧が動き出す。チュートリアルや長い説明文は置かない。
- 1センサーを原則1行にした、高密度な計器盤にする。カードを並べた余白の多い画面にはしない。
- 各行の左に活動ランプを置き、値が変わったら光らせる。
- Androidが返すセンサーは、未知のものやメーカー独自のものも除外せずに全部並べる。
- メーカーや機種を判定して動作を変える実装はしない。
- 画面は、メイン・センサー詳細・表示設定の3つだけ。
そして一番大事にしたのが、実機の事前調査の禁止です。
これら各機種の搭載センサー構成を事前調査しないでください。
「この機種にはこのセンサーがあるはず」とAIが先回りして調べ、親切にネタバレしてしまうのを防ぐためです。完成したアプリを実機に入れて、何が出てくるかを見ること自体を実験にしました。
途中で、この方針は一部変えています。S26 Ultraの画面に全センサーを収めることを完成条件にしたので、S26 UltraとPixel 7 Proは開発中からUSBでつなぎ、実機で数えたセンサーの数を見てUIを調整してよいことにしました。SHG03とタブレットは、完成するまで接続しない「未開封」のまま取っておきます。
Claude Codeのレビューで、一番効いた提案
Claude Codeには、いきなり実装させず、まず仕様のレビューと実装計画を出してもらいました。返ってきたレビューは、仕様の穴を埋めつつ、企画の面白さは壊さない内容でした。
言語はKotlin、画面は宣言的に組み立てるJetpack Composeです。センサーの列挙には、AndroidのSensorManagerから全種類(TYPE_ALL)を取得する方法を使い、名前と単位の対応表は、Androidが標準で定めている種類にだけ持たせます。表にない種類は「汎用表示」にして、何も除外しません。
一番効いたのは、この提案です。
刺激を与えてから詳細を開くと、その瞬間を見逃す → 表示中の全センサーについて、20秒分の履歴を常時保持する
メイン画面で「今、MAGが光った」と気づいてタップしても、詳細画面を開いたときにはもう終わっている。それを防ぐため、詳細画面を開いていないセンサーも含めて、全センサーの直近20秒を常に記録しておきます。実際に使うと、ここが一番ありがたい機能でした。
活動ランプの判定方法も、Claude Codeの提案です。センサーごとに値の大きさもノイズの量も違うので、固定の閾値では決められません。そこで、センサーごとに「静かなときの揺らぎ」を自動で学習し、それを大きく超える変化があったら光らせます。
こちらから直したのは、主に次の点です。
- UIの言語は英語に統一する。センサー名が英語なので、「ALL」「RESET ORDER」「PAUSE」「HOLD」などのボタンも英語にそろえる。
- ダークテーマに固定せず、AndroidのLight/Dark設定に従う。
余談ですが、Java時代のAndroid開発を知る身としては、Kotlinは少し寂しいところです。ただ、SensorManager、ライフサイクル、リングバッファといった設計の考え方は昔のままで、文法が分からなくても、何をしているかは追えます。
S26 Ultraは45個、Pixel 7 Proは39個を返した
Claude Codeは、ビルド環境と2台の接続を確認してから実装を進め、デバッグビルドを両方の実機に入れました。そこで出た数字が、S26 Ultraの45個、Pixel 7 Proの39個です。
初版の画面です。左がS26 Ultra、右がPixel 7 Proです。S26 Ultraの45個は、縦1画面にスクロールなしで収まっています。そのために文字は11.4dp相当まで小さくなりましたが、実物を見ると十分に読めました。
中身も面白いものでした。S26 UltraにはBACK_TAP(背面タップ)、POCKET_MODE、CAR_CRA_DETE(事故検知)といったSamsung独自のセンサーが並びます。Pixel 7 Proには、ジャイロの温度(GYRO_TEMPERA)、カメラの同期信号(CAMERA_VSYNCが4個)などが出てきました。どちらも、普通の「センサー一覧」の想像とはかなり違います。
実機では、課題も4つ見つかっています。Claude Codeの報告を要約すると、次のとおりです。
- センサー名の欄が短く、名前が読めない。→ 12文字に広げた。
- S26 Ultraで45行が画面に収まらない。→ 行の高さとヘッダーを調整した。
- Pixel 7 Proで、一度だけ反応するタイプのセンサーが、待ち受けを再開するたびに即座に反応し、短時間で542回に達した。→ 再開に間隔を入れた。
- S26 Ultraの独自センサーで、値の受信を拒否されるものが多い。→ 1回だけ別の頻度で再試行するようにした。
最後の件は、再試行しても約20個が受信できないまま残りました。一覧には出るのに、値は読めません。これは後で書きます。
磁石を近づけると、MAGだけが光る
ここからは、実際に触って遊んだ記録です。まずは磁石から。
左は、S26 Ultraに磁石を近づけたり離したりしているときの画面です。地磁気のセンサー(MAG)が-625μTまで振れ、光っているのはMAGと、その補正前の値(MAG-U)の2行だけです。加速度もジャイロも静かなままです。
右は、磁石を近づけたまま止めた画面です。MAGは-510μTと大きな値のままなのに、ランプは消えています。活動ランプが見ているのは値の大きさではなく変化なので、止まっている磁石には反応しません。仕様どおりですが、実物で見ると「なるほど」と思える画でした。
MAGの行をタップすると、詳細画面で直近20秒のグラフが見られます。
磁石を3回往復させたところです。地球の磁場は数十μTほどですが、磁石を近づけるたびにX・Y・Zがそれぞれ数百μT単位で振れています。
上部にはACC UNRELIABLEと出ています。これはセンサー自身が報告する精度で、磁石のせいで「今の値は信用できない」とセンサーが判断した状態です。こちらが何も設定しなくても、センサーの側が困っている様子まで画面に出てきました。
振ると、まとめて光る
次は、スマートフォンを手に持って動かします。
左が置いたまま、右が動かした瞬間です。加速度(ACCEL)、重力を除いた加速度(LINACC)、ジャイロ(GYRO)、回転(ROTVEC)などがまとめて光ります。初期の並び順は、動きに関わるセンサーが上に来るようにしてあるので、光るのは上の方の行に集中します。磁石のときにはMAGしか光らなかったのと対照的です。
左上のアイコンにも注目してください。たくさんのセンサーが同時に反応すると、マスコットのクロさんが「!?」という顔になります。これも後で紹介します。
加速度の詳細画面を開くと、センサーが値を送ってくる頻度に驚きます。
S26 Ultraの加速度センサーは、1秒間に約540回、値を送ってきました。アプリを起動してからの受信数は、34万回を超えています。普段スマートフォンを使っていて、裏でこれだけの値が流れているとは意識しません。
実はこの速さのせいで、最初の版では、振っても加速度のランプが光りにくいという問題がありました。1回ごとの値の差を見て判定していたので、1/540秒ではほとんど差が出なかったのです。Claude Codeは、18ミリ秒以上離れた値どうしを比べる方式に直し、500Hzでも50Hzと同じように反応することを確認するテストを追加しました。
手で塞ぐ、光を当てる
画面の上部を手で塞ぐと、近接センサー(PROX)が反応します。
S26 Ultraで、塞いだり離したりを繰り返したグラフです。値は0cmか5cmの2つしかなく、グラフは四角い波になりました。距離を測っているというより、「近い」か「遠い」かだけを返しています。Claude Codeがレビューで「近接センサーは二値しか返さない端末が多い」と書いていたとおりでした。
センサー名はPalm Proximity Sensorで、名前に「Palm(手のひら)」と付いていました。SHG03でも同じことを試すと、こちらも0cmと5cmの2値で、センサー名はstk3x3x Proximity Sensorでした。
照度センサー(LIGHT)は、暗い場所に置き、COBライトの無段階ダイヤルを回して明るさを上げ下げしました。このライトはPWM方式で、LEDを高速に点滅させ、点いている時間の割合で明るさを変えています。
ダイヤルを回すと、約1lxの暗がりから約123lxまで、回した分だけグラフが上がり、戻すと下がります。高速な点滅はグラフには出ず、平均した明るさとして読めています。
上り下りが滑らかな線ではなく階段状なのは、値が届く頻度が1秒に2.5〜4.8回ほどしかないためだと思います。加速度の540回とは桁が違います。
LOCKED、ARMED、N/A
メイン画面では、値のほかに、センサーの状態が記号で出ます。使っているうちに、この状態表示そのものが面白くなってきました。
| 表示 | 意味 |
|---|---|
| ○ / ● | 普通に値が読める。●は反応中 |
| – — | まだ値が届いていない |
| ◇ ARMED | 一度だけ反応するタイプのセンサーが、イベントを待っている |
| 🔒 LOCKED | 権限を許可すれば読める |
| × N/A | 一覧には出るが、値を受け取れない |
S26 Ultraの下半分に約20行並んでいたのが、× N/Aです。そのひとつ、BACK_TAPを開いてみました。
受信はFAILED、イベントは0回です。それでも、ベンダーはSamsung、種類の番号は65651、内部の名前はcom.samsung.sensor.back_tapと、素性はきちんと名乗っています。Androidの一覧には存在するが、一般のアプリには値を渡さない。そういうセンサーです。
外部チャットでは一度、N/AよりBLOCKED(拒否された)の方が分かりやすいのでは、という話になりました。私は、デバッガーや技術書でN/Aを見慣れているので、普通に通じると思っていました。
最終的にN/Aのままにしました。分かっているのは「一覧には出るが、値を受け取れない」ところまでで、それが権限で拒否されたのか、一般のアプリには公開されていないのか、条件がそろわないのかまでは、一律に断定できないからです。BLOCKEDと書くと、分かっていないことまで言い切ってしまいます。
逆に、理由がはっきりしているのが🔒 LOCKEDです。歩数計(STEPS)と歩行検出(STEP)は、Androidの「身体活動」の権限がないと値を受け取れません。
Pixel 7 Proで、LOCKEDのSTEPSを開き、権限を許可して歩いたところです。
面白いのは②です。許可した直後、最初に届いた値がいきなり35歩でした。歩数計はOSの側でずっと数え続けていて、アプリは許可されるまで見せてもらえなかっただけ、ということです。そこから歩くと、③のようにグラフが階段状に増えていきます。「権限が必要です」という説明文を読むより、ずっと分かりやすい体験でした。
45個全部を見せる。邪魔なら自分で消す
S26 Ultraの45個は、正直に言えばごちゃごちゃしています。下半分はN/Aばかりです。
それでも、初期状態は全部表示のままにしました。このアプリの目的は、Androidがアプリに見せているものを全部見ることです。BACK_TAPやPOCKET_MODEは、そこにいること自体が面白いので、勝手に隠すと面白さが減ります。
一方で、普段使いの計器盤としてはN/Aの行は邪魔です。そこで効くのが表示設定です。
左が全表示、右がN/Aの行を消した状態です。ヘッダーの数字が「45/45」から「27/45」に変わり、値が読めるセンサーが中心の計器盤になりました。行数が減った分、文字は自動で大きくなり、読みやすくなります。
表示設定では、センサーごとに表示のON/OFFを切り替え、ドラッグで並べ替えられます。「ALL」で全表示に戻り、「RESET ORDER」で並び順が初期状態に戻ります。表示をOFFにしたセンサーは、値の受信自体も止めます。
最初に全部を見て「こんなのまでいるのか」と驚き、使ううちに自分用の計器盤へ整理していく。その二段構えが、このアプリの遊び方になりました。
0°と360°の縦線から、新しい表示が生まれた
S26 Ultraで遊んでいて、ひとつ引っかかる画面がありました。端末の向き(ORIENT)の詳細画面です。
赤い線は方位(AZIMUTH)です。ところどころで、グラフの上から下まで縦線が走っています。
センサーが壊れたわけではありません。方位は0°〜360°の値なので、北をまたいで359°から0°になると、普通のグラフでは上端から下端へ飛んでしまいます。0°と360°は同じ向きなのに、直線のグラフでは両端に分かれているからです。
普通なら、359、360、361……と値を続けて、軸を広げる方法があります。ただ、ぐるぐる回すと値が720°、1080°と増えていき、方位ではなく「何回転したか」を表す値に変わってしまいます。
そこで、Claude Codeにレーダーの表示方式(PPIスコープ)で描けないか検討させました。角度を円周上の位置で表し、半径を時間にします。外周が現在で、中心に近いほど過去です。
Claude Codeが実装の前に描いた概念図です。円で表すと、0°と360°は同じ場所に来るので、北をまたいでも線が途切れません。ピッチとロールは円に向かないので、従来の時間グラフのままにしています。
実装された画面が、こちらです。
端末を手に持って、ほぼ一周回したところです。レーダーの方位(Azimuth)と距離(Range)になぞらえて、私はこの表示を「AzRange」と呼んでいます。回した軌跡が円周に沿って描かれ、時間とともに中心へ沈んでいきます。北をまたいでも、線は途切れずにつながっています。
数値の意味を知らなくても、「自分が回した跡だ」と分かります。縦線を消すための対策だったはずが、回した軌跡そのものが見える機能になりました。
この円の表示は、ORIENTのほか、回転系のセンサーにも使っています。ただし、ゲーム用の回転ベクトル(GAMEROT)だけは、北ではなく起動したときの向きが基準です。そこで、このセンサーだけ円の上端を「N」ではなく「0°」と表示しています。
コンパスだと誤解されないよう、北を示せるものと、角度しか示せないものを表示で分けました。
立てると、方位が暴れる
円の表示で遊んでいると、もうひとつ気づくことがあります。端末を縦に立てるほど、方位の軌跡がギザギザになるのです。
ピッチが-79°、ほぼ真っすぐ立てた状態で回したところです。内側の軌跡が細かく暴れています。
外部チャットに「Tan的なアレか」と聞くと、感覚としてはほぼ合っていて、正確にはオイラー角の特異点(ジンバルロック)だという答えでした。端末を立てると、端末の上方向を水平面に映した影がほとんど点になり、「水平面でどちらを向いているか」が決めにくくなります。わずかなノイズで、方位が大きく飛びます。
ではatan2のように、2つの値から角度を求めればひっくり返せないのか。これも聞いてみましたが、atan2でも、x・yが両方ほぼ0の点には方向を決められない、という答えでした。別の軸を使えば計算はできますが、そのときの方位は、Androidが返す方位とは別物になります。
そこで、これは直さないことにしました。
- 0°と360°の境目で線が飛ぶ → 表示の問題なので、円で表示して解決する
- 立てると方位が暴れる → 方位という表し方の限界なので、そのまま見せる
複数のセンサーを組み合わせて安定した姿勢を求めるのは、それを使う側のアプリの仕事です。AF-SensorMonitorは、Androidが返す値をそのまま見せるアプリなので、補正して隠すより、暴れる様子を見せる方が目的に合っています。
マスコットのクロさんが、アプリにデビュー
今回から、サイトのキャラクターをアプリにも登場させました。担当のクロさんです。
素材は外部チャットで生成し、Claude Codeに渡しました。ヘッダーの左端で、普段は平常心の顔をしています。4個以上のセンサーが同時に強く反応すると、「!?」の顔に変わります。
計器盤の見やすさを邪魔しないことが第一条件だったので、出てくるのはヘッダーの小さなアイコン、表示設定の最下部、アプリアイコンの3か所だけです。最初は「アプリアイコンだけで十分では」とも考えましたが、実物を見ると、センサーが暴れるとクロさんも暴れるのが妙に面白く、そのまま採用しました。
アプリアイコンは、キャラクターの顔ではなく、Cの字と緑のセンサーの点、電波のような弧にしています。ホーム画面では真面目な計測アプリ、中に入るとクロさんが驚いている、という役割分担です。
未開封の2台を開封する
S26 UltraとPixel 7 Proで一通り遊んだあと、いよいよ未開封の2台を接続しました。以前使っていたスマートフォンのSHARP SHG03(Android 12)と、ブラウザ用に使っているAndroid 13のタブレットです。
タブレットは開発者向けオプションが無効だったので、設定の「ビルド番号」を7回タップする、例の儀式から始めました。
結果は冒頭の画像のとおりです。SHG03は18個、タブレットは32個でした。
| 端末 | センサー数 | 中身の印象 |
|---|---|---|
| Galaxy S26 Ultra | 45 | 基本のセンサー+Samsung独自が山盛り。約20個はN/A |
| Pixel 7 Pro | 39 | 基本のセンサー+Google独自が山盛り |
| TECLAST P40HD | 32 | ジャイロ・地磁気・向き・照度・近接が見当たらない。動きの判定が大量 |
| SHARP SHG03 | 18 | 数は少ないが、ジャイロ・地磁気・回転・向き・照度・近接がそろう |
一番意外だったのはタブレットです。32個も名乗るのに、ジャイロも地磁気も向きのセンサーも出てきません。照度と近接も見当たりません。代わりに並ぶのは、加速度が通常版とWake-up版と補正前で4本、TAP、FLIP、FACE_UP_DOWN(表裏)、ELEVATOR、CHOPCHOPといった、動きを判定する系統のセンサーです。数が多いのは、同じものの通常版とWake-up版が並んでいるからでもあります。
タブレットの正体は、Android 13の10.1インチタブレット「TECLAST P40HD」でした。買ったときは、サイズと値段だけで選んだので、メーカー名すら忘れていました。低価格帯の機種なので、コストを抑えるためにセンサーを絞ったのかもしれません。ブラウザや動画など、普通にタブレットとして使う分には困らないので、それで十分という判断だったのだろう、と想像しています。
気になったCHOPCHOPも開いてみました。
チップメーカーのSpreadtrum(Sprd)由来のセンサーで、何かの動きをきっかけに反応するタイプでした。何をすると反応するのかは、まだ分かっていません。事前に調べずに、振ったりひっくり返したりして探すのも、このアプリの遊び方だと思っています。
一方、SHG03は18個しかないのに、ジャイロ、地磁気、回転、向き、照度、近接と、触って面白いセンサーが一通りそろっていました。
SHG03の一覧は、見ていて気持ちのいいものです。S26 UltraのようなN/Aの一団はなく、同じセンサーの通常版とWake-up版が何組も並ぶこともありません。18行のほとんどで値が動き、傾ければ傾き、回せば回転、手で塞げば近接と、触ったとおりに反応が返ってきます。気圧計こそありませんが、SHARPさん、必要最小限を分かっているな、というのが正直な感想です。
32個のタブレットより18個のSHG03の方が、センサーで遊ぶにはずっと面白い端末でした。
センサーの数だけを見ていたら、タブレットの方が豊富だと勘違いするところでした。スペック表を見ずに端末自身に聞いた結果、数と中身が一致しないことが分かったのは、今回一番の収穫です。
ちなみに、P40HDには以前作った軍人将棋も入れてみました。開発中のPCやS26 Ultraでは一瞬で指していたCPUが、ここではしっかり考え込みます。CPUは、ちゃんと考えていたようです。
未開封の2台に、Claude Codeが食いついた
SHG03とタブレットの画面を見せると、Claude Codeが別のところに気づきました。単位が切れています。
S26 Ultraの45個を1画面に収めるため、行の高さと文字の大きさは、センサーの数と画面の高さから自動で決めています。SHG03は18個しかないので、縦に余裕があり、文字が大きくなります。すると今度は横が足りなくなり、単位が切れていました。私はセンサーの中身ばかり見ていて、言われるまで気づきませんでした。
Claude Codeの調査では、原因は文字の間隔でした。行の文字に標準の文字間隔が入っていたため、文字が大きくなるほど横幅を食っていたのです。文字間隔をなくし、横幅の計算を見直して、右のように単位まで収まりました。あわせて、行の高さの端数のせいで最後の行がつぶれていた問題も直しています。
このときClaude Codeは、Pixel 7 Pro、SHG03、タブレットの3台を同時にUSBでつなぎ、順番に確認していました。最後にS26 Ultraもつないで、4台で同じRelease版のAPKを確認しています。
縦長で行数の多いS26 Ultra(45個)とPixel 7 Pro(39個)、行数の少ないSHG03(18個)、正方形に近いタブレット(32個)。手元にあった4台が、画面の比率とセンサー数の組み合わせを試す、ちょうどよいテスト機になりました。PCのUSBポートは、全部埋まりました。
残っている課題
1.0.0として一区切りにしましたが、使っているうちに気づいた点がまだあります。
- 歩数計のように値が変わったときだけ届くセンサーで、20秒以上変化がないと、値は残っているのにグラフの線が消える。
- 文字サイズを大きくしているタブレットで、詳細画面の見出しの2行目が状態の行に重なる。
どちらも、次の版で直す予定です。
ソース一式
AF-SensorMonitorのソース一式は、GitHubで公開しています。
ソースコードはMIT Licenseです。クロさんのマスコットやアプリアイコンなどのブランド素材は許諾していないので、流用する場合は差し替えてください。
Android版のAPKは配布していません。理由は、マインスイーパーのAndroid対応に書いたとおりです。興味のある方は、ソース一式をAndroid Studioで開き、ご自身の環境でビルドしてください。
制作中のClaude Codeとのやりとり
今回の制作で、Claude Codeへ出した指示とその回答を公開しています。仕様のレビューを頼むところから、実装、4台の実機での確認、開発を一旦終えるまでのやりとりです。
最初の長い指示は、外部チャットで企画と要件定義を行い、その結果から製造のために生成したプロンプトです。外部チャットとの相談そのものは公開せず、この記事の中で必要なところだけを抜粋しています。相談する場所と作らせる場所を分ける進め方は、私とAIコーディングで紹介しています。
AF-SensorMonitor制作中のClaude Codeとのやりとりを読む
仕様表ではなく、端末に聞く
作る前は、「傾けたり光を当てたりしたら面白そう」くらいの気持ちでした。
実際には、45個のセンサーを名乗るS26 Ultra、見せてもらえないN/Aの一団、許可すると動き出すLOCKED、磁石で自分の精度を下げる地磁気センサー、そして32個もあるのにジャイロを持たないタブレットと、仕様表からは見えないものが次々に出てきました。
作りながら仕様も変わりました。0°と360°の縦線に違和感を持ったところから、回した軌跡が見える円の表示が生まれ、センサーの少ない端末をつないだところで、単位が切れる問題が見つかりました。最初から完璧な仕様を渡したのではなく、実物を触った人間が違和感を拾い、AIと相談して直していく。その繰り返しで、当初の想像よりずっと面白いアプリになりました。























コメント