mirror of
https://github.com/cwinfo/envayasms.git
synced 2024-12-04 12:35:32 +00:00
add foreground service to keep App in memory (otherwise rate-limiting won't work) and provide notification
This commit is contained in:
parent
a793a5f2e3
commit
9a574b3ab4
@ -82,6 +82,9 @@
|
||||
</receiver>
|
||||
|
||||
<service android:name=".CheckMmsInboxService">
|
||||
</service>
|
||||
|
||||
<service android:name=".ForegroundService">
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
5
LICENSE
5
LICENSE
@ -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
|
||||
http://www.marvinlabs.com/2010/10/custom-listview-ability-check-items/
|
||||
|
||||
org.envaya.kalsms.App.chooseOutgoingSmsPackage includes code from Android,
|
||||
Copyright 2005-2008 The Android Open Source Project
|
||||
org.envaya.kalsms.App.chooseOutgoingSmsPackage and
|
||||
org.envaya.kalsms.ForegroundService include code from Android,
|
||||
Copyright 2005-2009 The Android Open Source Project
|
||||
See NOTICE
|
@ -7,6 +7,7 @@
|
||||
<string name="help">Help</string>
|
||||
<string name="retry_now">Retry</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,
|
||||
KalSMS will only forward SMS messages from the phone numbers
|
||||
listed below. (Incoming SMS messages from other phone numbers will be saved
|
||||
|
@ -16,10 +16,16 @@ class KalSMS
|
||||
const STATUS_FAILED = 'failed';
|
||||
const STATUS_SENT = 'sent';
|
||||
|
||||
const MESSAGE_TYPE_SMS = 'sms';
|
||||
const MESSAGE_TYPE_MMS = 'mms';
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -96,9 +102,9 @@ class KalSMS
|
||||
|
||||
class KalSMS_OutgoingMessage
|
||||
{
|
||||
public $id = '';
|
||||
public $to;
|
||||
public $message;
|
||||
public $id = ''; // ID generated by server
|
||||
public $to; // destination phone number
|
||||
public $message; // content of SMS message
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public $from;
|
||||
public $message;
|
||||
public $from; // Sender phone number
|
||||
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)
|
||||
{
|
||||
@ -132,6 +165,16 @@ class KalSMS_Action_Incoming extends KalSMS_Action
|
||||
$this->type = KalSMS::ACTION_INCOMING;
|
||||
$this->from = $_POST['from'];
|
||||
$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)
|
||||
@ -173,9 +216,9 @@ class KalSMS_Action_Outgoing extends KalSMS_Action
|
||||
|
||||
class KalSMS_Action_SendStatus extends KalSMS_Action
|
||||
{
|
||||
public $status;
|
||||
public $id;
|
||||
|
||||
public $status; // KalSMS::STATUS_* values
|
||||
public $id; // server ID previously used in KalSMS_OutgoingMessage
|
||||
|
||||
function __construct($type)
|
||||
{
|
||||
$this->type = KalSMS::ACTION_SEND_STATUS;
|
||||
|
@ -58,6 +58,9 @@ public final class App extends Application {
|
||||
|
||||
// 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 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_EXTRA_PACKAGES = "packages";
|
||||
@ -141,9 +144,15 @@ public final class App extends Application {
|
||||
mmsObserver = new MmsObserver(this);
|
||||
mmsObserver.register();
|
||||
|
||||
setOutgoingMessageAlarm();
|
||||
setOutgoingMessageAlarm();
|
||||
updateEnabledNotification();
|
||||
}
|
||||
|
||||
public void updateEnabledNotification()
|
||||
{
|
||||
startService(new Intent(this, ForegroundService.class));
|
||||
}
|
||||
|
||||
public synchronized String chooseOutgoingSmsPackage()
|
||||
{
|
||||
outgoingMessageCount++;
|
||||
|
@ -42,7 +42,14 @@ public class CheckMmsInboxService extends IntentService
|
||||
// times if we don't delete them.
|
||||
mmsUtils.markOldMms(mms);
|
||||
|
||||
app.forwardToServer(mms);
|
||||
if (mms.isForwardable())
|
||||
{
|
||||
app.forwardToServer(mms);
|
||||
}
|
||||
else
|
||||
{
|
||||
app.log("Ignoring incoming MMS from " + mms.getFrom());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
188
src/org/envaya/kalsms/ForegroundService.java
Executable file
188
src/org/envaya/kalsms/ForegroundService.java
Executable 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;
|
||||
}
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
package org.envaya.kalsms;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
final class MmsObserver extends ContentObserver {
|
||||
|
||||
|
@ -26,7 +26,6 @@ import org.apache.http.entity.mime.FormBodyPart;
|
||||
import org.apache.http.entity.mime.MultipartEntity;
|
||||
import org.apache.http.entity.mime.content.StringBody;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.envaya.kalsms.App;
|
||||
import org.envaya.kalsms.Base64Coder;
|
||||
import org.envaya.kalsms.OutgoingMessage;
|
||||
@ -210,6 +209,7 @@ public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
|
||||
try
|
||||
{
|
||||
handleResponse(response);
|
||||
response.getEntity().consumeContent();
|
||||
}
|
||||
catch (Throwable ex)
|
||||
{
|
||||
@ -226,10 +226,6 @@ public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
|
||||
|
||||
protected void handleResponse(HttpResponse response) throws Exception
|
||||
{
|
||||
if (response != null)
|
||||
{
|
||||
response.getEntity().consumeContent();
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleFailure()
|
||||
|
@ -102,6 +102,7 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
||||
{
|
||||
app.log(app.isEnabled() ? "SMS Gateway started." : "SMS Gateway stopped.");
|
||||
app.setOutgoingMessageAlarm();
|
||||
app.updateEnabledNotification();
|
||||
}
|
||||
|
||||
updatePrefSummary(findPreference(key));
|
||||
|
Loading…
Reference in New Issue
Block a user