mirror of
https://github.com/cwinfo/envayasms.git
synced 2025-07-04 05:57:44 +00:00
avoid exceeding Android limit of 100 outgoing SMS/app/hour; allow effectively increasing limit via expansion packs; revert Sms -> sms in XML response
This commit is contained in:
@ -1,16 +1,15 @@
|
||||
package org.envaya.kalsms;
|
||||
|
||||
import org.envaya.kalsms.task.PollerTask;
|
||||
import org.envaya.kalsms.task.HttpTask;
|
||||
import org.envaya.kalsms.receiver.OutgoingMessagePoller;
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.Application;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.telephony.SmsManager;
|
||||
@ -18,10 +17,15 @@ import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.Log;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.envaya.kalsms.receiver.OutgoingMessagePoller;
|
||||
import org.envaya.kalsms.task.HttpTask;
|
||||
import org.envaya.kalsms.task.PollerTask;
|
||||
|
||||
public final class App extends Application {
|
||||
|
||||
@ -37,9 +41,24 @@ public final class App extends Application {
|
||||
public static final String MESSAGE_TYPE_SMS = "sms";
|
||||
|
||||
public static final String LOG_NAME = "KALSMS";
|
||||
public static final String LOG_INTENT = "org.envaya.kalsms.LOG";
|
||||
|
||||
public static final int MAX_DISPLAYED_LOG = 15000;
|
||||
// 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 QUERY_EXPANSION_PACKS_INTENT = "org.envaya.kalsms.QUERY_EXPANSION_PACKS";
|
||||
public static final String QUERY_EXPANSION_PACKS_EXTRA_PACKAGES = "packages";
|
||||
|
||||
// Interface for sending outgoing messages to expansion packs
|
||||
public static final String OUTGOING_SMS_INTENT_SUFFIX = ".OUTGOING_SMS";
|
||||
public static final String OUTGOING_SMS_EXTRA_TO = "to";
|
||||
public static final String OUTGOING_SMS_EXTRA_BODY = "body";
|
||||
public static final int OUTGOING_SMS_UNHANDLED = Activity.RESULT_FIRST_USER;
|
||||
|
||||
// intent for MessageStatusNotifier to receive status updates for outgoing SMS
|
||||
// (even if sent by an expansion pack)
|
||||
public static final String MESSAGE_STATUS_INTENT = "org.envaya.kalsms.MESSAGE_STATUS";
|
||||
|
||||
public static final int MAX_DISPLAYED_LOG = 4000;
|
||||
public static final int LOG_TIMESTAMP_INTERVAL = 60000;
|
||||
|
||||
// Each QueuedMessage is identified within our internal Map by its Uri.
|
||||
@ -48,7 +67,13 @@ public final class App extends Application {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://org.envaya.kalsms");
|
||||
public static final Uri INCOMING_URI = Uri.withAppendedPath(CONTENT_URI, "incoming");
|
||||
public static final Uri OUTGOING_URI = Uri.withAppendedPath(CONTENT_URI, "outgoing");
|
||||
|
||||
|
||||
// max per-app outgoing SMS rate used by com.android.internal.telephony.SMSDispatcher
|
||||
// with a slightly longer check period to account for variance in the time difference
|
||||
// between when we prepare messages and when SMSDispatcher receives them
|
||||
public static int OUTGOING_SMS_CHECK_PERIOD = 3605000; // one hour plus 5 sec (in ms)
|
||||
public static int OUTGOING_SMS_MAX_COUNT = 100;
|
||||
|
||||
private Map<Uri, IncomingMessage> incomingMessages = new HashMap<Uri, IncomingMessage>();
|
||||
private Map<Uri, OutgoingMessage> outgoingMessages = new HashMap<Uri, OutgoingMessage>();
|
||||
|
||||
@ -57,6 +82,11 @@ public final class App extends Application {
|
||||
private SpannableStringBuilder displayedLog = new SpannableStringBuilder();
|
||||
private long lastLogTime;
|
||||
|
||||
private List<String> outgoingMessagePackages = new ArrayList<String>();
|
||||
private int outgoingMessageCount = -1;
|
||||
private HashMap<String, ArrayList<Long>> outgoingTimestamps
|
||||
= new HashMap<String, ArrayList<Long>>();
|
||||
|
||||
private MmsUtils mmsUtils;
|
||||
|
||||
@Override
|
||||
@ -67,6 +97,10 @@ public final class App extends Application {
|
||||
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mmsUtils = new MmsUtils(this);
|
||||
|
||||
outgoingMessagePackages.add(getPackageName());
|
||||
|
||||
updateExpansionPacks();
|
||||
|
||||
log(Html.fromHtml(
|
||||
isEnabled() ? "<b>SMS gateway running.</b>" : "<b>SMS gateway disabled.</b>"));
|
||||
|
||||
@ -77,8 +111,102 @@ public final class App extends Application {
|
||||
mmsObserver.register();
|
||||
|
||||
setOutgoingMessageAlarm();
|
||||
}
|
||||
|
||||
public synchronized String chooseOutgoingSmsPackage()
|
||||
{
|
||||
outgoingMessageCount++;
|
||||
|
||||
int numPackages = outgoingMessagePackages.size();
|
||||
|
||||
// round robin selection of packages that are under max sending rate
|
||||
for (int i = 0; i < numPackages; i++)
|
||||
{
|
||||
int packageIndex = (outgoingMessageCount + i) % numPackages;
|
||||
String packageName = outgoingMessagePackages.get(packageIndex);
|
||||
|
||||
// implement rate-limiting algorithm from
|
||||
// com.android.internal.telephony.SMSDispatcher.SmsCounter
|
||||
|
||||
if (!outgoingTimestamps.containsKey(packageName)) {
|
||||
outgoingTimestamps.put(packageName, new ArrayList<Long>());
|
||||
}
|
||||
|
||||
ArrayList<Long> sent = outgoingTimestamps.get(packageName);
|
||||
Long ct = System.currentTimeMillis();
|
||||
|
||||
//log(packageName + " SMS send size=" + sent.size());
|
||||
|
||||
// remove old timestamps
|
||||
while (sent.size() > 0 && (ct - sent.get(0)) > OUTGOING_SMS_CHECK_PERIOD )
|
||||
{
|
||||
sent.remove(0);
|
||||
}
|
||||
|
||||
if ( (sent.size() + 1) <= OUTGOING_SMS_MAX_COUNT)
|
||||
{
|
||||
sent.add(ct);
|
||||
return packageName;
|
||||
}
|
||||
}
|
||||
|
||||
log("Can't send outgoing SMS: maximum limit of "
|
||||
+ getOutgoingMessageLimit() + " in 1 hour reached");
|
||||
log("To increase this limit, install an expansion pack.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void setExpansionPacks(List<String> packages)
|
||||
{
|
||||
int prevLimit = getOutgoingMessageLimit();
|
||||
|
||||
if (packages == null)
|
||||
{
|
||||
packages = new ArrayList<String>();
|
||||
}
|
||||
|
||||
packages.add(getPackageName());
|
||||
|
||||
outgoingMessagePackages = packages;
|
||||
|
||||
int newLimit = getOutgoingMessageLimit();
|
||||
|
||||
if (prevLimit != newLimit)
|
||||
{
|
||||
log("Outgoing SMS limit: " + newLimit + " messages/hour");
|
||||
}
|
||||
}
|
||||
|
||||
public int getOutgoingMessageLimit()
|
||||
{
|
||||
return outgoingMessagePackages.size() * OUTGOING_SMS_MAX_COUNT;
|
||||
}
|
||||
|
||||
public void updateExpansionPacks()
|
||||
{
|
||||
ArrayList<String> packages = new ArrayList<String>();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putStringArrayList(App.QUERY_EXPANSION_PACKS_EXTRA_PACKAGES, packages);
|
||||
|
||||
sendOrderedBroadcast(
|
||||
new Intent(App.QUERY_EXPANSION_PACKS_INTENT),
|
||||
"android.permission.SEND_SMS",
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent resultIntent) {
|
||||
|
||||
setExpansionPacks(this.getResultExtras(false)
|
||||
.getStringArrayList(App.QUERY_EXPANSION_PACKS_EXTRA_PACKAGES));
|
||||
|
||||
}
|
||||
},
|
||||
null,
|
||||
Activity.RESULT_OK,
|
||||
null,
|
||||
extras);
|
||||
}
|
||||
|
||||
public void checkOutgoingMessages()
|
||||
{
|
||||
String serverUrl = getServerUrl();
|
||||
@ -266,7 +394,7 @@ public final class App extends Application {
|
||||
public synchronized void sendOutgoingMessage(OutgoingMessage sms) {
|
||||
Uri uri = sms.getUri();
|
||||
if (outgoingMessages.containsKey(uri)) {
|
||||
log(sms.getLogName() + " already sent, skipping");
|
||||
log("Duplicate outgoing " + sms.getLogName() + ", skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ final class MmsObserver extends ContentObserver {
|
||||
*/
|
||||
app.getContentResolver().registerContentObserver(
|
||||
MmsUtils.OBSERVER_URI, true, this);
|
||||
app.log("MMS content observer registered");
|
||||
|
||||
MmsUtils mmsUtils = app.getMmsUtils();
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
package org.envaya.kalsms;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import org.envaya.kalsms.receiver.OutgoingMessageRetry;
|
||||
import org.envaya.kalsms.receiver.MessageStatusNotifier;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.telephony.SmsManager;
|
||||
|
||||
public class OutgoingMessage extends QueuedMessage {
|
||||
|
||||
@ -87,18 +87,19 @@ public class OutgoingMessage extends QueuedMessage {
|
||||
|
||||
public void trySend()
|
||||
{
|
||||
SmsManager smgr = SmsManager.getDefault();
|
||||
|
||||
Intent intent = new Intent(app, MessageStatusNotifier.class);
|
||||
intent.setData(this.getUri());
|
||||
|
||||
PendingIntent sentIntent = PendingIntent.getBroadcast(
|
||||
app,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_ONE_SHOT);
|
||||
|
||||
smgr.sendTextMessage(getTo(), null, getMessageBody(), sentIntent, null);
|
||||
String packageName = app.chooseOutgoingSmsPackage();
|
||||
|
||||
if (packageName == null)
|
||||
{
|
||||
// todo... schedule retry
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(packageName + App.OUTGOING_SMS_INTENT_SUFFIX, this.getUri());
|
||||
intent.putExtra(App.OUTGOING_SMS_EXTRA_TO, getTo());
|
||||
intent.putExtra(App.OUTGOING_SMS_EXTRA_BODY, getMessageBody());
|
||||
|
||||
app.sendBroadcast(intent, "android.permission.SEND_SMS");
|
||||
}
|
||||
|
||||
protected Intent getRetryIntent() {
|
||||
|
22
src/org/envaya/kalsms/receiver/ExpansionPackInstallReceiver.java
Executable file
22
src/org/envaya/kalsms/receiver/ExpansionPackInstallReceiver.java
Executable file
@ -0,0 +1,22 @@
|
||||
package org.envaya.kalsms.receiver;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import org.envaya.kalsms.App;
|
||||
|
||||
public class ExpansionPackInstallReceiver extends BroadcastReceiver
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
|
||||
if (packageName != null && packageName.startsWith(context.getPackageName() + ".pack"))
|
||||
{
|
||||
app.updateExpansionPacks();
|
||||
}
|
||||
}
|
||||
}
|
31
src/org/envaya/kalsms/receiver/OutgoingSmsReceiver.java
Executable file
31
src/org/envaya/kalsms/receiver/OutgoingSmsReceiver.java
Executable file
@ -0,0 +1,31 @@
|
||||
package org.envaya.kalsms.receiver;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SmsManager;
|
||||
import org.envaya.kalsms.App;
|
||||
|
||||
public class OutgoingSmsReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
Bundle extras = intent.getExtras();
|
||||
String to = extras.getString(App.OUTGOING_SMS_EXTRA_TO);
|
||||
String body = extras.getString(App.OUTGOING_SMS_EXTRA_BODY);
|
||||
|
||||
SmsManager smgr = SmsManager.getDefault();
|
||||
|
||||
Intent statusIntent = new Intent(App.MESSAGE_STATUS_INTENT, intent.getData());
|
||||
|
||||
PendingIntent sentIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
0,
|
||||
statusIntent,
|
||||
PendingIntent.FLAG_ONE_SHOT);
|
||||
|
||||
smgr.sendTextMessage(to, null, body, sentIntent, null);
|
||||
}
|
||||
}
|
@ -190,7 +190,7 @@ public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
|
||||
DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
Document xml = xmlBuilder.parse(responseStream);
|
||||
|
||||
NodeList smsNodes = xml.getElementsByTagName("Sms");
|
||||
NodeList smsNodes = xml.getElementsByTagName("sms");
|
||||
for (int i = 0; i < smsNodes.getLength(); i++) {
|
||||
Element smsElement = (Element) smsNodes.item(i);
|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.envaya.kalsms.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
@ -11,13 +7,8 @@ import android.view.Menu;
|
||||
import android.widget.TextView;
|
||||
import org.envaya.kalsms.R;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jesse
|
||||
*/
|
||||
public class Help extends Activity {
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@ -29,8 +20,7 @@ public class Help extends Activity {
|
||||
String html = "<b>KalSMS</b> is a SMS gateway.<br /><br /> "
|
||||
+ "It forwards all incoming SMS messages received by this phone to a server on the internet, "
|
||||
+ "and also sends outgoing SMS messages from that server to other phones.<br /><br />"
|
||||
+ "(See https://github.com/youngj/KalSMS/wiki "
|
||||
+ "for information about setting up a server.)<br /><br />"
|
||||
+ "(See https://kalsms.net for information about setting up a server.)<br /><br />"
|
||||
+ "The Settings screen allows you configure KalSMS to work with a particular server, "
|
||||
+ "by entering the server URL, your phone number, "
|
||||
+ "and the password assigned to your phone on the server.<br /><br />"
|
||||
@ -39,11 +29,4 @@ public class Help extends Activity {
|
||||
help.setText(Html.fromHtml(html));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
this.finish();
|
||||
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user