Palm Programmer's Laboratory

トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

Palm OS Programmer's Companion Volume I/10-2

← 1 節に戻る ↑10 章トップへ 3 節に進む →


10-2 アラーム

 
Palm OS のアラームマネージャは、周期的な処理の実行やリマインダの表示に利用できるリアルタイムアラームのサポートを提供します。アラームマネージャは以下の特徴を持っています。

  • リアルタイムアラームを処理するため、タイムマネージャと連携します。
  • 特定時刻にアラームを設定したアプリケーションに起動コードを送信して、アプリケーションにアラーム時刻が到来したことを通知します。
  • 設定できるアラームは、アプリケーション毎に1つだけです。
  • アプリケーションによるアラームの処理は以下の2つのサイクルで行なわれます。
    • まず、アラームの発生がアプリケーションに通知されます。アプリケーションはその時点でそのアラームがまだ有効かどうかを検証します。アテンションマネージャを使用しないアプリケーションは通常この時点でサウンドを鳴らします。
    • 次に、ペンディング状態の全てのアラームの最初の通知が受信された後、別の通知が各アプリケーションに送信され、アプリケーションがなんらかの UI を表示できるようにします。

アラームマネージャ自身には UI がありません。受け取ったアラームでユーザーの注意を惹きたいアプリケーションは、それを自分でする必要があります。アラームマネージャはリマインダダイアログを提供しませんし、アラームサウンドを鳴らすこともしません。Palm OS 4.0 で動作しているアプリケーションは、ユーザーとのやりとりにアテンションマネージャを使用するべきです。その方法については、323 ページの“アテンションとアラーム”を参照して下さい。もっと古いバージョンの Palm OS で動作しているアプリケーションは UI を独自に用意する必要があります。その方法については以下のセクションで説明します。

IMPORTANT
ハンドヘルドがスリープ状態にある場合、アラームは数分遅れる場合があります。これはプロシージャアラームを使用している場合は特に重要です(“プロシージャアラームの設定”を参照 )。プロシージャアラームはスリープモード中でも実行されますが、頻度は下がる場合があります。

 

アラームの設定

アラームマネージャのもっとも一般的な使用方法は、アプリケーションでリアルタイムアラームを設定する場合です。時として、アプリケーションはユーザーにイベントを知らせるためにこの種のアラームを設定したい場合があります。例えば、Datebook アプリケーションは予定をユーザーに知らせるためにアラームを設定します。

このようなアラームは2段階の処理で実装できます。まず、AlmSetAlarm 関数を使用してアラームをセットします。このとき、いつアラームを発生させ、どのアプリケーションに知らせるかを指定します。

リスト 10.5 に、Datebook アプリケーションがどのようにアラームを設定しているかを示します。

リスト 10.5 アラームの設定

static void SetTimeOfNextAlarm (UInt32 alarmTime, UInt32 ref)
{
    UInt16 cardNo;
    LocalID dbID;
    DmSearchStateType searchInfo;
    DmGetNextDatabaseByTypeCreator (true, &searchInfo,
                                    sysFileTApplication, sysFileCDatebook,
                                    true, &cardNo, &dbID);
    AlmSetAlarm (cardNo, dbID, ref, alarmTime, true);
}

次に、PilotMain 関数が起動コード sysAppLaunchCmdAlarmTriggered および sysAppLaunchCmdDisplayAlarm に応答するようにします。

アラームがトリガーされると、アラームマネージャはその時間にアラームをセットしたアプリケーションに対して sysAppLaunchCmdAlarmTriggered 起動コードを介して通知を行ないます。それぞれのアプリケーションがこの起動コードの処理を終えると、アラームマネージャは各アプリケーションがアラームを表示できるよう、sysAppLaunchCmdDisplayAlarm 起動コードを送信します。セクション“アラームのシナリオ”では、これらの起動コードの受信と、その際アプリケーションが何をすべきかについてより詳細な説明をしています。これらの起動コードに対する応答の具体的な例については、Palm OS 3.5 に含まれる Datebook を参照して下さい。

また、以下の注意点も重要になります。

  • アプリケーションが設定できるアラームは1度に1つまでです。AlmSetAlarm をコールしてからそのアラームがトリガーされる前に再度コールすると、アラームマネージャは最初に設定されたアラームを2回目のアラームで置き換えます。アプリケーションでペンディングになっているアラームがあるかどうかは AlmGetAlarm 関数を使えばわかります。
  • 起動コードに応答する場合、( マルチセグメントアプリケーションでは )セグメント 0 の外側にある大域変数やコードにアクセスしないで下さい。AlmSetAlarm 関数は、アラームがトリガーされたときにアクセスできるように UInt32 型のパラメータを指定することができます( リスト 10.5 の ref パラメータがそれです )。これら両方の起動コードにおけるパラメータブロックは、このパラメータに対するアクセスを提供します。この参照パラメータで不十分であれば、アプリケーションフィーチャを定義することもできます。詳細は“Palm のシステムサポート”の“フィーチャ”を参照して下さい。
  • AlmSetAlarm 関数に渡すデータベース ID はアプリケーション( .prc ファイル )のローカル ID であり、アプリケーションがアクセスするレコードデータベースの ID ではありません。レコードデータベースの ID はアプリケーションのローカル ID よりも使用頻度が高いため、これは発生しがちなミスになっています。
  • AlmSetAlarm では、アラームの時刻は 1904/1/1 からの秒数として指定します。従来の日時表現をこの値に変換するには、TimDateTimeToSeconds 関数を使用します。

ペンディングになっているアラームをクリアするには、AlmSetAlarm をコールして日時を示すパラメータに 0 を渡します。

 

アラームのシナリオ

アラームの処理においてアプリケーションとアラームマネージャが通常どのようにやりとりをするかについて、以下に示します。

  1. アプリケーションは AlmSetAlarm を使用してアラームを設定します。アラームマネージャは新しいアラームをアラームキューに追加します。アラームキューには全てのアラームリクエストが含まれています。アラームを作成したアプリケーションにアラームマネージャが起動コードを送信できるようになるまで、トリガーされたアラームはキューに留まります。ただし、アラームキューが一杯になってしまうと、トリガーと通知の済んだエントリのうちもっとも古いものが削除され、新しいアラームのための場所を用意します。
  2. アラームの時刻が到来すると、アラームマネージャはアラームキューの中からその時刻にアラームを設定している最初のアプリケーションを探します。
  3. アラームマネージャはこのアプリケーションに sysAppLaunchCmdAlarmTriggered 起動コードを送信します。
  4. アプリケーションはこの時点で、 次のアラームを設定する、 短いサウンドを再生する、 なんらかの簡単なメンテナンス動作を行う、ことができます。アプリケーションは sysAppLaunchCmdAlarmTriggered に対する応答で時間のかかる処理を実行すべきではありません。なぜなら、同じ時刻にアラームを設定している他のアプリケーションがアラームを受信するのを遅させることになるからです。アプリケーションがそのアラームをユーザーに提示するためにアテンションマネージャを使用しているのであれば、この時点で AttnGetAttention をコールして、復帰前に起動コードのパラメータブロックの purgeAlarm フィールドに true を設定します。このアラームにそれ以上の処理を必要としない場合、アプリケーションは復帰前に起動コードの purgeAlarm フィールドに true を設定しなければなりません。これによりキューがアラームから削除され、sysAppLaunchCmdDisplayAlarm 起動コードを受けとることがなくなります。
  5. アラームマネージャは同じ時刻にアラームを設定している次のアプリケーションを探し、ステップ 2 と 3 を繰り返します。
  6. このプロセスを同時刻にアラームを設定しているアプリケーションが見つからなくなるまで繰り返します。
  7. アラームマネージャはもう一度同時刻にアラームを設定しているアプリケーションを最初から検索し、これらのアプリケーションに起動コード sysAppLaunchCmdDisplayAlarm を送信します。ただし、sysAppLaunchCmdAlarmTriggered の処理中に purgeAlarm フィールドに true を設定されたアラーム ―― アテンションマネージャを介してユーザーに提示されることになる全てのアラームも含めて ―― はこの時点でキューには存在しません。
  8. アプリケーションはこの時点で、 ダイアログボックスを表示する、 別のタイプのリマインダを表示する、ことができます。通常アプリケーションはこの時点でダイアログイベントを処理するためのネストされたイベントループに入ります。このネストされたイベントループはダイアログを破棄することになるイベントを事実上全て無視するため、ダイアログはスクリーン上に固定されることになります。ダイアログ上のボタンをタップするまでは、そのダイアログを破棄することはできません。その時点でアクティブだったアプリケーションはアクティブなままですが、(アラームダイアログがフルスクリーンの場合は)ユーザーはアプリケーションを見ることはできませんし、ダイアログがネストされたイベントループで全てのイベントを処理しているため、アプリケーションを操作することもできません。実質的にサスペンド状態になっていて、イベントの発生を待機しています。
  9. アラームマネージャはアラームがトリガーされるように設定した次のアプリケーションを検索し、ステップ 7 と 8 を繰り返します。
  10. 同時刻にアラームをセットしたアプリケーションが見つからなくなるまでこのプロセスが繰り返されます。古いアラームが画面に表示されている状態で新たなアラームがトリガーされた場合、この新しいアラーム時刻にアラームを設定している全てのアプリケーションに sysAppLaunchCmdAlarmTriggered 起動コードが送信されます。しかし、アラーム表示のサイクルは、最初のアラーム表示が完了するまで延期されます。

最初のアラームを破棄する前に次のアラームをオフにした場合、アラームマネージャは sysAppLaunchCmdAlarmTriggered 起動コードを送信しますが、sysAppLaunchCmdDisplayAlarm の送信は最初のアラームのダイアログが破棄されるまで待ちます。ダイアログを表示するアプリケーションの場合、これは通常一度に1つのダイアログしか表示されないことを意味します。アラームマネージャは起動コードの送信中にイベントループに戻らないため、最初のアラームダイアログが破棄されると、次のアラームダイアログがただちに表示されることになります。ユーザーから見れば、最終的には個々のアラームダイアログを順番に全て破棄しなければハンドヘルドが使えないことになります。

 

プロシージャアラームの設定

Palm OS 3.2 より、先のセクションで説明したアプリケーションベースのアラームに加え、プロシージャアラームの設定がシステムによりサポートされるようにになりました。プロシージャアラームとアプリケーションベースのアラームの違いは以下の通りです。

  • プロシージャアラームを設定する場合、通知を受けるべきアプリケーションのかわりに、アラームがトリガーされた際にコールされるべき関数のポインタを指定します。
  • アラームがトリガーされると、アラームマネージャは起動コードを使用するかわりに指定された関数をダイレクトにコールします。
  • システムがスリープモードにある場合でも、アラームは LCD をライトアップすることなくトリガーされます。

プロシージャアラームを使用するのは、以下のような状況です。

  • ユーザーの目にまったく触れないようなバックグラウンドタスクを実行したい場合。
  • シェアードライブラリを書いていて、その中にアラームを実装したい場合。
  • AttnUpdate を使用してアテンション項目を更新したいが、ハンドヘルドがスリープ状態の場合はディスプレイをライトアップしたくない場合

プロシージャアラームを設定するには、AlmSetAlarm のかわりに AlmSetProcAlarm を使用します( 同様に、このプロシージャでペンディングになっているアラームがあるかどうかを確認するには AlmGetAlarm のかわりに AlmGetProcAlarm を使用します )。

AlmSetProcAlarm は現状ではマクロとして実装されており、カード番号パラメータに特殊な値を使用して AlmSetAlarm をコールして、アラームマネージャにそれがプロシージャアラームであることを伝えます。アプリケーションのローカル ID とカード番号のかわりに、関数ポインタを指定します。その他のルールは AlmSetAlarm と同じです。特に、同一の関数はペンディング状態のアラームを同時に1つまでしか持てませんし、アラーム時刻に 0 を指定することでペンディング状態のアラームをクリアすることができます。

アラームがトリガーされると、アラームマネージャは指定された関数をコールします。この関数は以下のようなプロトタイプになっている必要があります。

void myAlarmFunc (UInt16 almProcCmd,
                  SysAlarmTriggeredParamType *paramP)
IMPORTANT
この関数ポインタは、AlmSetProcAlarm がコールされてからアラームがトリガーされる時点まで有効であり続ける必要があります。プロシージャがシェアードライブラリにある場合、ライブラリがオープンされた状態のままになっている必要があります。プロシージャが分割してロードされるコードリソースにある場合。そのリソースはアラームが発動されるされるまでロックされている必要があります。ライブラリをクローズしたりリソースをアンロックする場合、ペンディング状態のアラームを全て解除する必要があります。さもないと、アラームがトリガーされた時点でシステムがクラッシュすることになります。

プロシージャ関数の最初のパラメータには、アラームマネージャがその関数をコールした理由が指定されます。現在、アラームマネージャはこの関数を以下の2つの値でコールします。

  • アラームがトリガーされた場合。
  • ユーザーがシステム時刻を変更したため、アラーム時刻を調整しなければならない場合。

2つめのパラメータは、sysAppLaunchCmdAlarmTriggered 起動コードにおいて渡されるものと同一の構造体です。これはアラームを設定した時に指定された参照パラメータ、アラームを設定した時刻、およびアラームをキューから削除すべきかどうかを指定する purgeAlarm フィールドへのアクセスを提供します。プロシージャアラームの場合、アラームは常にキューから削除されます。システムはプロシージャアラーム関数をコールした後で、purgeAlarm に true を設定します。

プロシージャアラーム関数を含むアプリケーションが終了した後にアラームがトリガーされた場合、プロシージャアラーム関数は( たとえコードがメモリ上でロックされていたとしても )グローバル変数にはアクセスすることができない点に注意して下さい。アプリケーションが終了した時点で、大域はすでに存在していないからです。

 

プロシージャアラームとメニュー

プロシージャアラームは時としてハンドヘルドのディスプレイを定期的に更新するのに使用される場合があります。アラームがトリガーされた際、メニューには自動的に通知が行なわれないため、プロシージャアラームのコードがディスプレイを更新した際にメニューがオープンされていると、メニューを上書きすることになってしまいます。メニューがオープンされているかどうかを検出する方法については、“メニューの可視性チェック”で説明しているように winExitEvent と winEnterEvent を使用します。

 


← 1 節に戻る ↑10 章トップへ 3 節に進む →