diff options
19 files changed, 1046 insertions, 1578 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e9c91587..7afa87c1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -42,8 +42,9 @@ <service android:name="CellBroadcastConfigService" android:exported="false" /> - <service android:name="CellBroadcastDatabaseService" - android:exported="false" /> + <provider android:name="CellBroadcastContentProvider" + android:authorities="cellbroadcasts" + android:readPermission="android.permission.READ_CELL_BROADCASTS" /> <activity android:name="CellBroadcastListActivity" android:label="@string/app_label" diff --git a/res/values/strings.xml b/res/values/strings.xml index 420bf91c..9677fe66 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -42,8 +42,6 @@ <string name="menu_delete">Delete broadcast</string> <!-- Confirm Delete --> - <!-- Delete confirmation dialog title. [CHAR LIMIT=30] --> - <string name="confirm_dialog_title">Delete</string> <!-- Delete broadcast confirmation dialog message. [CHAR LIMIT=NONE] --> <string name="confirm_delete_broadcast">Delete this broadcast?</string> <!-- Delete all broadcasts confirmation dialog message. [CHAR LIMIT=NONE] --> diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java index ac5055d4..f817fc95 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java @@ -98,7 +98,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI public void handleMessage(Message msg) { switch (msg.what) { case ALERT_SOUND_FINISHED: - if (DBG) Log.v(TAG, "ALERT_SOUND_FINISHED"); + if (DBG) log("ALERT_SOUND_FINISHED"); stop(); // stop alert sound // if we can speak the message text if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) { @@ -112,9 +112,9 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI break; case ALERT_PAUSE_FINISHED: - if (DBG) Log.v(TAG, "ALERT_PAUSE_FINISHED"); + if (DBG) log("ALERT_PAUSE_FINISHED"); if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) { - if (DBG) Log.v(TAG, "Speaking broadcast text: " + mMessageBody); + if (DBG) log("Speaking broadcast text: " + mMessageBody); mTts.speak(mMessageBody, TextToSpeech.QUEUE_FLUSH, null); mState = STATE_SPEAKING; } else { @@ -147,7 +147,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI */ @Override public void onInit(int status) { - if (DBG) Log.v(TAG, "onInit() TTS engine status: " + status); + if (DBG) log("onInit() TTS engine status: " + status); if (status == TextToSpeech.SUCCESS) { mTtsEngineReady = true; // try to set the TTS language to match the broadcast @@ -165,14 +165,14 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI */ private void setTtsLanguage() { if (mMessageLanguage != null) { - if (DBG) Log.v(TAG, "Setting TTS language to '" + mMessageLanguage + '\''); + if (DBG) log("Setting TTS language to '" + mMessageLanguage + '\''); int result = mTts.setLanguage(new Locale(mMessageLanguage)); // success values are >= 0, failure returns negative value - if (DBG) Log.v(TAG, "TTS setLanguage() returned: " + result); + if (DBG) log("TTS setLanguage() returned: " + result); mTtsLanguageSupported = result >= 0; } else { // try to use the default TTS language for broadcasts with no language specified - if (DBG) Log.v(TAG, "No language specified in broadcast: using default"); + if (DBG) log("No language specified in broadcast: using default"); mTtsLanguageSupported = true; } } @@ -265,7 +265,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI // stop() checks to see if we are already playing. stop(); - if (DBG) Log.v(TAG, "play()"); + if (DBG) log("play()"); // future optimization: reuse media player object mMediaPlayer = new MediaPlayer(); @@ -329,7 +329,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI * Stops alert audio and speech. */ public void stop() { - if (DBG) Log.v(TAG, "stop()"); + if (DBG) log("stop()"); mHandler.removeMessages(ALERT_SOUND_FINISHED); mHandler.removeMessages(ALERT_PAUSE_FINISHED); @@ -350,4 +350,8 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI mAudioManager.abandonAudioFocus(null); mState = STATE_IDLE; } + + private static void log(String msg) { + Log.d(TAG, msg); + } } diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertDialog.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertDialog.java index e762678a..3be8439d 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertDialog.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertDialog.java @@ -16,15 +16,12 @@ package com.android.cellbroadcastreceiver; -import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.util.Log; +import android.telephony.CellBroadcastMessage; /** * Custom alert dialog with optional flashing warning icon. @@ -32,7 +29,6 @@ import android.util.Log; * Keyguard handling based on {@code AlarmAlert} class from DeskClock app. */ public class CellBroadcastAlertDialog extends CellBroadcastAlertFullScreen { - private static final String TAG = "CellBroadcastAlertDialog"; private BroadcastReceiver mScreenOffReceiver; diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java index ea6c5fcb..641fad21 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java @@ -24,6 +24,8 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.Telephony; +import android.telephony.CellBroadcastMessage; import android.view.LayoutInflater; import android.view.View; import android.view.Window; @@ -74,12 +76,12 @@ public class CellBroadcastAlertFullScreen extends Activity { @Override public void handleMessage(Message msg) { if (mIconAnimationState) { - mWarningIconView.setAlpha(255); + mWarningIconView.setImageAlpha(255); if (!mStopAnimation) { mAnimationHandler.sendEmptyMessageDelayed(0, WARNING_ICON_ON_DURATION_MSEC); } } else { - mWarningIconView.setAlpha(0); + mWarningIconView.setImageAlpha(0); if (!mStopAnimation) { mAnimationHandler.sendEmptyMessageDelayed(0, WARNING_ICON_OFF_DURATION_MSEC); } @@ -127,10 +129,11 @@ public class CellBroadcastAlertFullScreen extends Activity { setContentView(inflater.inflate(getLayoutResId(), null)); /* Initialize dialog text from alert message. */ - int titleId = message.getDialogTitleResource(); + int titleId = CellBroadcastResources.getDialogTitleResource(message); setTitle(titleId); ((TextView) findViewById(R.id.alertTitle)).setText(titleId); - ((TextView) findViewById(R.id.message)).setText(message.getFormattedMessageBody(this)); + ((TextView) findViewById(R.id.message)).setText( + CellBroadcastResources.getFormattedMessageBody(this, message)); /* dismiss button: close notification */ findViewById(R.id.dismissButton).setOnClickListener( @@ -176,13 +179,17 @@ public class CellBroadcastAlertFullScreen extends Activity { // Stop playing alert sound/vibration/speech (if started) stopService(new Intent(this, CellBroadcastAlertAudio.class)); - // Start database service to mark broadcast as read - Intent intent = new Intent(this, CellBroadcastDatabaseService.class); - intent.setAction(CellBroadcastDatabaseService.ACTION_MARK_BROADCAST_READ); - // Select by delivery time because we don't know the database row ID. - intent.putExtra(CellBroadcastDatabaseService.DATABASE_DELIVERY_TIME_EXTRA, - mMessage.getDeliveryTime()); - startService(intent); + final long deliveryTime = mMessage.getDeliveryTime(); + + // Mark broadcast as read on a background thread. + new CellBroadcastContentProvider.AsyncCellBroadcastTask(getContentResolver()) + .execute(new CellBroadcastContentProvider.CellBroadcastOperation() { + @Override + public boolean execute(CellBroadcastContentProvider provider) { + return provider.markBroadcastRead( + Telephony.CellBroadcasts.DELIVERY_TIME, deliveryTime); + } + }); if (mIsEmergencyAlert) { // stop animating emergency alert icon diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java index a81eb32c..6f510a3d 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java @@ -29,6 +29,7 @@ import android.os.IBinder; import android.os.PowerManager; import android.preference.PreferenceManager; import android.provider.Telephony; +import android.telephony.CellBroadcastMessage; import android.telephony.SmsCbCmasInfo; import android.telephony.SmsCbMessage; import android.util.Log; @@ -101,11 +102,14 @@ public class CellBroadcastAlertService extends Service { addToNotificationBar(cbm); } - // write to database on a separate service thread - Intent dbWriteIntent = new Intent(this, CellBroadcastDatabaseService.class); - dbWriteIntent.setAction(CellBroadcastDatabaseService.ACTION_INSERT_NEW_BROADCAST); - dbWriteIntent.putExtra(CellBroadcastMessage.SMS_CB_MESSAGE_EXTRA, cbm); - startService(dbWriteIntent); + // write to database on a background thread + new CellBroadcastContentProvider.AsyncCellBroadcastTask(getContentResolver()) + .execute(new CellBroadcastContentProvider.CellBroadcastOperation() { + @Override + public boolean execute(CellBroadcastContentProvider provider) { + return provider.insertNewBroadcast(cbm); + } + }); } /** @@ -186,7 +190,7 @@ public class CellBroadcastAlertService extends Service { audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_DURATION_EXTRA, Integer.parseInt(duration)); - int channelTitleId = message.getDialogTitleResource(); + int channelTitleId = CellBroadcastResources.getDialogTitleResource(message); CharSequence channelName = getText(channelTitleId); String messageBody = message.getMessageBody(); @@ -222,7 +226,7 @@ public class CellBroadcastAlertService extends Service { Notification.Builder builder = new Notification.Builder(this) .setSmallIcon(R.drawable.stat_color_warning) - .setTicker(getText(message.getDialogTitleResource())) + .setTicker(getText(CellBroadcastResources.getDialogTitleResource(message))) .setWhen(System.currentTimeMillis()) .setContentIntent(pi) .setFullScreenIntent(pi, true) @@ -242,7 +246,7 @@ public class CellBroadcastAlertService extends Service { * @param message the alert to display */ private void addToNotificationBar(CellBroadcastMessage message) { - int channelTitleId = message.getDialogTitleResource(); + int channelTitleId = CellBroadcastResources.getDialogTitleResource(message); CharSequence channelName = getText(channelTitleId); String messageBody = message.getMessageBody(); diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java index 53674ebe..ad77dc25 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java @@ -58,19 +58,19 @@ public class CellBroadcastConfigService extends IntentService { int startId = Integer.decode(channelRange.substring(0, dashIndex)); int endId = Integer.decode(channelRange.substring(dashIndex + 1)); if (enable) { - if (DBG) Log.d(TAG, "enabling emergency IDs " + startId + '-' + endId); + if (DBG) log("enabling emergency IDs " + startId + '-' + endId); manager.enableCellBroadcastRange(startId, endId); } else { - if (DBG) Log.d(TAG, "disabling emergency IDs " + startId + '-' + endId); + if (DBG) log("disabling emergency IDs " + startId + '-' + endId); manager.disableCellBroadcastRange(startId, endId); } } else { int messageId = Integer.decode(channelRange); if (enable) { - if (DBG) Log.d(TAG, "enabling emergency message ID " + messageId); + if (DBG) log("enabling emergency message ID " + messageId); manager.enableCellBroadcast(messageId); } else { - if (DBG) Log.d(TAG, "disabling emergency message ID " + messageId); + if (DBG) log("disabling emergency message ID " + messageId); manager.disableCellBroadcast(messageId); } } @@ -126,7 +126,7 @@ public class CellBroadcastConfigService extends IntentService { SmsManager manager = SmsManager.getDefault(); if (enableEmergencyAlerts) { - if (DBG) Log.d(TAG, "enabling emergency cell broadcast channels"); + if (DBG) log("enabling emergency cell broadcast channels"); if (!TextUtils.isEmpty(emergencyIdRange)) { setChannelRange(manager, emergencyIdRange, true); } else { @@ -135,10 +135,10 @@ public class CellBroadcastConfigService extends IntentService { SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER, SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER); } - if (DBG) Log.d(TAG, "enabled emergency cell broadcast channels"); + if (DBG) log("enabled emergency cell broadcast channels"); } else { // we may have enabled these channels previously, so try to disable them - if (DBG) Log.d(TAG, "disabling emergency cell broadcast channels"); + if (DBG) log("disabling emergency cell broadcast channels"); if (!TextUtils.isEmpty(emergencyIdRange)) { setChannelRange(manager, emergencyIdRange, false); } else { @@ -147,21 +147,25 @@ public class CellBroadcastConfigService extends IntentService { SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER, SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER); } - if (DBG) Log.d(TAG, "disabled emergency cell broadcast channels"); + if (DBG) log("disabled emergency cell broadcast channels"); } if (enableChannel50Alerts) { - if (DBG) Log.d(TAG, "enabling cell broadcast channel 50"); + if (DBG) log("enabling cell broadcast channel 50"); manager.enableCellBroadcast(50); - if (DBG) Log.d(TAG, "enabled cell broadcast channel 50"); + if (DBG) log("enabled cell broadcast channel 50"); } else { - if (DBG) Log.d(TAG, "disabling cell broadcast channel 50"); + if (DBG) log("disabling cell broadcast channel 50"); manager.disableCellBroadcast(50); - if (DBG) Log.d(TAG, "disabled cell broadcast channel 50"); + if (DBG) log("disabled cell broadcast channel 50"); } } catch (Exception ex) { Log.e(TAG, "exception enabling cell broadcast channels", ex); } } } + + private static void log(String msg) { + Log.d(TAG, msg); + } } diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java b/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java new file mode 100644 index 00000000..7c26c354 --- /dev/null +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cellbroadcastreceiver; + +import android.content.ContentProvider; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.Telephony; +import android.telephony.CellBroadcastMessage; +import android.text.TextUtils; +import android.util.Log; + +/** + * ContentProvider for the database of received cell broadcasts. + */ +public class CellBroadcastContentProvider extends ContentProvider { + private static final String TAG = "CellBroadcastContentProvider"; + + /** URI matcher for ContentProvider queries. */ + private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + + /** Authority string for content URIs. */ + static final String CB_AUTHORITY = "cellbroadcasts"; + + /** Content URI for notifying observers. */ + static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts/"); + + /** URI matcher type to get all cell broadcasts. */ + private static final int CB_ALL = 0; + + /** URI matcher type to get a cell broadcast by ID. */ + private static final int CB_ALL_ID = 1; + + /** MIME type for the list of all cell broadcasts. */ + private static final String CB_LIST_TYPE = "vnd.android.cursor.dir/cellbroadcast"; + + /** MIME type for an individual cell broadcast. */ + private static final String CB_TYPE = "vnd.android.cursor.item/cellbroadcast"; + + static { + sUriMatcher.addURI(CB_AUTHORITY, null, CB_ALL); + sUriMatcher.addURI(CB_AUTHORITY, "#", CB_ALL_ID); + } + + /** The database for this content provider. */ + private SQLiteOpenHelper mOpenHelper; + + /** + * Initialize content provider. + * @return true if the provider was successfully loaded, false otherwise + */ + @Override + public boolean onCreate() { + mOpenHelper = new CellBroadcastDatabaseHelper(getContext()); + return true; + } + + /** + * Return a cursor for the cell broadcast table. + * @param uri the URI to query. + * @param projection the list of columns to put into the cursor, or null. + * @param selection the selection criteria to apply when filtering rows, or null. + * @param selectionArgs values to replace ?s in selection string. + * @param sortOrder how the rows in the cursor should be sorted, or null to sort from most + * recently received to least recently received. + * @return a Cursor or null. + */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(CellBroadcastDatabaseHelper.TABLE_NAME); + + int match = sUriMatcher.match(uri); + switch (match) { + case CB_ALL: + // get all broadcasts + break; + + case CB_ALL_ID: + // get broadcast by ID + qb.appendWhere("(_id=" + uri.getPathSegments().get(0) + ')'); + break; + + default: + Log.e(TAG, "Invalid query: " + uri); + throw new IllegalArgumentException("Unknown URI: " + uri); + } + + String orderBy; + if (!TextUtils.isEmpty(sortOrder)) { + orderBy = sortOrder; + } else { + orderBy = Telephony.CellBroadcasts.DEFAULT_SORT_ORDER; + } + + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); + if (c != null) { + c.setNotificationUri(getContext().getContentResolver(), CONTENT_URI); + } + return c; + } + + /** + * Return the MIME type of the data at the specified URI. + * @param uri the URI to query. + * @return a MIME type string, or null if there is no type. + */ + @Override + public String getType(Uri uri) { + int match = sUriMatcher.match(uri); + switch (match) { + case CB_ALL: + return CB_LIST_TYPE; + + case CB_ALL_ID: + return CB_TYPE; + + default: + return null; + } + } + + /** + * Insert a new row. This throws an exception, as the database can only be modified by + * calling custom methods in this class, and not via the ContentProvider interface. + * @param uri the content:// URI of the insertion request. + * @param values a set of column_name/value pairs to add to the database. + * @return the URI for the newly inserted item. + */ + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("insert not supported"); + } + + /** + * Delete one or more rows. This throws an exception, as the database can only be modified by + * calling custom methods in this class, and not via the ContentProvider interface. + * @param uri the full URI to query, including a row ID (if a specific record is requested). + * @param selection an optional restriction to apply to rows when deleting. + * @return the number of rows affected. + */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("delete not supported"); + } + + /** + * Update one or more rows. This throws an exception, as the database can only be modified by + * calling custom methods in this class, and not via the ContentProvider interface. + * @param uri the URI to query, potentially including the row ID. + * @param values a Bundle mapping from column names to new column values. + * @param selection an optional filter to match rows to update. + * @return the number of rows affected. + */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("update not supported"); + } + + /** + * Internal method to insert a new Cell Broadcast into the database and notify observers. + * @param message the message to insert + * @return true if the database was updated, false otherwise + */ + boolean insertNewBroadcast(CellBroadcastMessage message) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + ContentValues cv = message.getContentValues(); + + long rowId = db.insert(CellBroadcastDatabaseHelper.TABLE_NAME, null, cv); + if (rowId != -1) { + return true; + } else { + Log.e(TAG, "failed to insert new broadcast into database"); + return false; + } + } + + /** + * Internal method to delete a cell broadcast by row ID and notify observers. + * @param rowId the row ID of the broadcast to delete + * @param decrementUnreadCount true to decrement the count of unread alerts + * @return true if the database was updated, false otherwise + */ + boolean deleteBroadcast(long rowId, boolean decrementUnreadCount) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + + int rowCount = db.delete(CellBroadcastDatabaseHelper.TABLE_NAME, + Telephony.CellBroadcasts._ID + "=?", + new String[]{Long.toString(rowId)}); + if (rowCount != 0) { + if (decrementUnreadCount) { + CellBroadcastReceiverApp.decrementUnreadAlertCount(); + } + return true; + } else { + Log.e(TAG, "failed to delete broadcast at row " + rowId); + return false; + } + } + + /** + * Internal method to delete all cell broadcasts and notify observers. + * @return true if the database was updated, false otherwise + */ + boolean deleteAllBroadcasts() { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + + int rowCount = db.delete(CellBroadcastDatabaseHelper.TABLE_NAME, null, null); + if (rowCount != 0) { + CellBroadcastReceiverApp.resetUnreadAlertCount(); + return true; + } else { + Log.e(TAG, "failed to delete all broadcasts"); + return false; + } + } + + /** + * Internal method to mark a broadcast as read and notify observers. The broadcast can be + * identified by delivery time (for new alerts) or by row ID. The caller is responsible for + * decrementing the unread non-emergency alert count, if necessary. + * + * @param columnName the column name to query (ID or delivery time) + * @param columnValue the ID or delivery time of the broadcast to mark read + * @return true if the database was updated, false otherwise + */ + boolean markBroadcastRead(String columnName, long columnValue) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + + ContentValues cv = new ContentValues(1); + cv.put(Telephony.CellBroadcasts.MESSAGE_READ, 1); + + String whereClause = columnName + "=?"; + String[] whereArgs = new String[]{Long.toString(columnValue)}; + + int rowCount = db.update(CellBroadcastDatabaseHelper.TABLE_NAME, cv, whereClause, whereArgs); + if (rowCount != 0) { + return true; + } else { + Log.e(TAG, "failed to mark broadcast read: " + columnName + " = " + columnValue); + return false; + } + } + + /** Callback for users of AsyncCellBroadcastOperation. */ + interface CellBroadcastOperation { + /** + * Perform an operation using the specified provider. + * @param provider the CellBroadcastContentProvider to use + * @return true if any rows were changed, false otherwise + */ + boolean execute(CellBroadcastContentProvider provider); + } + + /** + * Async task to call this content provider's internal methods on a background thread. + * The caller supplies the CellBroadcastOperation object to call for this provider. + */ + static class AsyncCellBroadcastTask extends AsyncTask<CellBroadcastOperation, Void, Void> { + /** Reference to this app's content resolver. */ + private ContentResolver mContentResolver; + + AsyncCellBroadcastTask(ContentResolver contentResolver) { + mContentResolver = contentResolver; + } + + /** + * Perform a generic operation on the CellBroadcastContentProvider. + * @param params the CellBroadcastOperation object to call for this provider + * @return void + */ + @Override + protected Void doInBackground(CellBroadcastOperation... params) { + ContentProviderClient cpc = mContentResolver.acquireContentProviderClient( + CellBroadcastContentProvider.CB_AUTHORITY); + CellBroadcastContentProvider provider = (CellBroadcastContentProvider) + cpc.getLocalContentProvider(); + + if (provider != null) { + try { + boolean changed = params[0].execute(provider); + if (changed) { + Log.d(TAG, "database changed: notifying observers..."); + mContentResolver.notifyChange(CONTENT_URI, null, false); + } + } finally { + cpc.release(); + } + } else { + Log.e(TAG, "getLocalContentProvider() returned null"); + } + + mContentResolver = null; // free reference to content resolver + return null; + } + } +} diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastCursorAdapter.java b/src/com/android/cellbroadcastreceiver/CellBroadcastCursorAdapter.java index 9641c64f..d10f63bb 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastCursorAdapter.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastCursorAdapter.java @@ -18,6 +18,7 @@ package com.android.cellbroadcastreceiver; import android.content.Context; import android.database.Cursor; +import android.telephony.CellBroadcastMessage; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastCursorLoader.java b/src/com/android/cellbroadcastreceiver/CellBroadcastCursorLoader.java deleted file mode 100644 index 2501336b..00000000 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastCursorLoader.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cellbroadcastreceiver; - -import android.content.AsyncTaskLoader; -import android.content.CancellationSignal; -import android.content.Context; -import android.content.OperationCanceledException; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.DBG; - -/** - * Async task loader for Cell Broadcast database cursors. Based on - * {@link android.content.CursorLoader}, which is used to query content providers. - */ -class CellBroadcastCursorLoader extends AsyncTaskLoader<Cursor> { - - static final String TAG = "CellBroadcastCursorLoader"; - static final String TABLE_NAME = "broadcasts"; - - private SQLiteDatabase mDatabase; - - Cursor mCursor; - CancellationSignal mCancellationSignal; - - /** - * Creates an empty cell broadcast cursor loader. The query is hardcoded into the class. - */ - public CellBroadcastCursorLoader(Context context) { - super(context); - } - - /** - * Called on a worker thread to perform the actual load and to return the result of the load - * operation. - * - * @return The result of the load operation. - * @throws android.content.OperationCanceledException - * if the load is canceled during execution. - * @see #isLoadInBackgroundCanceled - * @see #cancelLoadInBackground - * @see #onCanceled - */ - @Override - public Cursor loadInBackground() { - synchronized (this) { - if (isLoadInBackgroundCanceled()) { - throw new OperationCanceledException(); - } - mCancellationSignal = new CancellationSignal(); - } - try { - if (mDatabase == null) { - if (DBG) Log.d(TAG, "loadInBackground: opening SQLite database"); - mDatabase = new CellBroadcastDatabase.DatabaseHelper(getContext()) - .getReadableDatabase(); - } - Cursor cursor = mDatabase.query(false, TABLE_NAME, - CellBroadcastDatabase.Columns.QUERY_COLUMNS, null, null, null, null, - CellBroadcastDatabase.Columns.DELIVERY_TIME + " DESC", null, - mCancellationSignal); - - if (cursor != null) { - // Ensure the cursor window is filled - int count = cursor.getCount(); - if (DBG) Log.d(TAG, "loadInBackground() cursor row count = " + count); - } - return cursor; - } finally { - synchronized (this) { - mCancellationSignal = null; - } - } - } - - @Override - public void cancelLoadInBackground() { - super.cancelLoadInBackground(); - - synchronized (this) { - if (mCancellationSignal != null) { - mCancellationSignal.cancel(); - } - } - } - - /* Runs on the UI thread */ - @Override - public void deliverResult(Cursor cursor) { - if (isReset()) { - if (DBG) Log.d(TAG, "isReset() is true, closing cursor and returning"); - // An async query came in while the loader is stopped - if (cursor != null) { - cursor.close(); - } - return; - } - Cursor oldCursor = mCursor; - mCursor = cursor; - - if (isStarted()) { - if (DBG) Log.d(TAG, "isStarted() is true, delivering result"); - super.deliverResult(cursor); - } else { - if (DBG) Log.d(TAG, "isStarted() is false, not delivering!!!"); - } - - if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { - if (DBG) Log.d(TAG, "closing the old cursor..."); - oldCursor.close(); - } - } - - /** - * Starts an asynchronous query of the database. When the result is ready the callbacks - * will be called on the UI thread. If a previous load has been completed and is still valid - * the result may be passed to the callbacks immediately. - * - * Must be called from the UI thread - */ - @Override - protected void onStartLoading() { - if (mCursor != null) { - Log.d(TAG, "onStartLoading() delivering existing cursor"); - deliverResult(mCursor); - } - if (takeContentChanged() || mCursor == null) { - Log.d(TAG, "onStartLoading() calling forceLoad()"); - forceLoad(); - } - } - - /** - * Must be called from the UI thread - */ - @Override - protected void onStopLoading() { - // Attempt to cancel the current load task if possible. - cancelLoad(); - Log.d(TAG, "onStopLoading() called cancelLoad()"); - } - - @Override - public void onCanceled(Cursor cursor) { - if (cursor != null && !cursor.isClosed()) { - cursor.close(); - Log.d(TAG, "onCanceled() closed the cursor"); - } - } - - @Override - protected void onReset() { - super.onReset(); - - // Ensure the loader is stopped - onStopLoading(); - - if (mCursor != null && !mCursor.isClosed()) { - mCursor.close(); - Log.d(TAG, "onReset() closed the cursor"); - } - mCursor = null; - } -} diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java b/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java deleted file mode 100644 index 0eb48c29..00000000 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cellbroadcastreceiver; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; -import android.telephony.SmsCbCmasInfo; -import android.telephony.SmsCbEtwsInfo; -import android.telephony.SmsCbMessage; -import android.util.Log; - -import com.android.internal.telephony.gsm.SmsCbConstants; - -public class CellBroadcastDatabase { - // package local for efficient access from inner class - static final String TAG = "CellBroadcastDatabase"; - - private CellBroadcastDatabase() {} - - static final String DATABASE_NAME = "cell_broadcasts.db"; - static final String TABLE_NAME = "broadcasts"; - - /** Temporary table for upgrading the database version. */ - static final String TEMP_TABLE_NAME = "old_broadcasts"; - - /** - * Database version 1: initial version - * Database version 2-9: (reserved for OEM database customization) - * Database version 10: adds ETWS and CMAS columns and CDMA support - */ - static final int DATABASE_VERSION = 10; - - static final class Columns implements BaseColumns { - - private Columns() {} - - /** - * Message geographical scope. - * <P>Type: INTEGER</P> - */ - public static final String GEOGRAPHICAL_SCOPE = "geo_scope"; - - /** - * Message serial number. - * <P>Type: INTEGER</P> - */ - public static final String SERIAL_NUMBER = "serial_number"; - - /** - * PLMN of broadcast sender. (SERIAL_NUMBER + PLMN + LAC + CID) uniquely identifies a - * broadcast for duplicate detection purposes. - * <P>Type: TEXT</P> - */ - public static final String PLMN = "plmn"; - - /** - * Location Area (GSM) or Service Area (UMTS) of broadcast sender. Unused for CDMA. - * Only included if Geographical Scope of message is not PLMN wide (01). - * <P>Type: INTEGER</P> - */ - public static final String LAC = "lac"; - - /** - * Cell ID of message sender (GSM/UMTS). Unused for CDMA. Only included when the - * Geographical Scope of message is cell wide (00 or 11). - * <P>Type: INTEGER</P> - */ - public static final String CID = "cid"; - - /** - * Message code (OBSOLETE: merged into SERIAL_NUMBER). - * <P>Type: INTEGER</P> - */ - public static final String V1_MESSAGE_CODE = "message_code"; - - /** - * Message identifier (OBSOLETE: renamed to SERVICE_CATEGORY). - * <P>Type: INTEGER</P> - */ - public static final String V1_MESSAGE_IDENTIFIER = "message_id"; - - /** - * Service category (GSM/UMTS message identifier, CDMA service category). - * <P>Type: INTEGER</P> - */ - public static final String SERVICE_CATEGORY = "service_category"; - - /** - * Message language code. - * <P>Type: TEXT</P> - */ - public static final String LANGUAGE_CODE = "language"; - - /** - * Message body. - * <P>Type: TEXT</P> - */ - public static final String MESSAGE_BODY = "body"; - - /** - * Message delivery time. - * <P>Type: INTEGER (long)</P> - */ - public static final String DELIVERY_TIME = "date"; - - /** - * Has the message been viewed? - * <P>Type: INTEGER (boolean)</P> - */ - public static final String MESSAGE_READ = "read"; - - /** - * Message format (3GPP or 3GPP2). - * <P>Type: INTEGER</P> - */ - public static final String MESSAGE_FORMAT = "format"; - - /** - * Message priority (including emergency). - * <P>Type: INTEGER</P> - */ - public static final String MESSAGE_PRIORITY = "priority"; - - /** - * ETWS warning type (ETWS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String ETWS_WARNING_TYPE = "etws_warning_type"; - - /** - * CMAS message class (CMAS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String CMAS_MESSAGE_CLASS = "cmas_message_class"; - - /** - * CMAS category (CMAS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String CMAS_CATEGORY = "cmas_category"; - - /** - * CMAS response type (CMAS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String CMAS_RESPONSE_TYPE = "cmas_response_type"; - - /** - * CMAS severity (CMAS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String CMAS_SEVERITY = "cmas_severity"; - - /** - * CMAS urgency (CMAS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String CMAS_URGENCY = "cmas_urgency"; - - /** - * CMAS certainty (CMAS alerts only). - * <P>Type: INTEGER</P> - */ - public static final String CMAS_CERTAINTY = "cmas_certainty"; - - /** - * Query for list view adapter. - */ - static final String[] QUERY_COLUMNS = { - _ID, - GEOGRAPHICAL_SCOPE, - PLMN, - LAC, - CID, - SERIAL_NUMBER, - SERVICE_CATEGORY, - LANGUAGE_CODE, - MESSAGE_BODY, - DELIVERY_TIME, - MESSAGE_READ, - MESSAGE_FORMAT, - MESSAGE_PRIORITY, - ETWS_WARNING_TYPE, - CMAS_MESSAGE_CLASS, - CMAS_CATEGORY, - CMAS_RESPONSE_TYPE, - CMAS_SEVERITY, - CMAS_URGENCY, - CMAS_CERTAINTY - }; - } - - /* Column indexes for reading from cursor. */ - - static final int COLUMN_ID = 0; - static final int COLUMN_GEOGRAPHICAL_SCOPE = 1; - static final int COLUMN_PLMN = 2; - static final int COLUMN_LAC = 3; - static final int COLUMN_CID = 4; - static final int COLUMN_SERIAL_NUMBER = 5; - static final int COLUMN_SERVICE_CATEGORY = 6; // was COLUMN_MESSAGE_IDENTIFIER - static final int COLUMN_LANGUAGE_CODE = 7; - static final int COLUMN_MESSAGE_BODY = 8; - static final int COLUMN_DELIVERY_TIME = 9; - static final int COLUMN_MESSAGE_READ = 10; - static final int COLUMN_MESSAGE_FORMAT = 11; - static final int COLUMN_MESSAGE_PRIORITY = 12; - static final int COLUMN_ETWS_WARNING_TYPE = 13; - static final int COLUMN_CMAS_MESSAGE_CLASS = 14; - static final int COLUMN_CMAS_CATEGORY = 15; - static final int COLUMN_CMAS_RESPONSE_TYPE = 16; - static final int COLUMN_CMAS_SEVERITY = 17; - static final int COLUMN_CMAS_URGENCY = 18; - static final int COLUMN_CMAS_CERTAINTY = 19; - - static class DatabaseHelper extends SQLiteOpenHelper { - - DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_NAME + " (" - + Columns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + Columns.GEOGRAPHICAL_SCOPE + " INTEGER," - + Columns.PLMN + " TEXT," - + Columns.LAC + " INTEGER," - + Columns.CID + " INTEGER," - + Columns.SERIAL_NUMBER + " INTEGER," - + Columns.SERVICE_CATEGORY + " INTEGER," - + Columns.LANGUAGE_CODE + " TEXT," - + Columns.MESSAGE_BODY + " TEXT," - + Columns.DELIVERY_TIME + " INTEGER," - + Columns.MESSAGE_READ + " INTEGER," - + Columns.MESSAGE_FORMAT + " INTEGER," - + Columns.MESSAGE_PRIORITY + " INTEGER," - + Columns.ETWS_WARNING_TYPE + " INTEGER," - + Columns.CMAS_MESSAGE_CLASS + " INTEGER," - + Columns.CMAS_CATEGORY + " INTEGER," - + Columns.CMAS_RESPONSE_TYPE + " INTEGER," - + Columns.CMAS_SEVERITY + " INTEGER," - + Columns.CMAS_URGENCY + " INTEGER," - + Columns.CMAS_CERTAINTY + " INTEGER);"); - } - - /** Columns to copy on database upgrade. */ - private static final String[] COLUMNS_V1 = { - Columns.GEOGRAPHICAL_SCOPE, - Columns.SERIAL_NUMBER, - Columns.V1_MESSAGE_CODE, - Columns.V1_MESSAGE_IDENTIFIER, - Columns.LANGUAGE_CODE, - Columns.MESSAGE_BODY, - Columns.DELIVERY_TIME, - Columns.MESSAGE_READ, - }; - - private static final int COLUMN_V1_GEOGRAPHICAL_SCOPE = 0; - private static final int COLUMN_V1_SERIAL_NUMBER = 1; - private static final int COLUMN_V1_MESSAGE_CODE = 2; - private static final int COLUMN_V1_MESSAGE_IDENTIFIER = 3; - private static final int COLUMN_V1_LANGUAGE_CODE = 4; - private static final int COLUMN_V1_MESSAGE_BODY = 5; - private static final int COLUMN_V1_DELIVERY_TIME = 6; - private static final int COLUMN_V1_MESSAGE_READ = 7; - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion == newVersion) { - return; - } - Log.i(TAG, "Upgrading DB from version " + oldVersion + " to " + newVersion); - - if (oldVersion == 1) { - db.beginTransaction(); - try { - // Step 1: rename original table - db.execSQL("DROP TABLE IF EXISTS " + TEMP_TABLE_NAME); - db.execSQL("ALTER TABLE " + TABLE_NAME + " RENAME TO " + TEMP_TABLE_NAME); - - // Step 2: create new table and indices - onCreate(db); - - // Step 3: copy each message into the new table - Cursor cursor = db.query(TEMP_TABLE_NAME, COLUMNS_V1, null, null, null, null, - null); - try { - while (cursor.moveToNext()) { - upgradeMessageV1ToV2(db, cursor); - } - } finally { - cursor.close(); - } - - // Step 4: drop the original table and commit transaction - db.execSQL("DROP TABLE " + TEMP_TABLE_NAME); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - oldVersion = 2; - } - } - - /** - * Upgrades a single broadcast message from version 1 to version 2. - */ - private static void upgradeMessageV1ToV2(SQLiteDatabase db, Cursor cursor) { - int geographicalScope = cursor.getInt(COLUMN_V1_GEOGRAPHICAL_SCOPE); - int updateNumber = cursor.getInt(COLUMN_V1_SERIAL_NUMBER); - int messageCode = cursor.getInt(COLUMN_V1_MESSAGE_CODE); - int messageId = cursor.getInt(COLUMN_V1_MESSAGE_IDENTIFIER); - String languageCode = cursor.getString(COLUMN_V1_LANGUAGE_CODE); - String messageBody = cursor.getString(COLUMN_V1_MESSAGE_BODY); - long deliveryTime = cursor.getLong(COLUMN_V1_DELIVERY_TIME); - boolean isRead = (cursor.getInt(COLUMN_V1_MESSAGE_READ) != 0); - - int serialNumber = ((geographicalScope & 0x03) << 14) - | ((messageCode & 0x3ff) << 4) | (updateNumber & 0x0f); - - ContentValues cv = new ContentValues(16); - cv.put(Columns.GEOGRAPHICAL_SCOPE, geographicalScope); - cv.put(Columns.SERIAL_NUMBER, serialNumber); - cv.put(Columns.SERVICE_CATEGORY, messageId); - cv.put(Columns.LANGUAGE_CODE, languageCode); - cv.put(Columns.MESSAGE_BODY, messageBody); - cv.put(Columns.DELIVERY_TIME, deliveryTime); - cv.put(Columns.MESSAGE_READ, isRead); - cv.put(Columns.MESSAGE_FORMAT, SmsCbMessage.MESSAGE_FORMAT_3GPP); - - int etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN; - int cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; - int cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; - int cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; - int cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; - switch (messageId) { - case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING: - etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE; - break; - - case SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING: - etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI; - break; - - case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING: - etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI; - break; - - case SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE: - etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; - break; - - case SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE: - etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; - cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; - cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; - cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; - break; - - case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: - cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; - break; - } - - if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN - || cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { - cv.put(Columns.MESSAGE_PRIORITY, SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY); - } else { - cv.put(Columns.MESSAGE_PRIORITY, SmsCbMessage.MESSAGE_PRIORITY_NORMAL); - } - - if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN) { - cv.put(Columns.ETWS_WARNING_TYPE, etwsWarningType); - } - - if (cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { - cv.put(Columns.CMAS_MESSAGE_CLASS, cmasMessageClass); - } - - if (cmasSeverity != SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN) { - cv.put(Columns.CMAS_SEVERITY, cmasSeverity); - } - - if (cmasUrgency != SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN) { - cv.put(Columns.CMAS_URGENCY, cmasUrgency); - } - - if (cmasCertainty != SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN) { - cv.put(Columns.CMAS_CERTAINTY, cmasCertainty); - } - - db.insert(TABLE_NAME, null, cv); - } - } -}
\ No newline at end of file diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseHelper.java b/src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseHelper.java new file mode 100644 index 00000000..588b29c3 --- /dev/null +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseHelper.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cellbroadcastreceiver; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.Telephony; +import android.telephony.SmsCbCmasInfo; +import android.telephony.SmsCbEtwsInfo; +import android.telephony.SmsCbMessage; +import android.util.Log; + +import com.android.internal.telephony.gsm.SmsCbConstants; + +/** + * Open, create, and upgrade the cell broadcast SQLite database. Previously an inner class of + * {@code CellBroadcastDatabase}, this is now a top-level class. The column definitions in + * {@code CellBroadcastDatabase} have been moved to {@link Telephony.CellBroadcasts} in the + * framework, to simplify access to this database from third-party apps. + */ +public class CellBroadcastDatabaseHelper extends SQLiteOpenHelper { + + private static final String TAG = "CellBroadcastDatabaseHelper"; + + static final String DATABASE_NAME = "cell_broadcasts.db"; + static final String TABLE_NAME = "broadcasts"; + + /** Temporary table for upgrading the database version. */ + static final String TEMP_TABLE_NAME = "old_broadcasts"; + + /** + * Database version 1: initial version + * Database version 2-9: (reserved for OEM database customization) + * Database version 10: adds ETWS and CMAS columns and CDMA support + * Database version 11: adds delivery time index + */ + static final int DATABASE_VERSION = 11; + + CellBroadcastDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + + Telephony.CellBroadcasts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE + " INTEGER," + + Telephony.CellBroadcasts.PLMN + " TEXT," + + Telephony.CellBroadcasts.LAC + " INTEGER," + + Telephony.CellBroadcasts.CID + " INTEGER," + + Telephony.CellBroadcasts.SERIAL_NUMBER + " INTEGER," + + Telephony.CellBroadcasts.SERVICE_CATEGORY + " INTEGER," + + Telephony.CellBroadcasts.LANGUAGE_CODE + " TEXT," + + Telephony.CellBroadcasts.MESSAGE_BODY + " TEXT," + + Telephony.CellBroadcasts.DELIVERY_TIME + " INTEGER," + + Telephony.CellBroadcasts.MESSAGE_READ + " INTEGER," + + Telephony.CellBroadcasts.MESSAGE_FORMAT + " INTEGER," + + Telephony.CellBroadcasts.MESSAGE_PRIORITY + " INTEGER," + + Telephony.CellBroadcasts.ETWS_WARNING_TYPE + " INTEGER," + + Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS + " INTEGER," + + Telephony.CellBroadcasts.CMAS_CATEGORY + " INTEGER," + + Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE + " INTEGER," + + Telephony.CellBroadcasts.CMAS_SEVERITY + " INTEGER," + + Telephony.CellBroadcasts.CMAS_URGENCY + " INTEGER," + + Telephony.CellBroadcasts.CMAS_CERTAINTY + " INTEGER);"); + + createDeliveryTimeIndex(db); + } + + private void createDeliveryTimeIndex(SQLiteDatabase db) { + db.execSQL("CREATE INDEX IF NOT EXISTS deliveryTimeIndex ON " + TABLE_NAME + + " (" + Telephony.CellBroadcasts.DELIVERY_TIME + ");"); + } + + /** Columns to copy on database upgrade. */ + private static final String[] COLUMNS_V1 = { + Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, + Telephony.CellBroadcasts.SERIAL_NUMBER, + Telephony.CellBroadcasts.V1_MESSAGE_CODE, + Telephony.CellBroadcasts.V1_MESSAGE_IDENTIFIER, + Telephony.CellBroadcasts.LANGUAGE_CODE, + Telephony.CellBroadcasts.MESSAGE_BODY, + Telephony.CellBroadcasts.DELIVERY_TIME, + Telephony.CellBroadcasts.MESSAGE_READ, + }; + + private static final int COLUMN_V1_GEOGRAPHICAL_SCOPE = 0; + private static final int COLUMN_V1_SERIAL_NUMBER = 1; + private static final int COLUMN_V1_MESSAGE_CODE = 2; + private static final int COLUMN_V1_MESSAGE_IDENTIFIER = 3; + private static final int COLUMN_V1_LANGUAGE_CODE = 4; + private static final int COLUMN_V1_MESSAGE_BODY = 5; + private static final int COLUMN_V1_DELIVERY_TIME = 6; + private static final int COLUMN_V1_MESSAGE_READ = 7; + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion == newVersion) { + return; + } + // always log database upgrade + log("Upgrading DB from version " + oldVersion + " to " + newVersion); + + // Upgrade from V1 to V10 + if (oldVersion == 1) { + db.beginTransaction(); + try { + // Step 1: rename original table + db.execSQL("DROP TABLE IF EXISTS " + TEMP_TABLE_NAME); + db.execSQL("ALTER TABLE " + TABLE_NAME + " RENAME TO " + TEMP_TABLE_NAME); + + // Step 2: create new table and indices + onCreate(db); + + // Step 3: copy each message into the new table + Cursor cursor = db.query(TEMP_TABLE_NAME, COLUMNS_V1, null, null, null, null, + null); + try { + while (cursor.moveToNext()) { + upgradeMessageV1ToV2(db, cursor); + } + } finally { + cursor.close(); + } + + // Step 4: drop the original table and commit transaction + db.execSQL("DROP TABLE " + TEMP_TABLE_NAME); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + oldVersion = 10; // skip to version 10 + } + + // Note to OEMs: if you have customized the database schema since V1, you will need to + // add your own code here to convert from your version to version 10. + if (oldVersion < 10) { + throw new SQLiteException("CellBroadcastDatabase doesn't know how to upgrade " + + " DB version " + oldVersion + " (customized by OEM?)"); + } + + if (oldVersion == 10) { + createDeliveryTimeIndex(db); + oldVersion++; + } + } + + /** + * Upgrades a single broadcast message from version 1 to version 2. + */ + private static void upgradeMessageV1ToV2(SQLiteDatabase db, Cursor cursor) { + int geographicalScope = cursor.getInt(COLUMN_V1_GEOGRAPHICAL_SCOPE); + int updateNumber = cursor.getInt(COLUMN_V1_SERIAL_NUMBER); + int messageCode = cursor.getInt(COLUMN_V1_MESSAGE_CODE); + int messageId = cursor.getInt(COLUMN_V1_MESSAGE_IDENTIFIER); + String languageCode = cursor.getString(COLUMN_V1_LANGUAGE_CODE); + String messageBody = cursor.getString(COLUMN_V1_MESSAGE_BODY); + long deliveryTime = cursor.getLong(COLUMN_V1_DELIVERY_TIME); + boolean isRead = (cursor.getInt(COLUMN_V1_MESSAGE_READ) != 0); + + int serialNumber = ((geographicalScope & 0x03) << 14) + | ((messageCode & 0x3ff) << 4) | (updateNumber & 0x0f); + + ContentValues cv = new ContentValues(16); + cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, geographicalScope); + cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, serialNumber); + cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, messageId); + cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, languageCode); + cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, messageBody); + cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, deliveryTime); + cv.put(Telephony.CellBroadcasts.MESSAGE_READ, isRead); + cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, SmsCbMessage.MESSAGE_FORMAT_3GPP); + + int etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN; + int cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; + int cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; + int cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; + int cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; + switch (messageId) { + case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; + break; + } + + if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN + || cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { + cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, + SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY); + } else { + cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, + SmsCbMessage.MESSAGE_PRIORITY_NORMAL); + } + + if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN) { + cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsWarningType); + } + + if (cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { + cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasMessageClass); + } + + if (cmasSeverity != SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN) { + cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasSeverity); + } + + if (cmasUrgency != SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN) { + cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasUrgency); + } + + if (cmasCertainty != SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN) { + cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasCertainty); + } + + db.insert(TABLE_NAME, null, cv); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseService.java deleted file mode 100644 index 2e1d5b52..00000000 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseService.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cellbroadcastreceiver; - -import android.app.IntentService; -import android.content.ContentValues; -import android.content.Intent; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.util.Log; - -import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.DBG; - -/** - * Service to update the SQLite database to add a new broadcast message, - * or to delete one or all previously received broadcasts. - */ -public class CellBroadcastDatabaseService extends IntentService { - private static final String TAG = "CellBroadcastDatabaseService"; - - /** Action to insert a new message (passed as CellBroadcastMessage extra). */ - static final String ACTION_INSERT_NEW_BROADCAST = "ACTION_INSERT_NEW_BROADCAST"; - - /** Action to delete a single broadcast (row ID passed as extra). */ - static final String ACTION_DELETE_BROADCAST = "ACTION_DELETE_BROADCAST"; - - /** Action to mark a broadcast as read by the user (by row ID or delivery time extra). */ - static final String ACTION_MARK_BROADCAST_READ = "ACTION_MARK_BROADCAST_READ"; - - /** Action to delete all broadcasts from database (no extras). */ - static final String ACTION_DELETE_ALL_BROADCASTS = "ACTION_DELETE_ALL_BROADCASTS"; - - /** Identifier for getExtra() for row ID to delete or mark read. */ - public static final String DATABASE_ROW_ID_EXTRA = - "com.android.cellbroadcastreceiver.DATABASE_ROW_ID"; - - /** Identifier for getExtra() for delivery time of broadcast to mark read. */ - public static final String DATABASE_DELIVERY_TIME_EXTRA = - "com.android.cellbroadcastreceiver.DATABASE_DELIVERY_TIME"; - - /** Identifier for getExtra() to tell it to decrement the unread alert count. */ - public static final String DECREMENT_UNREAD_ALERT_COUNT = - "com.android.cellbroadcastreceiver.DECREMENT_UNREAD_ALERT_COUNT"; - - private SQLiteDatabase mBroadcastDb; - - /** Callback for the active list activity when the contents change. */ - private static CellBroadcastListActivity.CursorLoaderListFragment sActiveListFragment; - - public CellBroadcastDatabaseService() { - super(TAG); // use class name for worker thread name - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (mBroadcastDb != null) { - if (DBG) Log.d(TAG, "onDestroy: closing mBroadcastDb..."); - mBroadcastDb.close(); - mBroadcastDb = null; - } - } - - static void setActiveListFragment(CellBroadcastListActivity.CursorLoaderListFragment fragment) { - sActiveListFragment = fragment; - } - - @Override - public void onHandleIntent(Intent intent) { - // open the database on the worker thread - if (mBroadcastDb == null) { - if (DBG) Log.d(TAG, "onHandleIntent: opening mBroadcastDb..."); - CellBroadcastDatabase.DatabaseHelper helper = - new CellBroadcastDatabase.DatabaseHelper(this); - try { - mBroadcastDb = helper.getWritableDatabase(); - } catch (SQLiteException e) { - Log.e(TAG, "SQLite exception trying to open broadcasts db", e); - return; - } - } - - String action = intent.getAction(); - boolean notifyActiveListActivity = false; - if (ACTION_INSERT_NEW_BROADCAST.equals(action)) { - if (DBG) Log.d(TAG, "onHandleIntent: ACTION_INSERT_NEW_BROADCAST"); - CellBroadcastMessage cbm = intent.getParcelableExtra( - CellBroadcastMessage.SMS_CB_MESSAGE_EXTRA); - if (cbm == null) { - Log.e(TAG, "ACTION_INSERT_NEW_BROADCAST with no CB message extra"); - return; - } - - if (DBG) Log.d(TAG, "new broadcast deliveryTime = " + cbm.getDeliveryTime()); - - ContentValues cv = cbm.getContentValues(); - long rowId = mBroadcastDb.insert(CellBroadcastDatabase.TABLE_NAME, null, cv); - if (rowId == -1) { - Log.e(TAG, "failed to insert new broadcast into database!"); - } else { - notifyActiveListActivity = true; - } - } else if (ACTION_DELETE_BROADCAST.equals(action)) { - if (DBG) Log.d(TAG, "onHandleIntent: ACTION_DELETE_BROADCAST"); - long rowId = intent.getLongExtra(DATABASE_ROW_ID_EXTRA, -1); - if (rowId == -1) { - Log.e(TAG, "ACTION_DELETE_BROADCAST missing row ID to delete"); - return; - } - - int rowCount = mBroadcastDb.delete(CellBroadcastDatabase.TABLE_NAME, - CellBroadcastDatabase.Columns._ID + "=?", - new String[]{Long.toString(rowId)}); - if (rowCount != 0) { - notifyActiveListActivity = true; - if (intent.getBooleanExtra(DECREMENT_UNREAD_ALERT_COUNT, false)) { - CellBroadcastReceiverApp.decrementUnreadAlertCount(); - } - } - } else if (ACTION_DELETE_ALL_BROADCASTS.equals(action)) { - if (DBG) Log.d(TAG, "onHandleIntent: ACTION_DELETE_ALL_BROADCASTS"); - mBroadcastDb.delete(CellBroadcastDatabase.TABLE_NAME, null, null); - CellBroadcastReceiverApp.resetUnreadAlertCount(); - notifyActiveListActivity = true; - } else if (ACTION_MARK_BROADCAST_READ.equals(action)) { - if (DBG) Log.d(TAG, "onHandleIntent: ACTION_MARK_BROADCAST_READ"); - long rowId = intent.getLongExtra(DATABASE_ROW_ID_EXTRA, -1); - long deliveryTime = intent.getLongExtra(DATABASE_DELIVERY_TIME_EXTRA, -1); - if (rowId == -1 && deliveryTime == -1) { - Log.e(TAG, "ACTION_MARK_BROADCAST_READ missing row ID or delivery time"); - return; - } - ContentValues cv = new ContentValues(1); - cv.put(CellBroadcastDatabase.Columns.MESSAGE_READ, 1); - - // For new alerts, select by delivery time because the row ID is not known. - String whereClause; - String[] whereArgs; - if (rowId != -1) { - whereClause = CellBroadcastDatabase.Columns._ID + "=?"; - whereArgs = new String[]{Long.toString(rowId)}; - } else { - whereClause = CellBroadcastDatabase.Columns.DELIVERY_TIME + "=?"; - whereArgs = new String[]{Long.toString(deliveryTime)}; - } - - int rowCount = mBroadcastDb.update(CellBroadcastDatabase.TABLE_NAME, cv, - whereClause, whereArgs); - - if (rowCount != 0) { - notifyActiveListActivity = true; - } else { - Log.e(TAG, "MARK_BROADCAST_READ failed, rowId: " + rowId + " deliveryTime: " - + deliveryTime); - } - } else { - Log.e(TAG, "ignoring unexpected Intent with action " + action); - } - if (notifyActiveListActivity && sActiveListFragment != null) { - if (DBG) Log.d(TAG, "notifying databaseContentChanged() for " + action); - sActiveListFragment.databaseContentChanged(); - } - } -} diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastListActivity.java b/src/com/android/cellbroadcastreceiver/CellBroadcastListActivity.java index e32b491a..eb75a1e2 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastListActivity.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastListActivity.java @@ -23,13 +23,15 @@ import android.app.ListFragment; import android.app.LoaderManager; import android.app.NotificationManager; import android.content.Context; +import android.content.CursorLoader; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; -import android.util.Log; +import android.provider.Telephony; +import android.telephony.CellBroadcastMessage; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -41,15 +43,12 @@ import android.view.View.OnCreateContextMenuListener; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.ListView; -import android.widget.TextView; /** * This activity provides a list view of received cell broadcasts. Most of the work is handled * in the inner CursorLoaderListFragment class. */ public class CellBroadcastListActivity extends Activity { - // package local for efficient access from inner class - static final String TAG = "CellBroadcastListActivity"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -91,10 +90,6 @@ public class CellBroadcastListActivity extends Activity { // We have a menu item to show in action bar. setHasOptionsMenu(true); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); } @Override @@ -107,9 +102,6 @@ public class CellBroadcastListActivity extends Activity { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - // Tell database service to notify us when a new broadcast arrives. - CellBroadcastDatabaseService.setActiveListFragment(this); - // Set context menu for long-press. ListView listView = getListView(); listView.setOnCreateContextMenuListener(mOnCreateContextMenuListener); @@ -117,12 +109,10 @@ public class CellBroadcastListActivity extends Activity { // Create a cursor adapter to display the loaded data. mAdapter = new CellBroadcastCursorAdapter(getActivity(), null); setListAdapter(mAdapter); - } - @Override - public void onDestroy() { - super.onDestroy(); - CellBroadcastDatabaseService.setActiveListFragment(null); + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); } @Override @@ -146,7 +136,9 @@ public class CellBroadcastListActivity extends Activity { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { - return new CellBroadcastCursorLoader(getActivity()); + return new CursorLoader(getActivity(), CellBroadcastContentProvider.CONTENT_URI, + Telephony.CellBroadcasts.QUERY_COLUMNS, null, null, + Telephony.CellBroadcasts.DELIVERY_TIME + " DESC"); } @Override @@ -164,18 +156,6 @@ public class CellBroadcastListActivity extends Activity { mAdapter.swapCursor(null); } - /** - * Callback from CellBroadcastDatabaseService after content changes. - */ - void databaseContentChanged() { - Loader<Cursor> loader = getLoaderManager().getLoader(0); - if (loader != null) { - loader.onContentChanged(); - } else { - Log.w(TAG, "databaseContentChanged() called, but loader is null"); - } - } - private void showDialogAndMarkRead(CellBroadcastMessage cbm) { // show emergency alerts with the warning icon, but don't play alert tone Intent i = new Intent(getActivity(), CellBroadcastAlertDialog.class); @@ -201,10 +181,10 @@ public class CellBroadcastListActivity extends Activity { switch (item.getItemId()) { case MENU_DELETE: // We need to decrement the unread alert count if deleting unread alert - boolean isUnread = - (cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_READ) == 0); - confirmDeleteThread(cursor.getLong(CellBroadcastDatabase.COLUMN_ID), - isUnread); + boolean isUnread = (cursor.getInt(cursor.getColumnIndexOrThrow( + Telephony.CellBroadcasts.MESSAGE_READ)) == 0); + confirmDeleteThread(cursor.getLong(cursor.getColumnIndexOrThrow( + Telephony.CellBroadcasts._ID)), isUnread); break; case MENU_VIEW: @@ -256,20 +236,14 @@ public class CellBroadcastListActivity extends Activity { */ public static void confirmDeleteThreadDialog(DeleteThreadListener listener, boolean deleteAll, Context context) { - View contents = View.inflate(context, R.layout.delete_broadcast_dialog_view, null); - TextView msg = (TextView)contents.findViewById(R.id.message); - msg.setText(deleteAll - ? R.string.confirm_delete_all_broadcasts - : R.string.confirm_delete_broadcast); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.confirm_dialog_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setCancelable(true) - .setPositiveButton(R.string.button_delete, listener) - .setNegativeButton(R.string.button_cancel, null) - .setView(contents) - .show(); + builder.setIcon(android.R.drawable.ic_dialog_alert) + .setCancelable(true) + .setPositiveButton(R.string.button_delete, listener) + .setNegativeButton(R.string.button_cancel, null) + .setMessage(deleteAll ? R.string.confirm_delete_all_broadcasts + : R.string.confirm_delete_broadcast) + .show(); } public class DeleteThreadListener implements OnClickListener { @@ -283,23 +257,20 @@ public class CellBroadcastListActivity extends Activity { @Override public void onClick(DialogInterface dialog, int whichButton) { - // delete from database on a separate service thread - Intent dbWriteIntent = new Intent(getActivity(), - CellBroadcastDatabaseService.class); - if (mRowId != -1) { - dbWriteIntent.setAction(CellBroadcastDatabaseService.ACTION_DELETE_BROADCAST); - dbWriteIntent.putExtra(CellBroadcastDatabaseService.DATABASE_ROW_ID_EXTRA, - mRowId); - if (mIsUnread) { - // decrement unread alert count after delete - dbWriteIntent.putExtra( - CellBroadcastDatabaseService.DECREMENT_UNREAD_ALERT_COUNT, true); - } - } else { - dbWriteIntent.setAction( - CellBroadcastDatabaseService.ACTION_DELETE_ALL_BROADCASTS); - } - getActivity().startService(dbWriteIntent); + // delete from database on a background thread + new CellBroadcastContentProvider.AsyncCellBroadcastTask( + getActivity().getContentResolver()).execute( + new CellBroadcastContentProvider.CellBroadcastOperation() { + @Override + public boolean execute(CellBroadcastContentProvider provider) { + if (mRowId != -1) { + return provider.deleteBroadcast(mRowId, mIsUnread); + } else { + return provider.deleteAllBroadcasts(); + } + } + }); + dialog.dismiss(); } } diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastListItem.java b/src/com/android/cellbroadcastreceiver/CellBroadcastListItem.java index 52aff021..1f7da32a 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastListItem.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastListItem.java @@ -17,7 +17,9 @@ package com.android.cellbroadcastreceiver; import android.content.Context; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.telephony.CellBroadcastMessage; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.util.AttributeSet; @@ -64,9 +66,9 @@ public class CellBroadcastListItem extends RelativeLayout { getResources().getDrawable(R.drawable.list_item_background_read) : getResources().getDrawable(R.drawable.list_item_background_unread); - setBackgroundDrawable(background); + setBackground(background); - mChannelView.setText(message.getDialogTitleResource()); + mChannelView.setText(CellBroadcastResources.getDialogTitleResource(message)); mDateView.setText(message.getDateString(getContext())); mMessageView.setText(formatMessage(message)); } @@ -78,7 +80,7 @@ public class CellBroadcastListItem extends RelativeLayout { // Unread messages are shown in bold if (!message.isRead()) { - buf.setSpan(CellBroadcastMessage.STYLE_BOLD, 0, buf.length(), + buf.setSpan(Typeface.DEFAULT_BOLD, 0, buf.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } return buf; diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastMessage.java b/src/com/android/cellbroadcastreceiver/CellBroadcastMessage.java deleted file mode 100644 index f9ff4ec5..00000000 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastMessage.java +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.cellbroadcastreceiver; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Typeface; -import android.os.Parcel; -import android.os.Parcelable; -import android.telephony.SmsCbCmasInfo; -import android.telephony.SmsCbEtwsInfo; -import android.telephony.SmsCbLocation; -import android.telephony.SmsCbMessage; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.format.DateUtils; -import android.text.style.StyleSpan; - -/** - * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that - * decoded broadcast message objects can be passed between running Services. - * New broadcasts are received by {@link CellBroadcastReceiver}, - * displayed by {@link CellBroadcastAlertService}, and saved to SQLite by - * {@link CellBroadcastDatabaseService}. - */ -public class CellBroadcastMessage implements Parcelable { - - /** Identifier for getExtra() when adding this object to an Intent. */ - public static final String SMS_CB_MESSAGE_EXTRA = - "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE"; - - /** Bold style for formatting CMAS headers and unread message items. */ - static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD); - - /** SmsCbMessage. */ - private final SmsCbMessage mSmsCbMessage; - - private final long mDeliveryTime; - private boolean mIsRead; - - CellBroadcastMessage(SmsCbMessage message) { - mSmsCbMessage = message; - mDeliveryTime = System.currentTimeMillis(); - mIsRead = false; - } - - private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) { - mSmsCbMessage = message; - mDeliveryTime = deliveryTime; - mIsRead = isRead; - } - - private CellBroadcastMessage(Parcel in) { - mSmsCbMessage = new SmsCbMessage(in); - mDeliveryTime = in.readLong(); - mIsRead = (in.readInt() != 0); - } - - /** Parcelable: no special flags. */ - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - mSmsCbMessage.writeToParcel(out, flags); - out.writeLong(mDeliveryTime); - out.writeInt(mIsRead ? 1 : 0); - } - - public static final Parcelable.Creator<CellBroadcastMessage> CREATOR - = new Parcelable.Creator<CellBroadcastMessage>() { - public CellBroadcastMessage createFromParcel(Parcel in) { - return new CellBroadcastMessage(in); - } - - public CellBroadcastMessage[] newArray(int size) { - return new CellBroadcastMessage[size]; - } - }; - - /** - * Create a CellBroadcastMessage from a row in the database. - * @param cursor an open SQLite cursor pointing to the row to read - * @return the new CellBroadcastMessage - */ - public static CellBroadcastMessage createFromCursor(Cursor cursor) { - int geoScope = cursor.getInt(CellBroadcastDatabase.COLUMN_GEOGRAPHICAL_SCOPE); - int serialNum = cursor.getInt(CellBroadcastDatabase.COLUMN_SERIAL_NUMBER); - int category = cursor.getInt(CellBroadcastDatabase.COLUMN_SERVICE_CATEGORY); - String language = cursor.getString(CellBroadcastDatabase.COLUMN_LANGUAGE_CODE); - String body = cursor.getString(CellBroadcastDatabase.COLUMN_MESSAGE_BODY); - int format = cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_FORMAT); - int priority = cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_PRIORITY); - - String plmn = null; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_PLMN)) { - plmn = cursor.getString(CellBroadcastDatabase.COLUMN_PLMN); - } - - int lac = -1; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_LAC)) { - lac = cursor.getInt(CellBroadcastDatabase.COLUMN_LAC); - } - - int cid = -1; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CID)) { - cid = cursor.getInt(CellBroadcastDatabase.COLUMN_CID); - } - - SmsCbLocation location = new SmsCbLocation(plmn, lac, cid); - - SmsCbEtwsInfo etwsInfo = null; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_ETWS_WARNING_TYPE)) { - int warningType = cursor.getInt(CellBroadcastDatabase.COLUMN_ETWS_WARNING_TYPE); - etwsInfo = new SmsCbEtwsInfo(warningType, false, false, null); - } - - SmsCbCmasInfo cmasInfo = null; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CMAS_MESSAGE_CLASS)) { - int messageClass = cursor.getInt(CellBroadcastDatabase.COLUMN_CMAS_MESSAGE_CLASS); - - int cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CMAS_CATEGORY)) { - cmasCategory = cursor.getInt(CellBroadcastDatabase.COLUMN_CMAS_CATEGORY); - } - - int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CMAS_RESPONSE_TYPE)) { - responseType = cursor.getInt(CellBroadcastDatabase.COLUMN_CMAS_RESPONSE_TYPE); - } - - int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CMAS_SEVERITY)) { - severity = cursor.getInt(CellBroadcastDatabase.COLUMN_CMAS_SEVERITY); - } - - int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CMAS_URGENCY)) { - urgency = cursor.getInt(CellBroadcastDatabase.COLUMN_CMAS_URGENCY); - } - - int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; - if (!cursor.isNull(CellBroadcastDatabase.COLUMN_CMAS_CERTAINTY)) { - certainty = cursor.getInt(CellBroadcastDatabase.COLUMN_CMAS_CERTAINTY); - } - - cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity, - urgency, certainty); - } - - SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category, - language, body, priority, etwsInfo, cmasInfo); - - long deliveryTime = cursor.getLong(CellBroadcastDatabase.COLUMN_DELIVERY_TIME); - boolean isRead = (cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_READ) != 0); - - return new CellBroadcastMessage(msg, deliveryTime, isRead); - } - - /** - * Return a ContentValues object for insertion into the database. - * @return a new ContentValues object containing this object's data - */ - public ContentValues getContentValues() { - ContentValues cv = new ContentValues(16); - SmsCbMessage msg = mSmsCbMessage; - cv.put(CellBroadcastDatabase.Columns.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope()); - SmsCbLocation location = msg.getLocation(); - if (location.getPlmn() != null) { - cv.put(CellBroadcastDatabase.Columns.PLMN, location.getPlmn()); - } - if (location.getLac() != -1) { - cv.put(CellBroadcastDatabase.Columns.LAC, location.getLac()); - } - if (location.getCid() != -1) { - cv.put(CellBroadcastDatabase.Columns.CID, location.getCid()); - } - cv.put(CellBroadcastDatabase.Columns.SERIAL_NUMBER, msg.getSerialNumber()); - cv.put(CellBroadcastDatabase.Columns.SERVICE_CATEGORY, msg.getServiceCategory()); - cv.put(CellBroadcastDatabase.Columns.LANGUAGE_CODE, msg.getLanguageCode()); - cv.put(CellBroadcastDatabase.Columns.MESSAGE_BODY, msg.getMessageBody()); - cv.put(CellBroadcastDatabase.Columns.DELIVERY_TIME, mDeliveryTime); - cv.put(CellBroadcastDatabase.Columns.MESSAGE_READ, mIsRead); - cv.put(CellBroadcastDatabase.Columns.MESSAGE_FORMAT, msg.getMessageFormat()); - cv.put(CellBroadcastDatabase.Columns.MESSAGE_PRIORITY, msg.getMessagePriority()); - - SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); - if (etwsInfo != null) { - cv.put(CellBroadcastDatabase.Columns.ETWS_WARNING_TYPE, etwsInfo.getWarningType()); - } - - SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); - if (cmasInfo != null) { - cv.put(CellBroadcastDatabase.Columns.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass()); - cv.put(CellBroadcastDatabase.Columns.CMAS_CATEGORY, cmasInfo.getCategory()); - cv.put(CellBroadcastDatabase.Columns.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType()); - cv.put(CellBroadcastDatabase.Columns.CMAS_SEVERITY, cmasInfo.getSeverity()); - cv.put(CellBroadcastDatabase.Columns.CMAS_URGENCY, cmasInfo.getUrgency()); - cv.put(CellBroadcastDatabase.Columns.CMAS_CERTAINTY, cmasInfo.getCertainty()); - } - - return cv; - } - - /** - * Set or clear the "read message" flag. - * @param isRead true if the message has been read; false if not - */ - public void setIsRead(boolean isRead) { - mIsRead = isRead; - } - - public String getLanguageCode() { - return mSmsCbMessage.getLanguageCode(); - } - - public int getServiceCategory() { - return mSmsCbMessage.getServiceCategory(); - } - - public long getDeliveryTime() { - return mDeliveryTime; - } - - public String getMessageBody() { - return mSmsCbMessage.getMessageBody(); - } - - public boolean isRead() { - return mIsRead; - } - - public int getSerialNumber() { - return mSmsCbMessage.getSerialNumber(); - } - - /** - * Returns a styled CharSequence containing the message body and optional CMAS alert headers. - * @param context a Context for resource string access - * @return a CharSequence for display in the broadcast alert dialog - */ - public CharSequence getFormattedMessageBody(Context context) { - if (isCmasMessage()) { - SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); - SpannableStringBuilder buf = new SpannableStringBuilder(); - - // CMAS category - int categoryId = getCmasCategoryResId(cmasInfo); - if (categoryId != 0) { - buf.append(context.getText(R.string.cmas_category_heading)); - buf.append(context.getText(categoryId)); - buf.append('\n'); - } - - // CMAS response type - int responseId = getCmasResponseResId(cmasInfo); - if (responseId != 0) { - buf.append(context.getText(R.string.cmas_response_heading)); - buf.append(context.getText(responseId)); - buf.append('\n'); - } - - // CMAS severity - int severityId = getCmasSeverityResId(cmasInfo); - if (severityId != 0) { - buf.append(context.getText(R.string.cmas_severity_heading)); - buf.append(context.getText(severityId)); - buf.append('\n'); - } - - // CMAS urgency - int urgencyId = getCmasUrgencyResId(cmasInfo); - if (urgencyId != 0) { - buf.append(context.getText(R.string.cmas_urgency_heading)); - buf.append(context.getText(urgencyId)); - buf.append('\n'); - } - - // CMAS certainty - int certaintyId = getCmasCertaintyResId(cmasInfo); - if (certaintyId != 0) { - buf.append(context.getText(R.string.cmas_certainty_heading)); - buf.append(context.getText(certaintyId)); - buf.append('\n'); - } - - // Style all headings in bold - buf.setSpan(STYLE_BOLD, 0, buf.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - - buf.append(mSmsCbMessage.getMessageBody()); - return buf; - } else { - return mSmsCbMessage.getMessageBody(); - } - } - - /** - * Returns the string resource ID for the CMAS category. - * @return a string resource ID, or 0 if the CMAS category is unknown or not present - */ - private static int getCmasCategoryResId(SmsCbCmasInfo cmasInfo) { - switch (cmasInfo.getCategory()) { - case SmsCbCmasInfo.CMAS_CATEGORY_GEO: - return R.string.cmas_category_geo; - - case SmsCbCmasInfo.CMAS_CATEGORY_MET: - return R.string.cmas_category_met; - - case SmsCbCmasInfo.CMAS_CATEGORY_SAFETY: - return R.string.cmas_category_safety; - - case SmsCbCmasInfo.CMAS_CATEGORY_SECURITY: - return R.string.cmas_category_security; - - case SmsCbCmasInfo.CMAS_CATEGORY_RESCUE: - return R.string.cmas_category_rescue; - - case SmsCbCmasInfo.CMAS_CATEGORY_FIRE: - return R.string.cmas_category_fire; - - case SmsCbCmasInfo.CMAS_CATEGORY_HEALTH: - return R.string.cmas_category_health; - - case SmsCbCmasInfo.CMAS_CATEGORY_ENV: - return R.string.cmas_category_env; - - case SmsCbCmasInfo.CMAS_CATEGORY_TRANSPORT: - return R.string.cmas_category_transport; - - case SmsCbCmasInfo.CMAS_CATEGORY_INFRA: - return R.string.cmas_category_infra; - - case SmsCbCmasInfo.CMAS_CATEGORY_CBRNE: - return R.string.cmas_category_cbrne; - - case SmsCbCmasInfo.CMAS_CATEGORY_OTHER: - return R.string.cmas_category_other; - - default: - return 0; - } - } - - /** - * Returns the string resource ID for the CMAS response type. - * @return a string resource ID, or 0 if the CMAS response type is unknown or not present - */ - private static int getCmasResponseResId(SmsCbCmasInfo cmasInfo) { - switch (cmasInfo.getResponseType()) { - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_SHELTER: - return R.string.cmas_response_shelter; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE: - return R.string.cmas_response_evacuate; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_PREPARE: - return R.string.cmas_response_prepare; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EXECUTE: - return R.string.cmas_response_execute; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR: - return R.string.cmas_response_monitor; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_AVOID: - return R.string.cmas_response_avoid; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_ASSESS: - return R.string.cmas_response_assess; - - case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE: - return R.string.cmas_response_none; - - default: - return 0; - } - } - - /** - * Returns the string resource ID for the CMAS severity. - * @return a string resource ID, or 0 if the CMAS severity is unknown or not present - */ - private static int getCmasSeverityResId(SmsCbCmasInfo cmasInfo) { - switch (cmasInfo.getSeverity()) { - case SmsCbCmasInfo.CMAS_SEVERITY_EXTREME: - return R.string.cmas_severity_extreme; - - case SmsCbCmasInfo.CMAS_SEVERITY_SEVERE: - return R.string.cmas_severity_severe; - - default: - return 0; - } - } - - /** - * Returns the string resource ID for the CMAS urgency. - * @return a string resource ID, or 0 if the CMAS urgency is unknown or not present - */ - private static int getCmasUrgencyResId(SmsCbCmasInfo cmasInfo) { - switch (cmasInfo.getUrgency()) { - case SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE: - return R.string.cmas_urgency_immediate; - - case SmsCbCmasInfo.CMAS_URGENCY_EXPECTED: - return R.string.cmas_urgency_expected; - - default: - return 0; - } - } - - /** - * Returns the string resource ID for the CMAS certainty. - * @return a string resource ID, or 0 if the CMAS certainty is unknown or not present - */ - private static int getCmasCertaintyResId(SmsCbCmasInfo cmasInfo) { - switch (cmasInfo.getCertainty()) { - case SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED: - return R.string.cmas_certainty_observed; - - case SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY: - return R.string.cmas_certainty_likely; - - default: - return 0; - } - } - - /** - * Return whether the broadcast is an emergency (PWS) message type. - * This includes lower priority test messages and Amber alerts. - * - * All public alerts show the flashing warning icon in the dialog, - * but only emergency alerts play the alert sound and speak the message. - * - * @return true if the message is PWS type; false otherwise - */ - public boolean isPublicAlertMessage() { - return mSmsCbMessage.isEmergencyMessage(); - } - - /** - * Returns whether the broadcast is an emergency (PWS) message type, - * including test messages, but excluding lower priority Amber alert broadcasts. - * - * @return true if the message is PWS type, excluding Amber alerts - */ - public boolean isEmergencyAlertMessage() { - if (!mSmsCbMessage.isEmergencyMessage()) { - return false; - } - SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); - if (cmasInfo != null && - cmasInfo.getMessageClass() == SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY) { - return false; - } - return true; - } - - /** - * Return whether the broadcast is an ETWS emergency message type. - * @return true if the message is ETWS emergency type; false otherwise - */ - public boolean isEtwsMessage() { - return mSmsCbMessage.isEtwsMessage(); - } - - /** - * Return whether the broadcast is a CMAS emergency message type. - * @return true if the message is CMAS emergency type; false otherwise - */ - public boolean isCmasMessage() { - return mSmsCbMessage.isCmasMessage(); - } - - /** - * Return the CMAS message class. - * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or - * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert - */ - public int getCmasMessageClass() { - if (mSmsCbMessage.isCmasMessage()) { - return mSmsCbMessage.getCmasWarningInfo().getMessageClass(); - } else { - return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; - } - } - - /** - * Return whether the broadcast is an ETWS popup alert. - * This method checks the message ID and the message code. - * @return true if the message indicates an ETWS popup alert - */ - public boolean isEtwsPopupAlert() { - SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); - return etwsInfo != null && etwsInfo.isPopupAlert(); - } - - /** - * Return whether the broadcast is an ETWS emergency user alert. - * This method checks the message ID and the message code. - * @return true if the message indicates an ETWS emergency user alert - */ - public boolean isEtwsEmergencyUserAlert() { - SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); - return etwsInfo != null && etwsInfo.isEmergencyUserAlert(); - } - - /** - * Return whether the broadcast is an ETWS test message. - * @return true if the message is an ETWS test message; false otherwise - */ - public boolean isEtwsTestMessage() { - SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); - return etwsInfo != null && - etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; - } - - public int getDialogTitleResource() { - // ETWS warning types - SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); - if (etwsInfo != null) { - switch (etwsInfo.getWarningType()) { - case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE: - return R.string.etws_earthquake_warning; - - case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI: - return R.string.etws_tsunami_warning; - - case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI: - return R.string.etws_earthquake_and_tsunami_warning; - - case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE: - return R.string.etws_test_message; - - case SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY: - default: - return R.string.etws_other_emergency_type; - } - } - - // CMAS warning types - SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); - if (cmasInfo != null) { - switch (cmasInfo.getMessageClass()) { - case SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT: - return R.string.cmas_presidential_level_alert; - - case SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT: - return R.string.cmas_extreme_alert; - - case SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT: - return R.string.cmas_severe_alert; - - case SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY: - return R.string.cmas_amber_alert; - - case SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST: - return R.string.cmas_required_monthly_test; - - case SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE: - return R.string.cmas_exercise_alert; - - case SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE: - return R.string.cmas_operator_defined_alert; - - default: - return R.string.pws_other_message_identifiers; - } - } - - if (mSmsCbMessage.isEmergencyMessage()) { - return R.string.pws_other_message_identifiers; - } else { - return R.string.cb_other_message_identifiers; - } - } - - /** - * Return the abbreviated date string for the message delivery time. - * @param context the context object - * @return a String to use in the broadcast list UI - */ - String getDateString(Context context) { - int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME | - DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE | - DateUtils.FORMAT_CAP_AMPM; - return DateUtils.formatDateTime(context, mDeliveryTime, flags); - } - - /** - * Return the date string for the message delivery time, suitable for text-to-speech. - * @param context the context object - * @return a String for populating the list item AccessibilityEvent for TTS - */ - String getSpokenDateString(Context context) { - int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE; - return DateUtils.formatDateTime(context, mDeliveryTime, flags); - } -} diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java index 164cd8fc..94d71790 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java @@ -41,7 +41,7 @@ public class CellBroadcastReceiver extends BroadcastReceiver { } protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { - if (DBG) Log.d(TAG, "onReceive " + intent); + if (DBG) log("onReceive " + intent); String action = intent.getAction(); @@ -156,7 +156,7 @@ public class CellBroadcastReceiver extends BroadcastReceiver { */ static void startConfigService(Context context) { if (phoneIsCdma()) { - Log.d(TAG, "CDMA phone detected; doing nothing"); + if (DBG) log("CDMA phone detected; doing nothing"); } else { Intent serviceIntent = new Intent(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, null, context, CellBroadcastConfigService.class); @@ -179,4 +179,8 @@ public class CellBroadcastReceiver extends BroadcastReceiver { } return isCdma; } + + private static void log(String msg) { + Log.d(TAG, msg); + } } diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java index 9a9b3b7e..eb21e17e 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java @@ -27,7 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; * and remembers the time of the most recently received broadcast. */ public class CellBroadcastReceiverApp extends Application { - private static final String LOG_TAG = "CellBroadcastReceiverApp"; + private static final String TAG = "CellBroadcastReceiverApp"; @Override public void onCreate() { @@ -52,7 +52,7 @@ public class CellBroadcastReceiverApp extends Application { */ static void decrementUnreadAlertCount() { if (sUnreadAlertCount.decrementAndGet() < 0) { - Log.e("CellBroadcastReceiverApp", "mUnreadAlertCount < 0, resetting to 0"); + Log.e(TAG, "mUnreadAlertCount < 0, resetting to 0"); sUnreadAlertCount.set(0); } } diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java b/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java new file mode 100644 index 00000000..fc4b2d16 --- /dev/null +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.cellbroadcastreceiver; + +import android.content.Context; +import android.graphics.Typeface; +import android.telephony.CellBroadcastMessage; +import android.telephony.SmsCbCmasInfo; +import android.telephony.SmsCbEtwsInfo; +import android.text.Spannable; +import android.text.SpannableStringBuilder; + +/** + * Returns the string resource ID's for CMAS and ETWS emergency alerts. + */ +public class CellBroadcastResources { + + /** + * Returns a styled CharSequence containing the message body and optional CMAS alert headers. + * @param context a Context for resource string access + * @return a CharSequence for display in the broadcast alert dialog + */ + public static CharSequence getFormattedMessageBody(Context context, CellBroadcastMessage cbm) { + if (cbm.isCmasMessage()) { + SmsCbCmasInfo cmasInfo = cbm.getCmasWarningInfo(); + SpannableStringBuilder buf = new SpannableStringBuilder(); + + // CMAS category + int categoryId = getCmasCategoryResId(cmasInfo); + if (categoryId != 0) { + buf.append(context.getText(R.string.cmas_category_heading)); + buf.append(context.getText(categoryId)); + buf.append('\n'); + } + + // CMAS response type + int responseId = getCmasResponseResId(cmasInfo); + if (responseId != 0) { + buf.append(context.getText(R.string.cmas_response_heading)); + buf.append(context.getText(responseId)); + buf.append('\n'); + } + + // CMAS severity + int severityId = getCmasSeverityResId(cmasInfo); + if (severityId != 0) { + buf.append(context.getText(R.string.cmas_severity_heading)); + buf.append(context.getText(severityId)); + buf.append('\n'); + } + + // CMAS urgency + int urgencyId = getCmasUrgencyResId(cmasInfo); + if (urgencyId != 0) { + buf.append(context.getText(R.string.cmas_urgency_heading)); + buf.append(context.getText(urgencyId)); + buf.append('\n'); + } + + // CMAS certainty + int certaintyId = getCmasCertaintyResId(cmasInfo); + if (certaintyId != 0) { + buf.append(context.getText(R.string.cmas_certainty_heading)); + buf.append(context.getText(certaintyId)); + buf.append('\n'); + } + + // Style all headings in bold + buf.setSpan(Typeface.DEFAULT_BOLD, 0, buf.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + + buf.append(cbm.getMessageBody()); + return buf; + } else { + return cbm.getMessageBody(); + } + } + + /** + * Returns the string resource ID for the CMAS category. + * @return a string resource ID, or 0 if the CMAS category is unknown or not present + */ + private static int getCmasCategoryResId(SmsCbCmasInfo cmasInfo) { + switch (cmasInfo.getCategory()) { + case SmsCbCmasInfo.CMAS_CATEGORY_GEO: + return R.string.cmas_category_geo; + + case SmsCbCmasInfo.CMAS_CATEGORY_MET: + return R.string.cmas_category_met; + + case SmsCbCmasInfo.CMAS_CATEGORY_SAFETY: + return R.string.cmas_category_safety; + + case SmsCbCmasInfo.CMAS_CATEGORY_SECURITY: + return R.string.cmas_category_security; + + case SmsCbCmasInfo.CMAS_CATEGORY_RESCUE: + return R.string.cmas_category_rescue; + + case SmsCbCmasInfo.CMAS_CATEGORY_FIRE: + return R.string.cmas_category_fire; + + case SmsCbCmasInfo.CMAS_CATEGORY_HEALTH: + return R.string.cmas_category_health; + + case SmsCbCmasInfo.CMAS_CATEGORY_ENV: + return R.string.cmas_category_env; + + case SmsCbCmasInfo.CMAS_CATEGORY_TRANSPORT: + return R.string.cmas_category_transport; + + case SmsCbCmasInfo.CMAS_CATEGORY_INFRA: + return R.string.cmas_category_infra; + + case SmsCbCmasInfo.CMAS_CATEGORY_CBRNE: + return R.string.cmas_category_cbrne; + + case SmsCbCmasInfo.CMAS_CATEGORY_OTHER: + return R.string.cmas_category_other; + + default: + return 0; + } + } + + /** + * Returns the string resource ID for the CMAS response type. + * @return a string resource ID, or 0 if the CMAS response type is unknown or not present + */ + private static int getCmasResponseResId(SmsCbCmasInfo cmasInfo) { + switch (cmasInfo.getResponseType()) { + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_SHELTER: + return R.string.cmas_response_shelter; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE: + return R.string.cmas_response_evacuate; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_PREPARE: + return R.string.cmas_response_prepare; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EXECUTE: + return R.string.cmas_response_execute; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR: + return R.string.cmas_response_monitor; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_AVOID: + return R.string.cmas_response_avoid; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_ASSESS: + return R.string.cmas_response_assess; + + case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE: + return R.string.cmas_response_none; + + default: + return 0; + } + } + + /** + * Returns the string resource ID for the CMAS severity. + * @return a string resource ID, or 0 if the CMAS severity is unknown or not present + */ + private static int getCmasSeverityResId(SmsCbCmasInfo cmasInfo) { + switch (cmasInfo.getSeverity()) { + case SmsCbCmasInfo.CMAS_SEVERITY_EXTREME: + return R.string.cmas_severity_extreme; + + case SmsCbCmasInfo.CMAS_SEVERITY_SEVERE: + return R.string.cmas_severity_severe; + + default: + return 0; + } + } + + /** + * Returns the string resource ID for the CMAS urgency. + * @return a string resource ID, or 0 if the CMAS urgency is unknown or not present + */ + private static int getCmasUrgencyResId(SmsCbCmasInfo cmasInfo) { + switch (cmasInfo.getUrgency()) { + case SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE: + return R.string.cmas_urgency_immediate; + + case SmsCbCmasInfo.CMAS_URGENCY_EXPECTED: + return R.string.cmas_urgency_expected; + + default: + return 0; + } + } + + /** + * Returns the string resource ID for the CMAS certainty. + * @return a string resource ID, or 0 if the CMAS certainty is unknown or not present + */ + private static int getCmasCertaintyResId(SmsCbCmasInfo cmasInfo) { + switch (cmasInfo.getCertainty()) { + case SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED: + return R.string.cmas_certainty_observed; + + case SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY: + return R.string.cmas_certainty_likely; + + default: + return 0; + } + } + + public static int getDialogTitleResource(CellBroadcastMessage cbm) { + // ETWS warning types + SmsCbEtwsInfo etwsInfo = cbm.getEtwsWarningInfo(); + if (etwsInfo != null) { + switch (etwsInfo.getWarningType()) { + case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE: + return R.string.etws_earthquake_warning; + + case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI: + return R.string.etws_tsunami_warning; + + case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI: + return R.string.etws_earthquake_and_tsunami_warning; + + case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE: + return R.string.etws_test_message; + + case SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY: + default: + return R.string.etws_other_emergency_type; + } + } + + // CMAS warning types + SmsCbCmasInfo cmasInfo = cbm.getCmasWarningInfo(); + if (cmasInfo != null) { + switch (cmasInfo.getMessageClass()) { + case SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT: + return R.string.cmas_presidential_level_alert; + + case SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT: + return R.string.cmas_extreme_alert; + + case SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT: + return R.string.cmas_severe_alert; + + case SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY: + return R.string.cmas_amber_alert; + + case SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST: + return R.string.cmas_required_monthly_test; + + case SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE: + return R.string.cmas_exercise_alert; + + case SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE: + return R.string.cmas_operator_defined_alert; + + default: + return R.string.pws_other_message_identifiers; + } + } + + if (cbm.isPublicAlertMessage()) { + return R.string.pws_other_message_identifiers; + } else { + return R.string.cb_other_message_identifiers; + } + } +} |