Thynth
← Works Log

画面OFFでも継続!スマホ消灯デトックスの実装技術

スマホを伏せて寝る前のデトックスを可能にする、画面消灯・端末ロック検知の裏側。Capacitorプラグインと誤判定を防ぐリトライ戦略を解説します。

スマホを伏せて寝る、その瞬間を支える技術

スマホ依存から脱却するためのデトックスアプリTokStopの開発において、最もこだわったのが「寝る前のスマホの置き方」です。多くのユーザーは、スマホを伏せて画面を消し、そのまま眠りにつきたいと考えます。

しかし、一般的なスマホ制限アプリは「アプリを閉じる=デトックスの諦め(離脱)」と判定してしまいます。画面を消して寝ようとしただけなのに、アプリ側で「セッション失敗」と判定されては、ユーザー体験(UX)は最悪です。

そこで私たちは、画面OFFや端末ロック状態を検知し、セッションを安全に継続させる**「消灯デトックス」**機能を開発しました。本記事では、その技術的な裏側と、OS特有の挙動に立ち向かった泥臭い試行錯誤のプロセスを共有します。

解決したい課題:アプリ離脱と「画面消灯」の境界線

モバイルアプリにおいて、画面が消える(あるいは端末がロックされる)挙動は、技術的には「アプリがバックグラウンドに移行する」というイベントとして検知されます。これは、ユーザーが別のSNSアプリを開いたとき(離脱)と全く同じ挙動です。

TokStopでは、アプリ離脱を検知すると「5秒間の猶予タイマー」が作動し、それを過ぎるとセッション失敗になります。消灯デトックスを実現するためには、以下の2つを厳密に区別する必要がありました。

  1. アプリ離脱(NG行動): アプリを閉じて別のアプリを開く、またはホーム画面に戻る。
  2. 消灯デトックス(推奨行動): 電源ボタンを押して画面を消す、または端末をロックする。

Webブラウザ版ではこの区別が難しいため、今回はハイブリッドアプリフレームワークであるCapacitorを用い、ネイティブ機能にアクセスすることで解決を図りました。

開発で使用したプロンプト

実装にあたり、モバイル端末特有のセンサーデザインと非同期処理の競合を解決するため、以下のプロンプトを用いてAIと設計を詰めました。

アプリ離脱検知と画面消灯を区別するモバイルアプリのセンサーデザインを、猶予タイマーとリトライ戦略込みで解説して。

このプロンプトから、バックグラウンド遷移イベント発生時に「即座に判定するのではなく、OSの画面状態を非同期でポーリング確認する」という堅牢なリトライ戦略のヒントを得ました。

工夫した点と苦戦した失敗談

ネイティブAPIとの連携には、一筋縄ではいかないモバイルOS特有の「罠」が潜んでいました。

失敗1:画面OFFなのにセッションが失敗する

初期の実装では、バックグラウンド遷移イベント(appStateChange)をトリガーに、一律でアプリ離脱猶予タイマー(5秒)を回していました。その後、Capacitorのネイティブプラグイン ScreenState を用いて状態をチェックしようとしましたが、タイミングのズレにより判定が間に合わず、消灯してスマホを置いただけで5秒後にセッションが失敗してしまう事象が多発しました。

  • 修正案: セッション開始時に allowScreenOff フラグを有効化。バックグラウンド遷移時にまずこのフラグを確認し、画面消灯の可能性がある場合は「離脱猶予タイマー」の起動を一時的にサスペンド(保留)する分岐を導入しました。

失敗2:OSの状態遷移の遅延による誤判定

最も厄介だったのが、ユーザーが電源ボタンを押してから、OS内部で「画面消灯(またはロック)」のステータスが確定するまでに数百ミリ秒のタイムラグがある点です。ボタンを押した直後に1回だけ isScreenOff を確認すると、OSはまだ「点灯中(Screen ON)」と返してしまい、アプリ側が「離脱した」と誤判定してしまいました。

  • 修正案(extendedリトライ戦略): 判定関数 confirmScreenOffForDetoxSession を実装しました。これは、バックグラウンド移行後、100ms間隔で最大5回、画面状態を再確認(ポーリング)する仕組みです。さらに、万が一判定不能(タイムアウト)になった場合は、ユーザーの努力を無駄にしないよう「消灯(セッション継続)」側に倒すフォールバック処理を組み込みました。
// 画面消灯を確認するためのリトライ処理イメージ
async function confirmScreenOff(): Promise<boolean> {
  for (let i = 0; i < 5; i++) {
    const state = await ScreenState.getCurrentState();
    if (state.isScreenOff || state.deviceLocked) {
      return true; // 消灯を確認
    }
    await delay(100); // 100ms待機してリトライ
  }
  return true; // 判定不能時はユーザー保護のため消灯とみなす
}

完成物の評価:枕元でそっと寄り添うUXへ

この「消灯デトックス」機能の実装により、TokStopのUXは劇的に向上しました。ユーザーは「デトックスを開始」ボタンを押し、安心して電源ボタンを押して枕元にスマホを置くだけで、朝まで静かに計測が継続されます。iOSでは deviceLocked、Androidでは isScreenOff を正確にフックすることで、プラットフォームごとの挙動差分も吸収できています。

開発の試行錯誤については、制作ログ一覧でも他の機能と合わせて紹介しています。私たちは技術的なアプローチで、ユーザーの「スマホを触らない意思」を優しくサポートし続けます。より詳細なプロジェクトの全体像はプロジェクト一覧からぜひご覧ください。