テストとして、ペンは、ヘッドセットが接続されたときに着信コールに自動的に応答するアプリケーションを使用しました。 もちろん、そのような単純な関数が「箱から出して」システムに存在しないことは奇妙です。 はい、そして市場にはこれを行うことができるアプリケーションは1つしかなく、あまり信頼性がありませんでした。 この誤解を修正してみましょう。
一見したところ、アプリケーションは非常にシンプルである必要があります。
- マニフェストで、回線の状態の変更に関するメッセージ( TelephonyManager.ACTION_PHONE_STATE_CHANGED )の受信者を切断し、着信呼び出しを監視します。
- 通話が着信すると、ヘッドセットが接続されているかどうかを確認します。 私は主にBluetoothに興味がありましたが、有線ヘッドセットは追跡するのに良いでしょう。
- ヘッドセットが接続されている場合、電話に応答するように電話に指示します。
最初の段落には特別な問題はありませんでした。 しかし、他の2つはそれほど些細ではありませんでした。
電話に出る
3番目の段落から始めます。 Androidには、着信呼び出しに応答するAPIがありません。 バージョン1.xでこれを許可するドキュメント化されていないクラスやメソッドに手を差し伸べることができた場合、2.2ではこれらのすべての穴が確実に閉じられました。 かなりグーグルは同じ方法を見つけました:ヘッドセットのボタンを押すことを描写します( Intent.ACTION_MEDIA_BUTTON 、 KeyEvent.KEYCODE_HEADSETHOOK )。 確かに、微妙な点があります。 このイベントは、 sendBroadcastではなくsendOrderedBroadcastを介して送信する必要があります。 そうしないと、すべてのレシーバーにヒットし、それらの中には、たとえば、再生するものが見つかった場合に、すぐに起動するオーディオプレーヤーがあります。 おもしろいことに、エミュレーターでは、プレーヤーは順序付けられたブロードキャストを使用していても起動しますが、すべては電話で正常に機能します。 エミュレータとアイロンのもう1つの違い:最初は、ボタンのリリースに関するメッセージを送信するだけで十分です(ところで、プレーヤーは押すというメッセージだけで起動します)。 電話では、クリックとリリースの両方を生成する必要があります。 彼らは、1.xでは、injectKeyEvent関数(または同様の関数)の最後に到達することが可能であり、それがより正確だと言います。 しかし2.2では、この機能の保護が修復され、利用できません。
ヘッドセットの状態
ここで最も難しい部分はポイント2です。ヘッドセットが接続されているかどうかを確認してください。 問題は同じです-Androidには、見つけることができる既製のAPIはありません。 さらに興味深いことに、有線ヘッドセットと無線ヘッドセットの動作は著しく異なります。
原則として、両方のヘッドセットについて、接続/切断メッセージを追跡できます。 有線の場合、これはIntent.ACTION_HEADSET_PLUG (ヘッドセットステータスは追加パラメーターとして渡されます)、ワイヤレスの場合、 BluetoothDevice.ACTION_ACL_CONNECTEDメッセージとBluetoothDevice.ACTION_ACL_DISCONNECTEDメッセージのペアです。
最初に行うことは、これらのメッセージの受信者をマニフェストに直接登録して、何も見逃さないようにすることです。 ワイヤレスヘッドセットでは、この数は過ぎますが 、何らかの理由でACTION_HEADSET_PLUGはフラグFLAG_RECEIVER_Rector_ONLYで送信されます 。つまり、マニフェストからの受信者はそれを受信しません(ドキュメントにはそれについて何も記載されていないため、エラーのように見えます)。 それらをコードに登録する必要があります。 これは、目的の受信者をプログラムで登録し、常に実行され続けるサービスを開始する必要があることを意味します。 ただし、ここにはレーキもあります。このようなサービスは、システムによっていつでも強制終了される可能性があり(テスト中に発生)、その結果、いくつかのメッセージをスキップできます。 どうやら、市場の単一のアプリケーションで同様のアプローチが使用されたようです。これについては、記事の冒頭で述べましたが、ヘッドセットの接続を常に適切に決定するとは限りませんでした。 手で走らなければなりませんでした。
出口が見つかりました。 ACTION_HEADSET_PLUGメッセージがスティッキーであるという事実は救助に来ます。 つまり、新しく登録されたすべてのリスナーに最新のヘッドセットステータスが報告されます。 registerReceiver(null、headsetPlugFilter)を呼び出し、返されたインテントを確認することで、ワイヤレスヘッドセットのステータスをいつでも確認できることがわかりました 。 喜んで。 サービスは必要ありません。有線ヘッドセットのポイント2が実装されています。
ワイヤレスヘッドセットを使用します。 ここでは、接続/切断メッセージを必ず監視する必要があります。ヘッドセットの現在の状態はいつでも確認できません。 ただし、マニフェストを使用して受信者を登録できます。 別の欠点は、現在の状態をどこかに保存する必要があることですが、呼び出しの間にコードが実行されないため、通常の変数を使用することはできません。 この目的のために、アプリケーション設定で隠しパラメーターを選択しました。 設定ウィンドウに表示されないという意味で隠されています(このウィンドウは考慮しません-すべてがそこで単純です)。
キャッチホン
もう一つの重要なポイントを忘れていました。 人がすでに電話で話している場合、おそらく、新しい着信コールに自動的に応答する必要はありません。 私は設定でこのために別のオプションを作成しました。 したがって、応答する前に、通話時にアクティブな会話があったかどうかを確認する必要があります。 繰り返しになりますが、APIはこのための手段を提供していません。 利用できるのは、通話のためにすでに機能している受信機だけです。 電話の状態の変化をオフフックまたはアイドル状態で追跡できます。 ただし、(ワイヤレスヘッドセットの状態に関して)この状態を保存するためにアプリケーション設定を使用すると、非常に高価になる可能性があります。 したがって、オフフック状態をメモリに保存する作業サービスが再び必要です。 また、サービスが誤ったタイミングでシステムを停止する可能性を減らすために、startForegroundを使用して一時的にシステムを開始できます。 呼び出しが完了すると、stopForegroundを呼び出してサービスを一時停止できます。
印象
その結果、計画は実装されましたが、そのためには計画よりも多くのコードを書く必要があり、結果はあまりきれいに見えません。 正直なところ、なぜAPIにisHeadsetConnectedやanswerCallのような既製の関数が含まれていないのか理解できません。
ところで、私の当初の計画では、 TextToSpeechを使用して呼び出し元を宣言する別のオプションがありました 。 ただし、通話時にTTSからワイヤレスヘッドセットに音声をリダイレクトすることはできません(誰かが方法を知っている場合は共有してください)が、ポケットに入れて電話で話しても意味がありません。 私は気にしませんでした-私は全体としてこの機能を捨てました。 もちろん、すみません。
一般に、プラットフォームの利便性はこれまでのところ単なる理論にすぎませんでした。 実際には、常に奇妙で非論理的な制限に遭遇し、それらを回避することは常に可能とは限りません。 しかし、アイデアは良いです。 次に何が起こるか見てみましょう。 APIを思い起こさせて、それが本当に素晴らしいものになることを願っています。