summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml5
-rw-r--r--res/values/strings.xml2
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java22
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertDialog.java6
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java29
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java20
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java28
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java321
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastCursorAdapter.java1
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastCursorLoader.java181
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java483
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseHelper.java330
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastDatabaseService.java179
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastListActivity.java97
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastListItem.java8
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastMessage.java616
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java8
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java4
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastResources.java284
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;
+ }
+ }
+}