5
0
mirror of https://github.com/cwinfo/envayasms.git synced 2024-09-19 13:59:35 +00:00

add foreground service to keep App in memory (otherwise rate-limiting won't work) and provide notification

This commit is contained in:
Jesse Young 2011-09-19 18:51:45 -07:00
parent a793a5f2e3
commit 9a574b3ab4
10 changed files with 268 additions and 22 deletions

View File

@ -84,5 +84,8 @@
<service android:name=".CheckMmsInboxService"> <service android:name=".CheckMmsInboxService">
</service> </service>
<service android:name=".ForegroundService">
</service>
</application> </application>
</manifest> </manifest>

View File

@ -38,6 +38,7 @@ org.envaya.kalsms.ui.InertCheckBox and org.envaya.kalsms.ui.CheckableRelativeLay
is (c) Cédric Caron, released presumably into the public domain at is (c) Cédric Caron, released presumably into the public domain at
http://www.marvinlabs.com/2010/10/custom-listview-ability-check-items/ http://www.marvinlabs.com/2010/10/custom-listview-ability-check-items/
org.envaya.kalsms.App.chooseOutgoingSmsPackage includes code from Android, org.envaya.kalsms.App.chooseOutgoingSmsPackage and
Copyright 2005-2008 The Android Open Source Project org.envaya.kalsms.ForegroundService include code from Android,
Copyright 2005-2009 The Android Open Source Project
See NOTICE See NOTICE

View File

@ -7,6 +7,7 @@
<string name="help">Help</string> <string name="help">Help</string>
<string name="retry_now">Retry</string> <string name="retry_now">Retry</string>
<string name="forward_inbox">Fwd Inbox...</string> <string name="forward_inbox">Fwd Inbox...</string>
<string name='service_started'>New SMS will be forwarded to server</string>
<string name='test_senders'>When running KalSMS in Test Mode, <string name='test_senders'>When running KalSMS in Test Mode,
KalSMS will only forward SMS messages from the phone numbers KalSMS will only forward SMS messages from the phone numbers
listed below. (Incoming SMS messages from other phone numbers will be saved listed below. (Incoming SMS messages from other phone numbers will be saved

View File

@ -16,10 +16,16 @@ class KalSMS
const STATUS_FAILED = 'failed'; const STATUS_FAILED = 'failed';
const STATUS_SENT = 'sent'; const STATUS_SENT = 'sent';
const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_MMS = 'mms';
static function new_from_request() static function new_from_request()
{ {
$version = @$_POST['version']; $version = @$_POST['version'];
// If API version changes, could return different KalSMS instance
// to support multiple phone versions
return new KalSMS(); return new KalSMS();
} }
@ -96,9 +102,9 @@ class KalSMS
class KalSMS_OutgoingMessage class KalSMS_OutgoingMessage
{ {
public $id = ''; public $id = ''; // ID generated by server
public $to; public $to; // destination phone number
public $message; public $message; // content of SMS message
} }
class KalSMS_Action class KalSMS_Action
@ -121,10 +127,37 @@ class KalSMS_Action_Test extends KalSMS_Action
} }
} }
class KalSMS_MMS_Part
{
public $form_name; // name of form field with MMS part content
public $cid; // MMS Content-ID
public $type; // Content type
public $filename; // Original filename of MMS part on sender phone
public $tmp_name; // Temporary file where MMS part content is stored
public $size; // Content length
public $error; // see http://www.php.net/manual/en/features.file-upload.errors.php
function __construct($args)
{
$this->form_name = $args['name'];
$this->cid = $args['cid'];
$this->type = $args['type'];
$this->filename = $args['filename'];
$file = $_FILES[$this->form_name];
$this->tmp_name = $file['tmp_name'];
$this->size = $file['size'];
$this->error = $file['error'];
}
}
class KalSMS_Action_Incoming extends KalSMS_Action class KalSMS_Action_Incoming extends KalSMS_Action
{ {
public $from; public $from; // Sender phone number
public $message; public $message; // The message body of the SMS, or the content of the text/plain part of the MMS.
public $message_type; // KalSMS::MESSAGE_TYPE_MMS or KalSMS::MESSAGE_TYPE_SMS
public $mms_parts; // array of KalSMS_MMS_Part instances
function __construct($kalsms) function __construct($kalsms)
{ {
@ -132,6 +165,16 @@ class KalSMS_Action_Incoming extends KalSMS_Action
$this->type = KalSMS::ACTION_INCOMING; $this->type = KalSMS::ACTION_INCOMING;
$this->from = $_POST['from']; $this->from = $_POST['from'];
$this->message = $_POST['message']; $this->message = $_POST['message'];
$this->message_type = $_POST['message_type'];
if ($this->message_type == KalSMS::MESSAGE_TYPE_MMS)
{
$this->mms_parts = array();
foreach (json_decode($_POST['mms_parts'], true) as $mms_part)
{
$this->mms_parts[] = new KalSMS_MMS_Part($mms_part);
}
}
} }
function get_response_xml($messages) function get_response_xml($messages)
@ -173,8 +216,8 @@ class KalSMS_Action_Outgoing extends KalSMS_Action
class KalSMS_Action_SendStatus extends KalSMS_Action class KalSMS_Action_SendStatus extends KalSMS_Action
{ {
public $status; public $status; // KalSMS::STATUS_* values
public $id; public $id; // server ID previously used in KalSMS_OutgoingMessage
function __construct($type) function __construct($type)
{ {

View File

@ -59,6 +59,9 @@ public final class App extends Application {
// intent to signal to Main activity (if open) that log has changed // intent to signal to Main activity (if open) that log has changed
public static final String LOG_INTENT = "org.envaya.kalsms.LOG"; public static final String LOG_INTENT = "org.envaya.kalsms.LOG";
public static final String START_INTENT = "org.envaya.kalsms.START";
public static final String STOP_INTENT = "org.envaya.kalsms.STOP";
public static final String QUERY_EXPANSION_PACKS_INTENT = "org.envaya.kalsms.QUERY_EXPANSION_PACKS"; public static final String QUERY_EXPANSION_PACKS_INTENT = "org.envaya.kalsms.QUERY_EXPANSION_PACKS";
public static final String QUERY_EXPANSION_PACKS_EXTRA_PACKAGES = "packages"; public static final String QUERY_EXPANSION_PACKS_EXTRA_PACKAGES = "packages";
@ -142,6 +145,12 @@ public final class App extends Application {
mmsObserver.register(); mmsObserver.register();
setOutgoingMessageAlarm(); setOutgoingMessageAlarm();
updateEnabledNotification();
}
public void updateEnabledNotification()
{
startService(new Intent(this, ForegroundService.class));
} }
public synchronized String chooseOutgoingSmsPackage() public synchronized String chooseOutgoingSmsPackage()

View File

@ -42,7 +42,14 @@ public class CheckMmsInboxService extends IntentService
// times if we don't delete them. // times if we don't delete them.
mmsUtils.markOldMms(mms); mmsUtils.markOldMms(mms);
app.forwardToServer(mms); if (mms.isForwardable())
{
app.forwardToServer(mms);
}
else
{
app.log("Ignoring incoming MMS from " + mms.getFrom());
}
} }
} }
} }

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2009 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 org.envaya.kalsms;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.envaya.kalsms.ui.Main;
/*
* Service running in foreground to make sure App instance stays
* in memory (otherwise we could lose timestamps of sent messages
* which could cause us to exceed Android's SMS sending limit)
*
* Also adds notification to status bar.
*/
public class ForegroundService extends Service {
private App app;
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
private static final Class<?>[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class<?>[] mStopForegroundSignature = new Class[] {
boolean.class};
private NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
void invokeMethod(Method method, Object[] args) {
try {
mStartForeground.invoke(this, mStartForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod(mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mSetForeground, mSetForegroundArgs);
mNM.notify(id, notification);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
try {
mStopForeground.invoke(this, mStopForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke stopForeground", e);
}
return;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
mSetForegroundArgs[0] = Boolean.FALSE;
invokeMethod(mSetForeground, mSetForegroundArgs);
}
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
app = (App)getApplication();
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
return;
}
try {
mSetForeground = getClass().getMethod("setForeground",
mSetForegroundSignature);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!");
}
}
@Override
public void onDestroy() {
// Make sure our notification is gone.
stopForegroundCompat(R.string.service_started);
}
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
@Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
//@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return 1; //START_STICKY;
}
void handleCommand(Intent intent)
{
if (app.isEnabled())
{
CharSequence text = getText(R.string.service_started);
Notification notification = new Notification(R.drawable.icon, text,
System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, Main.class), 0);
notification.setLatestEventInfo(this,
"KalSMS running",
text, contentIntent);
startForegroundCompat(R.string.service_started, notification);
}
else
{
this.stopForegroundCompat(R.string.service_started);
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View File

@ -1,12 +1,9 @@
package org.envaya.kalsms; package org.envaya.kalsms;
import android.app.IntentService;
import android.content.Intent; import android.content.Intent;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.os.Handler; import android.os.Handler;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
final class MmsObserver extends ContentObserver { final class MmsObserver extends ContentObserver {

View File

@ -26,7 +26,6 @@ import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody; import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.envaya.kalsms.App; import org.envaya.kalsms.App;
import org.envaya.kalsms.Base64Coder; import org.envaya.kalsms.Base64Coder;
import org.envaya.kalsms.OutgoingMessage; import org.envaya.kalsms.OutgoingMessage;
@ -210,6 +209,7 @@ public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
try try
{ {
handleResponse(response); handleResponse(response);
response.getEntity().consumeContent();
} }
catch (Throwable ex) catch (Throwable ex)
{ {
@ -226,10 +226,6 @@ public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
protected void handleResponse(HttpResponse response) throws Exception protected void handleResponse(HttpResponse response) throws Exception
{ {
if (response != null)
{
response.getEntity().consumeContent();
}
} }
protected void handleFailure() protected void handleFailure()

View File

@ -102,6 +102,7 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
{ {
app.log(app.isEnabled() ? "SMS Gateway started." : "SMS Gateway stopped."); app.log(app.isEnabled() ? "SMS Gateway started." : "SMS Gateway stopped.");
app.setOutgoingMessageAlarm(); app.setOutgoingMessageAlarm();
app.updateEnabledNotification();
} }
updatePrefSummary(findPreference(key)); updatePrefSummary(findPreference(key));