mirror of
https://github.com/cwinfo/envayasms.git
synced 2024-12-04 12:35:32 +00:00
version 3.0 - real-time AMQP connections; change server API format from XML to JSON, update PHP server library; persistent storage of pending messages
This commit is contained in:
parent
f53ccc3cc9
commit
239ee1fd52
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
local.properties
|
|
||||||
bin
|
bin
|
||||||
gen
|
gen
|
||||||
nbandroid
|
nbandroid
|
||||||
|
private
|
||||||
|
local.properties
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "server/php/example/httpserver"]
|
[submodule "server/php/example/httpserver"]
|
||||||
path = server/php/example/httpserver
|
path = server/php/example/httpserver
|
||||||
url = git://github.com/youngj/httpserver.git
|
url = git://github.com/youngj/httpserver.git
|
||||||
|
[submodule "server/php/example/php-amqplib"]
|
||||||
|
path = server/php/example/php-amqplib
|
||||||
|
url = git://github.com/youngj/php-amqplib.git
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.envaya.sms"
|
package="org.envaya.sms"
|
||||||
android:versionCode="18"
|
android:versionCode="29"
|
||||||
android:versionName="2.0.5">
|
android:versionName="3.0.0">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="4" />
|
<uses-sdk android:minSdkVersion="4" />
|
||||||
|
|
||||||
@ -23,51 +23,69 @@
|
|||||||
|
|
||||||
<application android:name="org.envaya.sms.App"
|
<application android:name="org.envaya.sms.App"
|
||||||
android:icon="@drawable/icon" android:label="@string/app_name">
|
android:icon="@drawable/icon" android:label="@string/app_name">
|
||||||
|
<activity android:name="org.envaya.sms.ui.Main" android:label="@string/app_name" android:noHistory="true">
|
||||||
<activity android:name=".ui.LogView" android:label="@string/app_name">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity android:name=".ui.Help" android:label="EnvayaSMS : Help">
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.TestPhoneNumbers" android:label="EnvayaSMS : Test Phone Numbers">
|
<activity android:name="org.envaya.sms.ui.LogView" android:label="@string/log_view_title">
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="org.envaya.sms.ui.Help" android:label="@string/help_title">
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="org.envaya.sms.ui.TestPhoneNumbers" android:label="@string/test_phone_numbers_title">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.IgnoredPhoneNumbers" android:label="EnvayaSMS : Ignored Phone Numbers">
|
<activity android:name="org.envaya.sms.ui.IgnoredPhoneNumbers" android:label="@string/ignored_phone_numbers_title">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.MessagingInbox" android:label="EnvayaSMS : Forward Inbox">
|
<activity android:name="org.envaya.sms.ui.MessagingSmsInbox" android:label="@string/forward_saved_title">
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="org.envaya.sms.ui.MessagingMmsInbox" android:label="@string/forward_saved_title">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.PendingMessages" android:label="EnvayaSMS : Pending Messages">
|
<activity android:name="org.envaya.sms.ui.MessagingSentSms" android:label="@string/forward_saved_title">
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="org.envaya.sms.ui.PendingMessages" android:label="@string/pending_messages_title">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.Prefs" android:label="EnvayaSMS : Settings">
|
<activity android:name="org.envaya.sms.ui.Prefs" android:label="@string/settings_title">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name="org.envaya.sms.ui.ExpansionPacks" android:label="...">
|
||||||
|
</activity>
|
||||||
|
|
||||||
<receiver android:name=".receiver.SmsReceiver">
|
<receiver android:name="org.envaya.sms.receiver.SmsReceiver">
|
||||||
<intent-filter android:priority="101">
|
<intent-filter android:priority="101">
|
||||||
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.OutgoingSmsReceiver">
|
<receiver android:name="org.envaya.sms.receiver.OutgoingSmsReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.envaya.sms.OUTGOING_SMS" />
|
<action android:name="org.envaya.sms.OUTGOING_SMS" />
|
||||||
<data android:scheme="content" />
|
<data android:scheme="content" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.MessageStatusNotifier" android:exported="true">
|
<receiver android:name="org.envaya.sms.receiver.MessageStatusNotifier" android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.envaya.sms.MESSAGE_STATUS" />
|
<action android:name="org.envaya.sms.MESSAGE_STATUS" />
|
||||||
<data android:scheme="content" />
|
<data android:scheme="content" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name="org.envaya.sms.receiver.NudgeReceiver" android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.envaya.sms.NUDGE" />
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
we don't really use message delivery notifications yet...
|
we don't really use message delivery notifications yet...
|
||||||
@ -80,31 +98,28 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<receiver android:name=".receiver.DequeueOutgoingMessageReceiver">
|
<receiver android:name="org.envaya.sms.receiver.DequeueOutgoingMessageReceiver">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.OutgoingMessageTimeout">
|
<receiver android:name="org.envaya.sms.receiver.OutgoingMessageTimeout">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.OutgoingMessagePoller">
|
<receiver android:name="org.envaya.sms.receiver.OutgoingMessagePoller">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.OutgoingMessageRetry">
|
<receiver android:name="org.envaya.sms.receiver.OutgoingMessageRetry">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.IncomingMessageRetry">
|
<receiver android:name="org.envaya.sms.receiver.IncomingMessageRetry">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.ReenableWifiReceiver">
|
<receiver android:name="org.envaya.sms.receiver.ReenableWifiReceiver">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.BootReceiver">
|
<receiver android:name="org.envaya.sms.receiver.StartAmqpConsumer">
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.ExpansionPackInstallReceiver">
|
<receiver android:name="org.envaya.sms.receiver.ExpansionPackInstallReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||||
@ -113,13 +128,13 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.ConnectivityChangeReceiver" >
|
<receiver android:name="org.envaya.sms.receiver.ConnectivityChangeReceiver" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receiver.DeviceStatusReceiver">
|
<receiver android:name="org.envaya.sms.receiver.DeviceStatusReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
|
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
|
||||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
|
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
|
||||||
@ -128,11 +143,19 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<service android:name=".CheckMmsInboxService">
|
<service android:name="org.envaya.sms.service.CheckMessagingService">
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service android:name=".ForegroundService">
|
<service android:name="org.envaya.sms.service.EnabledChangedService">
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service android:name="org.envaya.sms.service.ForegroundService">
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service android:name="org.envaya.sms.service.AmqpConsumerService">
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service android:name="org.envaya.sms.service.AmqpHeartbeatService">
|
||||||
|
</service>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
@ -0,0 +1,17 @@
|
|||||||
|
# This file is used to override default values used by the Ant build system.
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems, as it is
|
||||||
|
# integral to the build system of your project.
|
||||||
|
|
||||||
|
# This file is only used by the Ant script.
|
||||||
|
|
||||||
|
# You can use this to override default values such as
|
||||||
|
# 'source.dir' for the location of your java source folder and
|
||||||
|
# 'out.dir' for the location of your output folder.
|
||||||
|
|
||||||
|
# You can also use it define how the release builds are signed by declaring
|
||||||
|
# the following properties:
|
||||||
|
# 'key.store' for the location of your keystore and
|
||||||
|
# 'key.alias' for the name of the key to use.
|
||||||
|
# The password will be asked during the build when you use the 'release' target.
|
||||||
|
|
31
build.xml
31
build.xml
@ -43,23 +43,22 @@
|
|||||||
<fail
|
<fail
|
||||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
|
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
|
||||||
unless="sdk.dir"
|
unless="sdk.dir"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
<!-- extension targets. Uncomment the ones where you want to do custom work
|
Import per project custom build rules if present at the root of the project.
|
||||||
in between standard targets -->
|
This is the place to put custom intermediary targets such as:
|
||||||
<!--
|
-pre-build
|
||||||
<target name="-pre-build">
|
-pre-compile
|
||||||
</target>
|
-post-compile (This is typically used for code obfuscation.
|
||||||
<target name="-pre-compile">
|
Compiled code location: ${out.classes.absolute.dir}
|
||||||
</target>
|
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||||
|
-post-package
|
||||||
/* This is typically used for code obfuscation.
|
-post-build
|
||||||
Compiled code location: ${out.classes.absolute.dir}
|
-pre-clean
|
||||||
If this is not done in place, override ${out.dex.input.absolute.dir} */
|
-->
|
||||||
<target name="-post-compile">
|
|
||||||
</target>
|
<import file="custom_rules.xml" optional="true" />
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Import the actual build file.
|
<!-- Import the actual build file.
|
||||||
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system use,
|
||||||
|
# "build.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
|
||||||
|
# Project target.
|
||||||
|
target=android-4
|
15
export.properties
Normal file
15
export.properties
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Export properties
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
|
||||||
|
# The main content for this file is:
|
||||||
|
# - package name for the application being export
|
||||||
|
# - list of the projects being export
|
||||||
|
# - version code for the application
|
||||||
|
|
||||||
|
# You can also use it define how the release builds are signed by declaring
|
||||||
|
# the following properties:
|
||||||
|
# 'key.store' for the location of your keystore and
|
||||||
|
# 'key.alias' for the name of the key alias to use.
|
||||||
|
# The password will be asked during the build when you use the 'release' target.
|
||||||
|
|
BIN
libs/commons-cli-1.1.jar
Normal file
BIN
libs/commons-cli-1.1.jar
Normal file
Binary file not shown.
BIN
libs/commons-io-1.2.jar
Normal file
BIN
libs/commons-io-1.2.jar
Normal file
Binary file not shown.
BIN
libs/rabbitmq-client.jar
Normal file
BIN
libs/rabbitmq-client.jar
Normal file
Binary file not shown.
20
proguard-project.txt
Normal file
20
proguard-project.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# To enable ProGuard in your project, edit project.properties
|
||||||
|
# to define the proguard.config property as described in that file.
|
||||||
|
#
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the ProGuard
|
||||||
|
# include property in project.properties.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
@ -3,18 +3,22 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent" android:background="#333333">
|
android:layout_height="fill_parent" android:background="#333333">
|
||||||
<ScrollView android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent" android:layout_weight="1">
|
|
||||||
<TextView
|
<TextView
|
||||||
android:linksClickable="true"
|
android:linksClickable="true"
|
||||||
android:drawablePadding="5px"
|
android:drawablePadding="5px"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/help"
|
android:id="@+id/help"
|
||||||
android:autoLink="web"
|
android:autoLink="web"
|
||||||
android:textColor="#FFFFFF"
|
android:textColor="#FFFFFF"
|
||||||
android:layout_margin="5px">
|
android:layout_margin="5px">
|
||||||
</TextView>
|
</TextView>
|
||||||
</ScrollView>
|
<Button
|
||||||
|
android:id="@+id/reset"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:onClick="resetClicked"
|
||||||
|
android:text="Reset All Settings" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
@ -5,13 +5,17 @@
|
|||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp">
|
android:paddingRight="8dp">
|
||||||
|
<Spinner android:id="@+android:id/inbox_selector"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</Spinner>
|
||||||
<ListView android:id="@android:id/list"
|
<ListView android:id="@android:id/list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
/>
|
/>
|
||||||
<TextView android:id="@android:id/empty"
|
<TextView android:id="@android:id/empty"
|
||||||
android:text="The inbox is empty."
|
android:text="No messages found."
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
@ -3,14 +3,46 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent" android:background="#333333">
|
android:layout_height="fill_parent" android:background="#333333">
|
||||||
<ScrollView android:id="@+id/info_scroll" android:layout_width="fill_parent"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true"
|
||||||
|
android:onClick="infoClicked"
|
||||||
|
android:padding="10px"
|
||||||
|
android:background="#666666">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/heading"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:gravity="center"
|
||||||
|
></TextView>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:gravity="center"
|
||||||
|
android:id="@+id/info"
|
||||||
|
></TextView>
|
||||||
|
</LinearLayout>
|
||||||
|
<ScrollView android:id="@+id/log_scroll" android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent" android:layout_weight="1">
|
android:layout_height="fill_parent" android:layout_weight="1">
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:id="@+id/info"
|
android:id="@+id/log"
|
||||||
android:textColor="#FFFFFF"
|
android:textColor="#FFFFFF"
|
||||||
|
android:textColorLink="#FFFFFF"
|
||||||
android:layout_margin="5px"></TextView>
|
android:layout_margin="5px"></TextView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/upgrade_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="20sp"
|
||||||
|
android:onClick="upgradeClicked"
|
||||||
|
android:text="" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
@ -11,6 +11,13 @@
|
|||||||
android:text="@string/test_phone_numbers">
|
android:text="@string/test_phone_numbers">
|
||||||
</TextView>
|
</TextView>
|
||||||
|
|
||||||
|
<CheckBox android:id="@+id/auto_add_outgoing"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="autoAddOutgoingClicked"
|
||||||
|
android:text="Automatically add recipients of outgoing messages"
|
||||||
|
/>
|
||||||
|
|
||||||
<ListView android:id="@android:id/list"
|
<ListView android:id="@android:id/list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<item android:id="@+id/check_now"
|
<item android:id="@+id/check_now"
|
||||||
android:icon="@drawable/ic_menu_tick"
|
android:icon="@drawable/ic_menu_tick"
|
||||||
android:title="@string/check_now" />
|
android:title="@string/check_now" />
|
||||||
<item android:id="@+id/forward_inbox"
|
<item android:id="@+id/forward_saved"
|
||||||
android:icon="@drawable/ic_menu_dialog"
|
android:icon="@drawable/ic_menu_dialog"
|
||||||
android:title="@string/forward_inbox" />
|
android:title="@string/forward_saved" />
|
||||||
<item android:id="@+id/retry_now"
|
<item android:id="@+id/retry_now"
|
||||||
android:icon="@drawable/ic_menu_magnet"
|
android:icon="@drawable/ic_menu_magnet"
|
||||||
android:title="@string/retry_now" />
|
android:title="@string/retry_now" />
|
||||||
|
@ -1,18 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">EnvayaSMS</string>
|
<string name="app_name">EnvayaSMS</string>
|
||||||
|
<string name="running">EnvayaSMS running</string>
|
||||||
|
<string name="disabled">EnvayaSMS disabled</string>
|
||||||
|
<string name="started">EnvayaSMS started.</string>
|
||||||
|
<string name="stopped">EnvayaSMS stopped.</string>
|
||||||
|
<string name="log_view_title">EnvayaSMS : Log View</string>
|
||||||
|
<string name="add_phone_title">EnvayaSMS : Add Phone</string>
|
||||||
|
<string name="help_title">EnvayaSMS : Help</string>
|
||||||
|
<string name='test_phone_numbers_title'>EnvayaSMS : Test Phone Numbers</string>
|
||||||
|
<string name='ignored_phone_numbers_title'>EnvayaSMS : Ignored Phone Numbers</string>
|
||||||
|
<string name='forward_saved_title'>EnvayaSMS : Forward Saved Messages</string>
|
||||||
|
<string name='pending_messages_title'>EnvayaSMS : Pending Messages</string>
|
||||||
|
<string name='settings_title'>EnvayaSMS : Settings</string>
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="test">Test Connection</string>
|
<string name="test">Test Connection</string>
|
||||||
<string name="check_now">Check Messages</string>
|
<string name="check_now">Check Messages</string>
|
||||||
<string name="help">Help</string>
|
<string name="help">Help</string>
|
||||||
<string name="pending">Pending Msgs...</string>
|
<string name="pending">Pending Msgs...</string>
|
||||||
<string name="retry_now">Retry</string>
|
<string name="retry_now">Retry</string>
|
||||||
<string name="forward_inbox">Fwd Inbox...</string>
|
<string name="forward_saved">Fwd Saved...</string>
|
||||||
<string name='service_started'>New SMS will be forwarded to server</string>
|
<string name='service_started'>New messages will be forwarded to server</string>
|
||||||
<string name='test_phone_numbers'>When running EnvayaSMS in Test Mode,
|
<string name='test_phone_numbers'>When running Telerivet in Test Mode,
|
||||||
EnvayaSMS will only forward SMS messages from the phone numbers
|
EnvayaSMS 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
|
||||||
in the normal Messaging inbox, and outgoing messages will be ignored.)</string>
|
in the normal Messaging inbox.)</string>
|
||||||
<string name='ignored_phone_numbers'>
|
<string name='ignored_phone_numbers'>
|
||||||
EnvayaSMS will ignore SMS messages from the phone numbers listed below.
|
EnvayaSMS will ignore SMS messages from the phone numbers listed below.
|
||||||
Incoming messages from these senders will be saved in the normal Messaging inbox.</string>
|
Incoming messages from these senders will be saved in the normal Messaging inbox.</string>
|
||||||
|
23
res/xml/expansion_packs.xml
Normal file
23
res/xml/expansion_packs.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
android:key="pack01"
|
||||||
|
android:title="SMS Expansion Pack 1"
|
||||||
|
android:summary="...">
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:data="market://details?id=org.envaya.sms.pack01"
|
||||||
|
/>
|
||||||
|
</PreferenceScreen>
|
||||||
|
<PreferenceScreen
|
||||||
|
android:key="pack02"
|
||||||
|
android:title="SMS Expansion Pack 2"
|
||||||
|
android:summary="...">
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:data="market://details?id=org.envaya.sms.pack02"
|
||||||
|
/>
|
||||||
|
</PreferenceScreen>
|
||||||
|
</PreferenceScreen>
|
@ -6,9 +6,11 @@
|
|||||||
android:key="enabled"
|
android:key="enabled"
|
||||||
android:title="Enable EnvayaSMS"
|
android:title="Enable EnvayaSMS"
|
||||||
android:defaultValue='false'
|
android:defaultValue='false'
|
||||||
android:summaryOn="All new SMS will be forwarded between phone and server"
|
android:summaryOn="New messages will be forwarded between phone and server"
|
||||||
android:summaryOff="New SMS will not be forwarded between phone and server"
|
android:summaryOff="New messages will not be forwarded between phone and server"
|
||||||
></CheckBoxPreference>
|
></CheckBoxPreference>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="Server Settings">
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:key="server_url"
|
android:key="server_url"
|
||||||
@ -37,36 +39,42 @@
|
|||||||
android:entries="@array/check_intervals"
|
android:entries="@array/check_intervals"
|
||||||
android:entryValues="@array/check_intervals_values"
|
android:entryValues="@array/check_intervals_values"
|
||||||
></ListPreference>
|
></ListPreference>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="Messaging Settings">
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="keep_in_inbox"
|
android:key="keep_in_inbox"
|
||||||
android:title="Keep new messages"
|
android:title="Keep new messages"
|
||||||
android:summaryOff="Incoming SMS will not be stored in Messaging inbox"
|
android:summaryOff="Incoming messages will not be stored in Messaging inbox"
|
||||||
android:summaryOn="Incoming SMS will be stored in Messaging inbox"
|
android:summaryOn="Incoming messages will be stored in Messaging inbox"
|
||||||
></CheckBoxPreference>
|
></CheckBoxPreference>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="call_notifications"
|
android:key="call_notifications"
|
||||||
android:title="Call notifications"
|
android:title="Call notifications"
|
||||||
android:summaryOff="EnvayaSMS will not notify server when phone receives an incoming call"
|
android:summaryOff="Telerivet will not notify server when phone receives an incoming call"
|
||||||
android:summaryOn="EnvayaSMS will notify server when phone receives an incoming call"
|
android:summaryOn="Telerivet will notify server when phone receives an incoming call"
|
||||||
></CheckBoxPreference>
|
></CheckBoxPreference>
|
||||||
|
|
||||||
<ListPreference
|
<PreferenceScreen
|
||||||
android:key="wifi_sleep_policy"
|
android:key="send_limit"
|
||||||
android:title="Wi-Fi sleep policy"
|
android:title="SMS rate limit"
|
||||||
android:defaultValue="never"
|
android:summary="..."
|
||||||
android:entries="@array/wifi_sleep_policies"
|
>
|
||||||
android:entryValues="@array/wifi_sleep_policies_values"
|
<intent
|
||||||
>
|
android:action="android.intent.action.MAIN"
|
||||||
</ListPreference>
|
android:targetPackage="org.envaya.sms"
|
||||||
|
android:targetClass="org.envaya.sms.ui.ExpansionPacks" />
|
||||||
|
</PreferenceScreen>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="network_failover"
|
android:key="forward_sent"
|
||||||
android:title="Network failover"
|
android:title="Forward sent messages"
|
||||||
android:summaryOff="Do nothing if phone can't connect to server via Wi-Fi"
|
android:summaryOff="SMS sent from Messaging app will not be forwarded to server"
|
||||||
android:summaryOn="Automatically switch to mobile data if phone can't connect to server via Wi-Fi"
|
android:summaryOn="SMS sent from Messaging app will be forwarded to server"
|
||||||
></CheckBoxPreference>
|
></CheckBoxPreference>
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="ignored_numbers"
|
android:key="ignored_numbers"
|
||||||
@ -77,7 +85,7 @@
|
|||||||
android:action="android.intent.action.MAIN"
|
android:action="android.intent.action.MAIN"
|
||||||
android:targetPackage="org.envaya.sms"
|
android:targetPackage="org.envaya.sms"
|
||||||
android:targetClass="org.envaya.sms.ui.IgnoredPhoneNumbers" />
|
android:targetClass="org.envaya.sms.ui.IgnoredPhoneNumbers" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="test_mode"
|
android:key="test_mode"
|
||||||
@ -96,16 +104,114 @@
|
|||||||
android:action="android.intent.action.MAIN"
|
android:action="android.intent.action.MAIN"
|
||||||
android:targetPackage="org.envaya.sms"
|
android:targetPackage="org.envaya.sms"
|
||||||
android:targetClass="org.envaya.sms.ui.TestPhoneNumbers" />
|
android:targetClass="org.envaya.sms.ui.TestPhoneNumbers" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceCategory android:title="Networking Settings">
|
||||||
android:key="help"
|
|
||||||
android:title="About EnvayaSMS"
|
<ListPreference
|
||||||
>
|
android:key="wifi_sleep_policy"
|
||||||
|
android:title="Wi-Fi sleep policy"
|
||||||
|
android:defaultValue="never"
|
||||||
|
android:entries="@array/wifi_sleep_policies"
|
||||||
|
android:entryValues="@array/wifi_sleep_policies_values"
|
||||||
|
>
|
||||||
|
</ListPreference>
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="network_failover"
|
||||||
|
android:title="Network failover"
|
||||||
|
android:summaryOff="Do nothing if phone can't connect to server via Wi-Fi"
|
||||||
|
android:summaryOn="Automatically switch to mobile data if phone can't connect to server via Wi-Fi"
|
||||||
|
></CheckBoxPreference>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="AMQP Settings (Real-Time Connection)">
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="amqp_enabled"
|
||||||
|
android:title="Enable AMQP"
|
||||||
|
android:summaryOff="AMQP is disabled"
|
||||||
|
android:summaryOn="AMQP is enabled"
|
||||||
|
></CheckBoxPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_host"
|
||||||
|
android:title="AMQP Host"
|
||||||
|
android:inputType="textUri"
|
||||||
|
android:defaultValue=""
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_port"
|
||||||
|
android:title="AMQP Port"
|
||||||
|
android:inputType="number"
|
||||||
|
android:defaultValue="5672"
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_vhost"
|
||||||
|
android:title="AMQP Virtual Host"
|
||||||
|
android:inputType="text"
|
||||||
|
android:defaultValue="/"
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="amqp_ssl"
|
||||||
|
android:title="AMQP SSL"
|
||||||
|
android:summaryOff="Off (Plain text)"
|
||||||
|
android:summaryOn="On (Encrypted)"
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></CheckBoxPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_user"
|
||||||
|
android:title="AMQP User"
|
||||||
|
android:defaultValue=""
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_password"
|
||||||
|
android:title="AMQP Password"
|
||||||
|
android:defaultValue=""
|
||||||
|
android:password="true"
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_queue"
|
||||||
|
android:title="AMQP Queue Name"
|
||||||
|
android:defaultValue=""
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="amqp_heartbeat"
|
||||||
|
android:title="AMQP Heartbeat (sec)"
|
||||||
|
android:inputType="number"
|
||||||
|
android:defaultValue="60"
|
||||||
|
android:dependency="amqp_enabled"
|
||||||
|
></EditTextPreference>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="Help">
|
||||||
|
<PreferenceScreen
|
||||||
|
android:key="help"
|
||||||
|
android:title="About EnvayaSMS"
|
||||||
|
>
|
||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.MAIN"
|
android:action="android.intent.action.MAIN"
|
||||||
android:targetPackage="org.envaya.sms"
|
android:targetPackage="org.envaya.sms"
|
||||||
android:targetClass="org.envaya.sms.ui.Help" />
|
android:targetClass="org.envaya.sms.ui.Help" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -1,24 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PHP server library for EnvayaSMS
|
* PHP server library for EnvayaSMS 3.0
|
||||||
*
|
*
|
||||||
* For example usage see example/www/index.php
|
* For example usage see example/www/gateway.php
|
||||||
*/
|
*/
|
||||||
|
class EnvayaSMS
|
||||||
class EnvayaSMS
|
|
||||||
{
|
{
|
||||||
const ACTION_INCOMING = 'incoming';
|
const ACTION_INCOMING = 'incoming';
|
||||||
const ACTION_OUTGOING = 'outgoing';
|
const ACTION_FORWARD_SENT = 'forward_sent';
|
||||||
const ACTION_SEND_STATUS = 'send_status';
|
const ACTION_SEND_STATUS = 'send_status';
|
||||||
const ACTION_DEVICE_STATUS = 'device_status';
|
const ACTION_DEVICE_STATUS = 'device_status';
|
||||||
const ACTION_TEST = 'test';
|
const ACTION_TEST = 'test';
|
||||||
|
const ACTION_OUTGOING = 'outgoing';
|
||||||
|
const ACTION_AMQP_STARTED = 'amqp_started';
|
||||||
|
// ACTION_OUTGOING should probably be const ACTION_POLL = 'poll',
|
||||||
|
// but 'outgoing' maintains backwards compatibility between new phone versions with old servers
|
||||||
|
|
||||||
const STATUS_QUEUED = 'queued';
|
const STATUS_QUEUED = 'queued';
|
||||||
const STATUS_FAILED = 'failed';
|
const STATUS_FAILED = 'failed';
|
||||||
const STATUS_SENT = 'sent';
|
const STATUS_SENT = 'sent';
|
||||||
const STATUS_CANCELLED = 'cancelled';
|
const STATUS_CANCELLED = 'cancelled';
|
||||||
|
|
||||||
|
const EVENT_SEND = 'send';
|
||||||
|
const EVENT_CANCEL = 'cancel';
|
||||||
|
const EVENT_CANCEL_ALL = 'cancel_all';
|
||||||
|
const EVENT_LOG = 'log';
|
||||||
|
const EVENT_SETTINGS = 'settings';
|
||||||
|
|
||||||
const DEVICE_STATUS_POWER_CONNECTED = "power_connected";
|
const DEVICE_STATUS_POWER_CONNECTED = "power_connected";
|
||||||
const DEVICE_STATUS_POWER_DISCONNECTED = "power_disconnected";
|
const DEVICE_STATUS_POWER_DISCONNECTED = "power_disconnected";
|
||||||
const DEVICE_STATUS_BATTERY_LOW = "battery_low";
|
const DEVICE_STATUS_BATTERY_LOW = "battery_low";
|
||||||
@ -29,8 +38,10 @@ class EnvayaSMS
|
|||||||
const MESSAGE_TYPE_MMS = 'mms';
|
const MESSAGE_TYPE_MMS = 'mms';
|
||||||
const MESSAGE_TYPE_CALL = 'call';
|
const MESSAGE_TYPE_CALL = 'call';
|
||||||
|
|
||||||
const NETWORK_MOBILE = "MOBILE";
|
// power source constants same as from Android's BatteryManager.EXTRA_PLUGGED
|
||||||
const NETWORK_WIFI = "WIFI";
|
const POWER_SOURCE_BATTERY = 0;
|
||||||
|
const POWER_SOURCE_AC = 1;
|
||||||
|
const POWER_SOURCE_USB = 2;
|
||||||
|
|
||||||
static function escape($val)
|
static function escape($val)
|
||||||
{
|
{
|
||||||
@ -41,60 +52,36 @@ class EnvayaSMS
|
|||||||
|
|
||||||
static function get_request()
|
static function get_request()
|
||||||
{
|
{
|
||||||
if (!isset(static::$request))
|
if (!isset(self::$request))
|
||||||
{
|
{
|
||||||
$version = @$_POST['version'];
|
$version = @$_POST['version'];
|
||||||
|
|
||||||
// If API version changes, could return
|
if (isset($_POST['action']))
|
||||||
// different EnvayaSMS_Request instance
|
{
|
||||||
// to support multiple phone versions
|
self::$request = new EnvayaSMS_ActionRequest();
|
||||||
|
}
|
||||||
static::$request = new EnvayaSMS_Request();
|
else
|
||||||
|
{
|
||||||
|
self::$request = new EnvayaSMS_Request();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return static::$request;
|
return self::$request;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function get_error_xml($message)
|
|
||||||
{
|
|
||||||
ob_start();
|
|
||||||
echo "<?xml version='1.0' encoding='UTF-8'?>\n";
|
|
||||||
echo "<response>";
|
|
||||||
echo "<error>";
|
|
||||||
echo EnvayaSMS::escape($message);
|
|
||||||
echo "</error>";
|
|
||||||
echo "</response>";
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
static function get_success_xml()
|
|
||||||
{
|
|
||||||
ob_start();
|
|
||||||
echo "<?xml version='1.0' encoding='UTF-8'?>\n";
|
|
||||||
echo "<response></response>";
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnvayaSMS_Request
|
class EnvayaSMS_Request
|
||||||
{
|
{
|
||||||
private $request_action;
|
|
||||||
|
|
||||||
public $version;
|
public $version;
|
||||||
public $phone_number;
|
|
||||||
public $log;
|
|
||||||
|
|
||||||
public $version_name;
|
public $version_name;
|
||||||
public $sdk_int;
|
public $sdk_int;
|
||||||
public $manufacturer;
|
public $manufacturer;
|
||||||
public $model;
|
public $model;
|
||||||
public $network;
|
|
||||||
|
|
||||||
function __construct()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->version = $_POST['version'];
|
$this->version = (int)@$_POST['version'];
|
||||||
$this->phone_number = $_POST['phone_number'];
|
|
||||||
$this->log = $_POST['log'];
|
|
||||||
$this->network = @$_POST['network'];
|
|
||||||
|
|
||||||
if (preg_match('#/(?P<version_name>[\w\.\-]+) \(Android; SDK (?P<sdk_int>\d+); (?P<manufacturer>[^;]*); (?P<model>[^\)]*)\)#',
|
if (preg_match('#/(?P<version_name>[\w\.\-]+) \(Android; SDK (?P<sdk_int>\d+); (?P<manufacturer>[^;]*); (?P<model>[^\)]*)\)#',
|
||||||
@$_SERVER['HTTP_USER_AGENT'], $matches))
|
@$_SERVER['HTTP_USER_AGENT'], $matches))
|
||||||
@ -103,9 +90,115 @@ class EnvayaSMS_Request
|
|||||||
$this->sdk_int = $matches['sdk_int'];
|
$this->sdk_int = $matches['sdk_int'];
|
||||||
$this->manufacturer = $matches['manufacturer'];
|
$this->manufacturer = $matches['manufacturer'];
|
||||||
$this->model = $matches['model'];
|
$this->model = $matches['model'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function supports_json()
|
||||||
|
{
|
||||||
|
return $this->version >= 28;
|
||||||
|
}
|
||||||
|
|
||||||
|
function supports_update_settings()
|
||||||
|
{
|
||||||
|
return $this->version >= 29;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_response_type()
|
||||||
|
{
|
||||||
|
if ($this->supports_json())
|
||||||
|
{
|
||||||
|
return 'application/json';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 'text/xml';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function render_response($events = null /* optional array of EnvayaSMS_Event objects */)
|
||||||
|
{
|
||||||
|
if ($this->supports_json())
|
||||||
|
{
|
||||||
|
return json_encode(array('events' => $events));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
echo "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||||
|
echo "<response>";
|
||||||
|
|
||||||
|
if ($events)
|
||||||
|
{
|
||||||
|
foreach ($events as $event)
|
||||||
|
{
|
||||||
|
echo "<messages>";
|
||||||
|
if ($event instanceof EnvayaSMS_Event_Send)
|
||||||
|
{
|
||||||
|
if ($event->messages)
|
||||||
|
{
|
||||||
|
foreach ($event->messages as $message)
|
||||||
|
{
|
||||||
|
$type = isset($message->type) ? $message->type : EnvayaSMS::MESSAGE_TYPE_SMS;
|
||||||
|
$id = isset($message->id) ? " id=\"".EnvayaSMS::escape($message->id)."\"" : "";
|
||||||
|
$to = isset($message->to) ? " to=\"".EnvayaSMS::escape($message->to)."\"" : "";
|
||||||
|
$priority = isset($message->priority) ? " priority=\"".$message->priority."\"" : "";
|
||||||
|
echo "<$type$id$to$priority>".EnvayaSMS::escape($message->message)."</$type>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "</messages>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "</response>";
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_error_response($message)
|
||||||
|
{
|
||||||
|
if ($this->supports_json())
|
||||||
|
{
|
||||||
|
return json_encode(array('error' => array('message' => $message)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
echo "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||||
|
echo "<response>";
|
||||||
|
echo "<error>";
|
||||||
|
echo EnvayaSMS::escape($message);
|
||||||
|
echo "</error>";
|
||||||
|
echo "</response>";
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnvayaSMS_ActionRequest extends EnvayaSMS_Request
|
||||||
|
{
|
||||||
|
private $request_action;
|
||||||
|
|
||||||
|
public $settings_version; // integer version of current settings (as provided by server)
|
||||||
|
public $phone_number; // phone number of Android phone
|
||||||
|
public $log; // app log messages since last successful request
|
||||||
|
public $now; // current time (ms since Unix epoch) according to Android clock
|
||||||
|
public $network; // name of network, like WIFI or MOBILE (may vary depending on phone)
|
||||||
|
public $battery; // battery level as percentage
|
||||||
|
public $power; // power source integer, see EnvayaSMS::POWER_SOURCE_*
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->phone_number = $_POST['phone_number'];
|
||||||
|
$this->log = $_POST['log'];
|
||||||
|
$this->network = @$_POST['network'];
|
||||||
|
$this->now = @$_POST['now'];
|
||||||
|
$this->settings_version = @$_POST['settings_version'];
|
||||||
|
$this->battery = @$_POST['battery'];
|
||||||
|
$this->power = @$_POST['power'];
|
||||||
|
}
|
||||||
|
|
||||||
function get_action()
|
function get_action()
|
||||||
{
|
{
|
||||||
if (!$this->request_action)
|
if (!$this->request_action)
|
||||||
@ -121,7 +214,9 @@ class EnvayaSMS_Request
|
|||||||
{
|
{
|
||||||
case EnvayaSMS::ACTION_INCOMING:
|
case EnvayaSMS::ACTION_INCOMING:
|
||||||
return new EnvayaSMS_Action_Incoming($this);
|
return new EnvayaSMS_Action_Incoming($this);
|
||||||
case EnvayaSMS::ACTION_OUTGOING:
|
case EnvayaSMS::ACTION_FORWARD_SENT:
|
||||||
|
return new EnvayaSMS_Action_ForwardSent($this);
|
||||||
|
case EnvayaSMS::ACTION_OUTGOING:
|
||||||
return new EnvayaSMS_Action_Outgoing($this);
|
return new EnvayaSMS_Action_Outgoing($this);
|
||||||
case EnvayaSMS::ACTION_SEND_STATUS:
|
case EnvayaSMS::ACTION_SEND_STATUS:
|
||||||
return new EnvayaSMS_Action_SendStatus($this);
|
return new EnvayaSMS_Action_SendStatus($this);
|
||||||
@ -129,6 +224,8 @@ class EnvayaSMS_Request
|
|||||||
return new EnvayaSMS_Action_Test($this);
|
return new EnvayaSMS_Action_Test($this);
|
||||||
case EnvayaSMS::ACTION_DEVICE_STATUS:
|
case EnvayaSMS::ACTION_DEVICE_STATUS:
|
||||||
return new EnvayaSMS_Action_DeviceStatus($this);
|
return new EnvayaSMS_Action_DeviceStatus($this);
|
||||||
|
case EnvayaSMS::ACTION_AMQP_STARTED:
|
||||||
|
return new EnvayaSMS_Action_AmqpStarted($this);
|
||||||
default:
|
default:
|
||||||
return new EnvayaSMS_Action($this);
|
return new EnvayaSMS_Action($this);
|
||||||
}
|
}
|
||||||
@ -163,29 +260,8 @@ class EnvayaSMS_Request
|
|||||||
|
|
||||||
$input .= ",$password";
|
$input .= ",$password";
|
||||||
|
|
||||||
//error_log("Signed data: '$input'");
|
|
||||||
|
|
||||||
return base64_encode(sha1($input, true));
|
return base64_encode(sha1($input, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
static function get_messages_xml($messages)
|
|
||||||
{
|
|
||||||
ob_start();
|
|
||||||
echo "<?xml version='1.0' encoding='UTF-8'?>\n";
|
|
||||||
echo "<response>";
|
|
||||||
echo "<messages>";
|
|
||||||
foreach ($messages as $message)
|
|
||||||
{
|
|
||||||
$type = isset($message->type) ? $message->type : EnvayaSMS::MESSAGE_TYPE_SMS;
|
|
||||||
$id = isset($message->id) ? " id=\"".EnvayaSMS::escape($message->id)."\"" : "";
|
|
||||||
$to = isset($message->to) ? " to=\"".EnvayaSMS::escape($message->to)."\"" : "";
|
|
||||||
$priority = isset($message->priority) ? " priority=\"".$message->priority."\"" : "";
|
|
||||||
echo "<$type$id$to$priority>".EnvayaSMS::escape($message->message)."</$type>";
|
|
||||||
}
|
|
||||||
echo "</messages>";
|
|
||||||
echo "</response>";
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnvayaSMS_OutgoingMessage
|
class EnvayaSMS_OutgoingMessage
|
||||||
@ -197,6 +273,10 @@ class EnvayaSMS_OutgoingMessage
|
|||||||
public $type; // EnvayaSMS::MESSAGE_TYPE_* value (default sms)
|
public $type; // EnvayaSMS::MESSAGE_TYPE_* value (default sms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An 'action' is the term for a HTTP request that app sends to the server.
|
||||||
|
*/
|
||||||
|
|
||||||
class EnvayaSMS_Action
|
class EnvayaSMS_Action
|
||||||
{
|
{
|
||||||
public $type;
|
public $type;
|
||||||
@ -233,24 +313,19 @@ class EnvayaSMS_MMS_Part
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnvayaSMS_Action_Incoming extends EnvayaSMS_Action
|
abstract class EnvayaSMS_Action_Forward extends EnvayaSMS_Action
|
||||||
{
|
{
|
||||||
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; // The message body of the SMS, or the content of the text/plain part of the MMS.
|
||||||
public $message_type; // EnvayaSMS::MESSAGE_TYPE_MMS or EnvayaSMS::MESSAGE_TYPE_SMS
|
public $message_type; // EnvayaSMS::MESSAGE_TYPE_MMS or EnvayaSMS::MESSAGE_TYPE_SMS
|
||||||
public $mms_parts; // array of EnvayaSMS_MMS_Part instances
|
public $mms_parts; // array of EnvayaSMS_MMS_Part instances
|
||||||
public $timestamp; // timestamp of incoming message (added in version 12)
|
public $timestamp; // timestamp of incoming message (added in version 12)
|
||||||
public $age; // delay in ms between time when message originally received and when forwarded to server (added in version 18)
|
|
||||||
|
|
||||||
function __construct($request)
|
function __construct($request)
|
||||||
{
|
{
|
||||||
parent::__construct($request);
|
parent::__construct($request);
|
||||||
$this->type = EnvayaSMS::ACTION_INCOMING;
|
|
||||||
$this->from = $_POST['from'];
|
|
||||||
$this->message = @$_POST['message'];
|
$this->message = @$_POST['message'];
|
||||||
$this->message_type = $_POST['message_type'];
|
$this->message_type = $_POST['message_type'];
|
||||||
$this->timestamp = @$_POST['timestamp'];
|
$this->timestamp = @$_POST['timestamp'];
|
||||||
$this->age = @$_POST['age'];
|
|
||||||
|
|
||||||
if ($this->message_type == EnvayaSMS::MESSAGE_TYPE_MMS)
|
if ($this->message_type == EnvayaSMS::MESSAGE_TYPE_MMS)
|
||||||
{
|
{
|
||||||
@ -260,26 +335,52 @@ class EnvayaSMS_Action_Incoming extends EnvayaSMS_Action
|
|||||||
$this->mms_parts[] = new EnvayaSMS_MMS_Part($mms_part);
|
$this->mms_parts[] = new EnvayaSMS_MMS_Part($mms_part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function get_response_xml($messages)
|
|
||||||
|
class EnvayaSMS_Action_Incoming extends EnvayaSMS_Action_Forward
|
||||||
|
{
|
||||||
|
public $from; // Sender phone number
|
||||||
|
|
||||||
|
function __construct($request)
|
||||||
{
|
{
|
||||||
return $this->request->get_messages_xml($messages);
|
parent::__construct($request);
|
||||||
|
$this->type = EnvayaSMS::ACTION_INCOMING;
|
||||||
|
$this->from = $_POST['from'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EnvayaSMS_Action_ForwardSent extends EnvayaSMS_Action_Forward
|
||||||
|
{
|
||||||
|
public $to; // Recipient phone number
|
||||||
|
|
||||||
|
function __construct($request)
|
||||||
|
{
|
||||||
|
parent::__construct($request);
|
||||||
|
$this->type = EnvayaSMS::ACTION_FORWARD_SENT;
|
||||||
|
$this->to = $_POST['to'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnvayaSMS_Action_AmqpStarted extends EnvayaSMS_Action
|
||||||
|
{
|
||||||
|
public $consumer_tag;
|
||||||
|
|
||||||
|
function __construct($request)
|
||||||
|
{
|
||||||
|
parent::__construct($request);
|
||||||
|
$this->type = EnvayaSMS::ACTION_AMQP_STARTED;
|
||||||
|
$this->consumer_tag = $_POST['consumer_tag'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class EnvayaSMS_Action_Outgoing extends EnvayaSMS_Action
|
class EnvayaSMS_Action_Outgoing extends EnvayaSMS_Action
|
||||||
{
|
{
|
||||||
function __construct($request)
|
function __construct($request)
|
||||||
{
|
{
|
||||||
parent::__construct($request);
|
parent::__construct($request);
|
||||||
$this->type = EnvayaSMS::ACTION_OUTGOING;
|
$this->type = EnvayaSMS::ACTION_OUTGOING;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_response_xml($messages)
|
|
||||||
{
|
|
||||||
return $this->request->get_messages_xml($messages);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnvayaSMS_Action_Test extends EnvayaSMS_Action
|
class EnvayaSMS_Action_Test extends EnvayaSMS_Action
|
||||||
@ -318,3 +419,89 @@ class EnvayaSMS_Action_DeviceStatus extends EnvayaSMS_Action
|
|||||||
$this->status = $_POST['status'];
|
$this->status = $_POST['status'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An 'event' is the term for something the server sends to the app,
|
||||||
|
* either via a response to an 'action', or directly via AMQP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EnvayaSMS_Event
|
||||||
|
{
|
||||||
|
public $event;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Formats this event as the body of an AMQP message.
|
||||||
|
*/
|
||||||
|
function render()
|
||||||
|
{
|
||||||
|
return json_encode($this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Instruct the phone to send one or more outgoing messages (SMS or USSD)
|
||||||
|
*/
|
||||||
|
class EnvayaSMS_Event_Send extends EnvayaSMS_Event
|
||||||
|
{
|
||||||
|
public $messages;
|
||||||
|
|
||||||
|
function __construct($messages /* array of EnvayaSMS_OutgoingMessage objects */)
|
||||||
|
{
|
||||||
|
$this->event = EnvayaSMS::EVENT_SEND;
|
||||||
|
$this->messages = $messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update some of the app's settings.
|
||||||
|
*/
|
||||||
|
class EnvayaSMS_Event_Settings extends EnvayaSMS_Event
|
||||||
|
{
|
||||||
|
public $settings;
|
||||||
|
|
||||||
|
function __construct($settings /* associative array of key => value pairs (values can be int, bool, or string) */)
|
||||||
|
{
|
||||||
|
$this->event = EnvayaSMS::EVENT_SETTINGS;
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cancel sending a message that was previously queued in the app via a 'send' event.
|
||||||
|
* Has no effect if the message has already been sent.
|
||||||
|
*/
|
||||||
|
class EnvayaSMS_Event_Cancel extends EnvayaSMS_Event
|
||||||
|
{
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
function __construct($id /* id of previously created EnvayaSMS_OutgoingMessage object (string) */)
|
||||||
|
{
|
||||||
|
$this->event = EnvayaSMS::EVENT_CANCEL;
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cancels all outgoing messages that are currently queued in the app. Incoming mesages are not affected.
|
||||||
|
*/
|
||||||
|
class EnvayaSMS_Event_CancelAll extends EnvayaSMS_Event
|
||||||
|
{
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->event = EnvayaSMS::EVENT_CANCEL_ALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Appends a message to the app log.
|
||||||
|
*/
|
||||||
|
class EnvayaSMS_Event_Log extends EnvayaSMS_Event
|
||||||
|
{
|
||||||
|
public $message;
|
||||||
|
|
||||||
|
function __construct($message)
|
||||||
|
{
|
||||||
|
$this->event = EnvayaSMS::EVENT_LOG;
|
||||||
|
$this->message = $message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,19 +10,21 @@ PHP web server, by running the following commands:
|
|||||||
git submodule init
|
git submodule init
|
||||||
php server.php
|
php server.php
|
||||||
|
|
||||||
example/config.php contains the list of phone numbers and passwords for phones running EnvayaSMS.
|
example/config.php contains the password for a phone running EnvayaSMS. The password
|
||||||
|
This password must match the password in the EnvayaSMS app settings,
|
||||||
|
otherwise example/gateway.php will return an "Invalid password" error.
|
||||||
|
|
||||||
On a phone running EnvayaSMS, go to Menu -> Settings and enter:
|
On a phone running EnvayaSMS, go to Menu -> Settings and enter:
|
||||||
* Server URL: The URL to example/www/index.php.
|
* Server URL: The URL to example/www/gateway.php.
|
||||||
If you're using server.php, this will be http://<your_ip_address>:8002/
|
If you're using server.php, this will be http://<your_ip_address>:8002/gateway.php
|
||||||
* Your phone number: One of the phone numbers listed in example/config.php
|
* Your phone number: The phone number of your Android phone
|
||||||
* Password: The corresponding password in example/config.php
|
* Password: The password in example/config.php
|
||||||
|
|
||||||
To send an outgoing SMS, use
|
To send an outgoing SMS, use
|
||||||
php example/send_sms.php
|
php example/send_sms.php
|
||||||
|
|
||||||
example/www/test.html allows you to simulate the HTTP requests made by EnvayaSMS
|
example/www/test.html allows you to simulate the HTTP requests made by EnvayaSMS
|
||||||
in your browser without actually using the EnvayaSMS app.
|
in your browser without actually using the EnvayaSMS app.
|
||||||
If you're using server.php, just go to http://<your_ip_address>:8002/test.html
|
If you're using server.php, just go to http://<your_ip_address>:8002/test.html
|
||||||
|
|
||||||
See EnvayaSMS.php and example/www/index.php
|
See EnvayaSMS.php and example/www/gateway.php
|
||||||
|
@ -1,9 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$PASSWORDS = array(
|
ini_set('display_errors','0');
|
||||||
'16505551212' => 'rosebud',
|
|
||||||
'16505551213' => 's3krit',
|
|
||||||
);
|
|
||||||
$PHONE_NUMBERS = array_keys($PASSWORDS);
|
|
||||||
|
|
||||||
$OUTGOING_DIR_NAME = __DIR__."/outgoing_sms";
|
/*
|
||||||
|
* This password must match the password in the EnvayaSMS app settings,
|
||||||
|
* otherwise example/www/gateway.php will return an "Invalid request signature" error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$PASSWORD = 'rosebud';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* example/send_sms.php uses the local file system to queue outgoing messages
|
||||||
|
* in this directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$OUTGOING_DIR_NAME = __DIR__."/outgoing_sms";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AMQP allows you to send outgoing messages in real-time (i.e. 'push' instead of polling).
|
||||||
|
* In order to use AMQP, you would need to install an AMQP server such as RabbitMQ, and
|
||||||
|
* also enter the AMQP connection settings in the app. (The settings in the EnvayaSMS app
|
||||||
|
* should use the same vhost and queue name, but may use a different host/port/user/password.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
$AMQP_SETTINGS = array(
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 5672,
|
||||||
|
'user' => 'guest',
|
||||||
|
'password' => 'guest',
|
||||||
|
'vhost' => '/',
|
||||||
|
'queue_name' => "envayasms"
|
||||||
|
);
|
||||||
|
1
server/php/example/php-amqplib
Submodule
1
server/php/example/php-amqplib
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 056e94d28d14466c19a4eccb15f2d5e8e7ba017d
|
@ -1,46 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Command line script to simulate sending an outgoing SMS from the server.
|
* Command line script to send an outgoing SMS from the server.
|
||||||
*
|
*
|
||||||
* The message will be queued on the server until the next time
|
* This example script queues outgoing messages using the local filesystem.
|
||||||
* EnvayaSMS checks for outgoing messages.
|
* The messages are sent the next time EnvayaSMS sends an ACTION_OUTGOING request to www/gateway.php.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once __DIR__."/config.php";
|
require_once __DIR__."/config.php";
|
||||||
|
require_once dirname(__DIR__)."/EnvayaSMS.php";
|
||||||
|
|
||||||
$arg_len = sizeof($argv);
|
if (sizeof($argv) == 3)
|
||||||
|
|
||||||
if ($arg_len == 4)
|
|
||||||
{
|
|
||||||
$from = $argv[1];
|
|
||||||
$to = $argv[2];
|
|
||||||
$message = $argv[3];
|
|
||||||
}
|
|
||||||
else if ($arg_len == 3)
|
|
||||||
{
|
{
|
||||||
$from = $PHONE_NUMBERS[0];
|
|
||||||
$to = $argv[1];
|
$to = $argv[1];
|
||||||
$message = $argv[2];
|
$body = $argv[2];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error_log("Usage: php send_sms.php [<from>] <to> \"<message>\"");
|
error_log("Usage: php send_sms.php <to> \"<message>\"");
|
||||||
error_log("Examples: ");
|
error_log("Example: ");
|
||||||
error_log(" php send_sms.php 16505551212 16504449876 \"hello world\"");
|
|
||||||
error_log(" php send_sms.php 16504449876 \"hello world\"");
|
error_log(" php send_sms.php 16504449876 \"hello world\"");
|
||||||
die;
|
die;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = uniqid("");
|
$message = new EnvayaSMS_OutgoingMessage();
|
||||||
|
$message->id = uniqid("");
|
||||||
|
$message->to = $to;
|
||||||
|
$message->message = $body;
|
||||||
|
|
||||||
$filename = "$OUTGOING_DIR_NAME/$id.json";
|
file_put_contents("$OUTGOING_DIR_NAME/{$message->id}.json", json_encode($message));
|
||||||
|
|
||||||
file_put_contents($filename, json_encode(array(
|
echo "Message {$message->id} added to filesystem queue\n";
|
||||||
'from' => $from,
|
|
||||||
'to' => $to,
|
|
||||||
'message' => $message,
|
|
||||||
'id' => $id
|
|
||||||
)));
|
|
||||||
|
|
||||||
echo "Message $id added to outgoing queue\n";
|
|
||||||
|
45
server/php/example/send_sms_amqp.php
Normal file
45
server/php/example/send_sms_amqp.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command line script to send an outgoing SMS from the server.
|
||||||
|
*
|
||||||
|
* Requires an AMQP server to be configured in config.php, and
|
||||||
|
* pushes SMS to the phone immediately using the real-time connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__."/config.php";
|
||||||
|
require_once dirname(__DIR__)."/EnvayaSMS.php";
|
||||||
|
require_once __DIR__."/php-amqplib/amqp.inc";
|
||||||
|
|
||||||
|
if (sizeof($argv) == 3)
|
||||||
|
{
|
||||||
|
$to = $argv[1];
|
||||||
|
$body = $argv[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error_log("Usage: php send_sms_amqp.php <to> \"<message>\"");
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = new EnvayaSMS_OutgoingMessage();
|
||||||
|
$message->id = uniqid("");
|
||||||
|
$message->to = $to;
|
||||||
|
$message->message = $body;
|
||||||
|
|
||||||
|
$conn = new AMQPConnection($AMQP_SETTINGS['host'], $AMQP_SETTINGS['port'],
|
||||||
|
$AMQP_SETTINGS['user'], $AMQP_SETTINGS['password'], $AMQP_SETTINGS['vhost']);
|
||||||
|
|
||||||
|
$ch = $conn->channel();
|
||||||
|
$ch->queue_declare($AMQP_SETTINGS['queue_name'], false, true, false, false);
|
||||||
|
|
||||||
|
$event = new EnvayaSMS_Event_Send(array($message));
|
||||||
|
|
||||||
|
$msg = new AMQPMessage($event->render(), array('content_type' => 'application/json', 'delivery-mode' => 2));
|
||||||
|
|
||||||
|
$ch->basic_publish($msg, '', $AMQP_SETTINGS['queue_name']);
|
||||||
|
|
||||||
|
$ch->close();
|
||||||
|
$conn->close();
|
||||||
|
|
||||||
|
echo "Message {$message->id} added to AMQP queue\n";
|
87
server/php/example/www/index.php → server/php/example/www/gateway.php
Executable file → Normal file
87
server/php/example/www/index.php → server/php/example/www/gateway.php
Executable file → Normal file
@ -1,44 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This example script implements the EnvayaSMS API.
|
||||||
|
*
|
||||||
|
* It sends an auto-reply to each incoming message, and sends outgoing SMS
|
||||||
|
* that were previously queued by example/send_sms.php .
|
||||||
|
*
|
||||||
|
* To use this file, set the URL to this file as as the the Server URL in the EnvayaSMS app.
|
||||||
|
* The password in the EnvayaSMS app settings must be the same as $PASSWORD in config.php.
|
||||||
|
*/
|
||||||
|
|
||||||
require_once dirname(__DIR__)."/config.php";
|
require_once dirname(__DIR__)."/config.php";
|
||||||
require_once dirname(dirname(__DIR__))."/EnvayaSMS.php";
|
require_once dirname(dirname(__DIR__))."/EnvayaSMS.php";
|
||||||
|
|
||||||
ini_set('display_errors','0');
|
|
||||||
|
|
||||||
// this example implementation uses the filesystem to store outgoing SMS messages,
|
|
||||||
// but presumably a production implementation would use another storage method
|
|
||||||
|
|
||||||
$request = EnvayaSMS::get_request();
|
$request = EnvayaSMS::get_request();
|
||||||
|
|
||||||
$phone_number = $request->phone_number;
|
header("Content-Type: {$request->get_response_type()}");
|
||||||
|
|
||||||
$password = @$PASSWORDS[$phone_number];
|
if (!$request->is_validated($PASSWORD))
|
||||||
|
|
||||||
header("Content-Type: text/xml");
|
|
||||||
|
|
||||||
if (!isset($password) || !$request->is_validated($password))
|
|
||||||
{
|
{
|
||||||
header("HTTP/1.1 403 Forbidden");
|
header("HTTP/1.1 403 Forbidden");
|
||||||
error_log("Invalid request signature");
|
error_log("Invalid password");
|
||||||
echo EnvayaSMS::get_error_xml("Invalid request signature");
|
echo $request->render_error_response("Invalid password");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append to EnvayaSMS app log
|
|
||||||
$app_log = $request->log;
|
|
||||||
if ($app_log)
|
|
||||||
{
|
|
||||||
$log_file = dirname(__DIR__)."/log/sms_".preg_replace('#[^\w]#', '', $request->phone_number).".log";
|
|
||||||
$f = fopen($log_file, "a");
|
|
||||||
fwrite($f, $app_log);
|
|
||||||
fclose($f);
|
|
||||||
}
|
|
||||||
|
|
||||||
$action = $request->get_action();
|
$action = $request->get_action();
|
||||||
|
|
||||||
switch ($action->type)
|
switch ($action->type)
|
||||||
{
|
{
|
||||||
case EnvayaSMS::ACTION_INCOMING:
|
case EnvayaSMS::ACTION_INCOMING:
|
||||||
|
|
||||||
|
// Send an auto-reply for each incoming message.
|
||||||
|
|
||||||
$type = strtoupper($action->message_type);
|
$type = strtoupper($action->message_type);
|
||||||
|
|
||||||
error_log("Received $type from {$action->from}");
|
error_log("Received $type from {$action->from}");
|
||||||
@ -62,25 +56,29 @@ switch ($action->type)
|
|||||||
$reply->message = "You said: {$action->message}";
|
$reply->message = "You said: {$action->message}";
|
||||||
|
|
||||||
error_log("Sending reply: {$reply->message}");
|
error_log("Sending reply: {$reply->message}");
|
||||||
|
|
||||||
echo $action->get_response_xml(array($reply));
|
echo $request->render_response(array(
|
||||||
|
new EnvayaSMS_Event_Send(array($reply))
|
||||||
|
));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case EnvayaSMS::ACTION_OUTGOING:
|
case EnvayaSMS::ACTION_OUTGOING:
|
||||||
$messages = array();
|
$messages = array();
|
||||||
|
|
||||||
|
// In this example implementation, outgoing SMS messages are queued
|
||||||
|
// on the local file system by send_sms.php.
|
||||||
|
|
||||||
$dir = opendir($OUTGOING_DIR_NAME);
|
$dir = opendir($OUTGOING_DIR_NAME);
|
||||||
while ($file = readdir($dir))
|
while ($file = readdir($dir))
|
||||||
{
|
{
|
||||||
if (preg_match('#\.json$#', $file))
|
if (preg_match('#\.json$#', $file))
|
||||||
{
|
{
|
||||||
$data = json_decode(file_get_contents("$OUTGOING_DIR_NAME/$file"), true);
|
$data = json_decode(file_get_contents("$OUTGOING_DIR_NAME/$file"), true);
|
||||||
if ($data && @$data['from'] == $phone_number)
|
if ($data)
|
||||||
{
|
{
|
||||||
$sms = new EnvayaSMS_OutgoingMessage();
|
$sms = new EnvayaSMS_OutgoingMessage();
|
||||||
$sms->id = $data['id'];
|
$sms->id = $data['id'];
|
||||||
$sms->to = $data['to'];
|
$sms->to = $data['to'];
|
||||||
$sms->from = $data['from'];
|
|
||||||
$sms->message = $data['message'];
|
$sms->message = $data['message'];
|
||||||
$messages[] = $sms;
|
$messages[] = $sms;
|
||||||
}
|
}
|
||||||
@ -88,33 +86,40 @@ switch ($action->type)
|
|||||||
}
|
}
|
||||||
closedir($dir);
|
closedir($dir);
|
||||||
|
|
||||||
echo $action->get_response_xml($messages);
|
$events = array();
|
||||||
|
|
||||||
|
if ($messages)
|
||||||
|
{
|
||||||
|
$events[] = new EnvayaSMS_Event_Send($messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $request->render_response($events);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case EnvayaSMS::ACTION_SEND_STATUS:
|
case EnvayaSMS::ACTION_SEND_STATUS:
|
||||||
|
|
||||||
$id = $action->id;
|
$id = $action->id;
|
||||||
|
|
||||||
|
error_log("message $id status: {$action->status}");
|
||||||
|
|
||||||
// delete file with matching id
|
// delete file with matching id
|
||||||
if (preg_match('#^\w+$#', $id) && unlink("$OUTGOING_DIR_NAME/$id.json"))
|
if (preg_match('#^\w+$#', $id))
|
||||||
{
|
{
|
||||||
echo EnvayaSMS::get_success_xml();
|
unlink("$OUTGOING_DIR_NAME/$id.json");
|
||||||
}
|
}
|
||||||
else
|
echo $request->render_response();
|
||||||
{
|
|
||||||
header("HTTP/1.1 404 Not Found");
|
|
||||||
echo EnvayaSMS::get_error_xml("Invalid id");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
case EnvayaSMS::ACTION_DEVICE_STATUS:
|
case EnvayaSMS::ACTION_DEVICE_STATUS:
|
||||||
error_log("device_status = {$action->status}");
|
error_log("device_status = {$action->status}");
|
||||||
echo EnvayaSMS::get_success_xml();
|
echo $request->render_response();
|
||||||
return;
|
return;
|
||||||
case EnvayaSMS::ACTION_TEST:
|
case EnvayaSMS::ACTION_TEST:
|
||||||
echo EnvayaSMS::get_success_xml();
|
echo $request->render_response();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
header("HTTP/1.1 404 Not Found");
|
header("HTTP/1.1 404 Not Found");
|
||||||
echo EnvayaSMS::get_error_xml("Invalid action");
|
echo $request->render_error_response("The server does not support the requested action.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
87
server/php/example/www/gateway_amqp.php
Normal file
87
server/php/example/www/gateway_amqp.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An example implementation of the EnvayaSMS server API that uses AMQP
|
||||||
|
* to send outgoing messages in real-time, intended to be used together with
|
||||||
|
* example/send_sms_amqp.php.
|
||||||
|
*
|
||||||
|
* To use this file, set the URL to this file as as the the Server URL in the EnvayaSMS app.
|
||||||
|
* The password in the EnvayaSMS app settings must be the same as $PASSWORD in config.php.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once dirname(__DIR__)."/config.php";
|
||||||
|
require_once dirname(dirname(__DIR__))."/EnvayaSMS.php";
|
||||||
|
|
||||||
|
$request = EnvayaSMS::get_request();
|
||||||
|
|
||||||
|
header("Content-Type: {$request->get_response_type()}");
|
||||||
|
|
||||||
|
if (!$request->is_validated($PASSWORD))
|
||||||
|
{
|
||||||
|
header("HTTP/1.1 403 Forbidden");
|
||||||
|
error_log("Invalid password");
|
||||||
|
echo $request->render_error_response("Invalid password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$action = $request->get_action();
|
||||||
|
|
||||||
|
switch ($action->type)
|
||||||
|
{
|
||||||
|
case EnvayaSMS::ACTION_INCOMING:
|
||||||
|
|
||||||
|
// Doesn't do anything with incoming messages
|
||||||
|
|
||||||
|
error_log("Received {$action->message_type} from {$action->from}: {$action->message}");
|
||||||
|
echo $request->render_response();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EnvayaSMS::ACTION_OUTGOING:
|
||||||
|
|
||||||
|
// Doesn't need to do anything when polling for outgoing messages
|
||||||
|
// since they should be sent via the AMQP connection.
|
||||||
|
|
||||||
|
// Optionally, you could use AMQP basic_get to retrieve any messages
|
||||||
|
// from the AMQP queue so that it works in both polling and push modes.
|
||||||
|
|
||||||
|
error_log("No messages here, use AMQP instead");
|
||||||
|
echo $request->render_response(array(
|
||||||
|
new EnvayaSMS_Event_Log("No messages via polling, use AMQP instead")
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EnvayaSMS::ACTION_AMQP_STARTED:
|
||||||
|
|
||||||
|
// The main point of this action is to allow the server to kick off old
|
||||||
|
// AMQP connections (that weren't closed properly) before their heartbeat timeout
|
||||||
|
// expires. This makes it possible to use long heartbeat timeouts to maximize
|
||||||
|
// the phone's battery life.
|
||||||
|
|
||||||
|
// With RabbitMQ, this can be done using the management API:
|
||||||
|
|
||||||
|
// GET /queues/VHOST/QUEUE_NAME
|
||||||
|
// to get the connection name for each consumer other than the current one
|
||||||
|
|
||||||
|
// DELETE /connections/CONNECTION_NAME
|
||||||
|
// to close the connection for each consumer other than the current one
|
||||||
|
|
||||||
|
error_log("AMQP connection started with consumer tag {$action->consumer_tag}");
|
||||||
|
echo $request->render_response();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EnvayaSMS::ACTION_SEND_STATUS:
|
||||||
|
error_log("message {$action->id} status: {$action->status}");
|
||||||
|
echo $request->render_response();
|
||||||
|
return;
|
||||||
|
case EnvayaSMS::ACTION_DEVICE_STATUS:
|
||||||
|
error_log("device_status = {$action->status}");
|
||||||
|
echo $request->render_response();
|
||||||
|
return;
|
||||||
|
case EnvayaSMS::ACTION_TEST:
|
||||||
|
echo $request->render_response();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
header("HTTP/1.1 404 Not Found");
|
||||||
|
echo $request->render_error_response("The server does not support the requested action.");
|
||||||
|
return;
|
||||||
|
}
|
@ -31,13 +31,26 @@ body
|
|||||||
<tr><th>Phone Number</th><td><input id='phone_number' type='text' /></td></tr>
|
<tr><th>Phone Number</th><td><input id='phone_number' type='text' /></td></tr>
|
||||||
<tr><th>Password</th><td><input id='password' type='password' /></td></tr>
|
<tr><th>Password</th><td><input id='password' type='password' /></td></tr>
|
||||||
<tr><th>Log Messages</th><td><textarea id='log' style='width:250px'></textarea></td></tr>
|
<tr><th>Log Messages</th><td><textarea id='log' style='width:250px'></textarea></td></tr>
|
||||||
|
<tr><th>Send Limit</th><td><input id='send_limit' value='100' type='text' /></td></tr>
|
||||||
|
<tr><th>Settings Version</th><td><input id='settings_version' value='1' type='text' /></td></tr>
|
||||||
|
<tr><th>Battery</th><td><input id='battery' value='100' type='text' /></td></tr>
|
||||||
|
<tr><th>Power Source</th><td><select id='power'>
|
||||||
|
<option value='0'>0 (battery)</option>
|
||||||
|
<option value='1'>1 (USB)</option>
|
||||||
|
<option value='2'>2 (AC)</option>
|
||||||
|
</select></td></tr>
|
||||||
|
<tr><th>Network Type</th><td><input id='network' value='WIFI' type='text' /></td></tr>
|
||||||
|
<tr><th>Current Timestamp</th><td><input id='now' type='text' /></td></tr>
|
||||||
|
|
||||||
<tr><th>Action</th><td><select id='action' onchange='actionChanged()' onkeypress='actionChanged()'>
|
<tr><th>Action</th><td><select id='action' onchange='actionChanged()' onkeypress='actionChanged()'>
|
||||||
<option value='incoming'>incoming</option>
|
<option value='incoming'>incoming</option>
|
||||||
<option value='outgoing'>outgoing</option>
|
<option value='outgoing'>outgoing</option>
|
||||||
<option value='send_status'>send_status</option>
|
<option value='send_status'>send_status</option>
|
||||||
<option value='device_status'>device_status</option>
|
<option value='device_status'>device_status</option>
|
||||||
<option value='test'>test</option>
|
<option value='test'>test</option>
|
||||||
|
<option value='amqp_started'>amqp_started</option>
|
||||||
</select></td></tr>
|
</select></td></tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id='action_incoming'>
|
<div id='action_incoming'>
|
||||||
@ -47,6 +60,7 @@ body
|
|||||||
<tr><th>Message Type</th><td><select id='message_type'>
|
<tr><th>Message Type</th><td><select id='message_type'>
|
||||||
<option value='sms'>sms</option>
|
<option value='sms'>sms</option>
|
||||||
<option value='mms'>mms</option>
|
<option value='mms'>mms</option>
|
||||||
|
<option value='call'>call</option>
|
||||||
</select></td></tr>
|
</select></td></tr>
|
||||||
<tr><th>Message</th><td><textarea id='message' style='width:250px'></textarea></td></tr>
|
<tr><th>Message</th><td><textarea id='message' style='width:250px'></textarea></td></tr>
|
||||||
<tr><th>Timestamp</th><td><input id='timestamp' type='text' /></td></tr>
|
<tr><th>Timestamp</th><td><input id='timestamp' type='text' /></td></tr>
|
||||||
@ -64,6 +78,7 @@ body
|
|||||||
<option value='sent'>sent</option>
|
<option value='sent'>sent</option>
|
||||||
<option value='failed'>failed</option>
|
<option value='failed'>failed</option>
|
||||||
<option value='queued'>queued</option>
|
<option value='queued'>queued</option>
|
||||||
|
<option value='cancelled'>cancelled</option>
|
||||||
</select></td></tr>
|
</select></td></tr>
|
||||||
<tr><th>Error Message</th><td><input id='error' type='text' size='50' /></td></tr>
|
<tr><th>Error Message</th><td><input id='error' type='text' size='50' /></td></tr>
|
||||||
</table>
|
</table>
|
||||||
@ -80,10 +95,16 @@ body
|
|||||||
<option value='power_disconnected'>power_disconnected</option>
|
<option value='power_disconnected'>power_disconnected</option>
|
||||||
<option value='battery_low'>battery_low</option>
|
<option value='battery_low'>battery_low</option>
|
||||||
<option value='battery_okay'>battery_okay</option>
|
<option value='battery_okay'>battery_okay</option>
|
||||||
|
<option value='send_limit_exceeded'>send_limit_exceeded</option>
|
||||||
</select></td></tr>
|
</select></td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div id='action_amqp_started' style='display:none'>
|
||||||
|
<h4>Parameters for action=amqp_started:</h4>
|
||||||
|
<table class='smsTable'>
|
||||||
|
<tr><th>Consumer Tag</th><td><input id='consumer_tag' type='text' /></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type='text/javascript'>
|
<script type='text/javascript'>
|
||||||
|
|
||||||
@ -110,11 +131,17 @@ function performAction() {
|
|||||||
var action = $('action').value;
|
var action = $('action').value;
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
version: '13',
|
version: '29',
|
||||||
phone_number: $('phone_number').value,
|
phone_number: $('phone_number').value,
|
||||||
action: action,
|
action: action,
|
||||||
log: $('log').value
|
log: $('log').value,
|
||||||
};
|
send_limit: $('send_limit').value,
|
||||||
|
settings_version: $('settings_version').value,
|
||||||
|
battery: $('battery').value,
|
||||||
|
power: $('power').value,
|
||||||
|
network: $('network').value,
|
||||||
|
now: $('now').value
|
||||||
|
};
|
||||||
|
|
||||||
if (action == 'incoming')
|
if (action == 'incoming')
|
||||||
{
|
{
|
||||||
@ -133,6 +160,10 @@ function performAction() {
|
|||||||
{
|
{
|
||||||
params.status = $('device_status').value;
|
params.status = $('device_status').value;
|
||||||
}
|
}
|
||||||
|
else if (action == 'amqp_started')
|
||||||
|
{
|
||||||
|
params.status = $('consumer_tag').value;
|
||||||
|
}
|
||||||
|
|
||||||
var xhr = (window.ActiveXObject && !window.XMLHttpRequest) ? new ActiveXObject("Msxml2.XMLHTTP") : new XMLHttpRequest();
|
var xhr = (window.ActiveXObject && !window.XMLHttpRequest) ? new ActiveXObject("Msxml2.XMLHTTP") : new XMLHttpRequest();
|
||||||
|
|
||||||
@ -189,8 +220,9 @@ function performAction() {
|
|||||||
xhr.send(paramStr);
|
xhr.send(paramStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
$('server_url').value = location.href.replace("test.html","");
|
$('server_url').value = location.href.replace("test.html","gateway.php");
|
||||||
$('timestamp').value = new Date().getTime();
|
$('timestamp').value = new Date().getTime();
|
||||||
|
$('now').value = new Date().getTime();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
464
src/org/envaya/sms/AmqpConsumer.java
Normal file
464
src/org/envaya/sms/AmqpConsumer.java
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
package org.envaya.sms;
|
||||||
|
|
||||||
|
import org.envaya.sms.service.AmqpConsumerService;
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import com.rabbitmq.client.ConnectionFactory;
|
||||||
|
import com.rabbitmq.client.Connection;
|
||||||
|
import com.rabbitmq.client.Channel;
|
||||||
|
import com.rabbitmq.client.QueueingConsumer;
|
||||||
|
import com.rabbitmq.client.AlreadyClosedException;
|
||||||
|
import com.rabbitmq.client.ShutdownSignalException;
|
||||||
|
import org.envaya.sms.receiver.StartAmqpConsumer;
|
||||||
|
import org.envaya.sms.service.AmqpHeartbeatService;
|
||||||
|
import org.envaya.sms.task.HttpTask;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.concurrent.Delayed;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class AmqpConsumer {
|
||||||
|
|
||||||
|
public static final long RETRY_DELAY = 5000; // ms
|
||||||
|
public static final long RETRY_ERROR_DELAY = 60000; // ms
|
||||||
|
public static final long MIN_EXPECTED_ERROR_DELAY = 10000; // ms
|
||||||
|
|
||||||
|
public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
|
||||||
|
// constant not added until Android 3.1 but seems to work on older versions
|
||||||
|
// (at least 2.3)
|
||||||
|
|
||||||
|
protected Channel channel = null;
|
||||||
|
protected Connection connection;
|
||||||
|
|
||||||
|
protected App app;
|
||||||
|
|
||||||
|
protected ConsumeThread consumeThread;
|
||||||
|
|
||||||
|
protected WifiManager.WifiLock wifiLock;
|
||||||
|
|
||||||
|
protected WifiManager wifiManager;
|
||||||
|
protected AlarmManager alarmManager;
|
||||||
|
//protected PowerManager powerManager;
|
||||||
|
|
||||||
|
public AmqpConsumer(App app)
|
||||||
|
{
|
||||||
|
this.app = app;
|
||||||
|
wifiManager = (WifiManager) app.getSystemService(Context.WIFI_SERVICE);
|
||||||
|
alarmManager = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
//powerManager = (PowerManager) app.getSystemService(Context.POWER_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'async' and 'delayed' methods must not be synchronized or app will deadlock ---
|
||||||
|
// StartAmqpConsumerService thread owns lock on AmqpConsumer and needs lock on App to call app.log,
|
||||||
|
// while main thread in onConnectivityChanged owns lock on App and must not take any locks on AmqpConsumer
|
||||||
|
public void startAsync()
|
||||||
|
{
|
||||||
|
startStopAsync(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopAsync()
|
||||||
|
{
|
||||||
|
cancelStartDelayed();
|
||||||
|
startStopAsync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startStopAsync(boolean start)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(app, AmqpConsumerService.class);
|
||||||
|
intent.putExtra("start", start);
|
||||||
|
app.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelStartDelayed()
|
||||||
|
{
|
||||||
|
alarmManager.cancel(getStartPendingIntent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingIntent getStartPendingIntent()
|
||||||
|
{
|
||||||
|
return PendingIntent.getBroadcast(app,
|
||||||
|
0,
|
||||||
|
new Intent(app, StartAmqpConsumer.class),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startDelayed(long delay)
|
||||||
|
{
|
||||||
|
alarmManager.set(
|
||||||
|
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||||
|
SystemClock.elapsedRealtime() + delay,
|
||||||
|
getStartPendingIntent()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void startBlocking()
|
||||||
|
{
|
||||||
|
stopBlocking();
|
||||||
|
|
||||||
|
boolean enabled = app.tryGetBooleanSetting("amqp_enabled", false);
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
|
app.log("Real-time connection disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String host = app.tryGetStringSetting("amqp_host", null);
|
||||||
|
int port = app.tryGetIntegerSetting("amqp_port", 0);
|
||||||
|
boolean ssl = app.tryGetBooleanSetting("amqp_ssl", false);
|
||||||
|
String vhost = app.tryGetStringSetting("amqp_vhost", null);
|
||||||
|
String username = app.tryGetStringSetting("amqp_user", null);
|
||||||
|
String password = app.tryGetStringSetting("amqp_password", null);
|
||||||
|
String queue = app.tryGetStringSetting("amqp_queue", null);
|
||||||
|
|
||||||
|
if (host == null || port == 0 || username == null || password == null || queue == null || vhost == null)
|
||||||
|
{
|
||||||
|
app.log("Real-time connection not configured");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean started = tryStart(host, port, ssl, vhost, username, password, queue);
|
||||||
|
if (!started)
|
||||||
|
{
|
||||||
|
startDelayed(RETRY_ERROR_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable heartbeatRunnable;
|
||||||
|
|
||||||
|
public synchronized void setHeartbeatRunnable(Runnable heartbeatRunnable)
|
||||||
|
{
|
||||||
|
this.heartbeatRunnable = heartbeatRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void sendHeartbeatBlocking()
|
||||||
|
{
|
||||||
|
if (heartbeatRunnable != null)
|
||||||
|
{
|
||||||
|
heartbeatRunnable.run();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("no heartbeat runnable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingIntent getHeartbeatPendingIntent()
|
||||||
|
{
|
||||||
|
return PendingIntent.getService(app,
|
||||||
|
0,
|
||||||
|
new Intent(app, AmqpHeartbeatService.class),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HeartbeatExecutor extends ScheduledThreadPoolExecutor
|
||||||
|
{
|
||||||
|
public HeartbeatExecutor()
|
||||||
|
{
|
||||||
|
super(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
|
||||||
|
{
|
||||||
|
long delayMs = TimeUnit.MILLISECONDS.convert(initialDelay, unit);
|
||||||
|
long periodMs = TimeUnit.MILLISECONDS.convert(period, unit);
|
||||||
|
|
||||||
|
setHeartbeatRunnable(command);
|
||||||
|
|
||||||
|
alarmManager.setRepeating(
|
||||||
|
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||||
|
SystemClock.elapsedRealtime() + delayMs,
|
||||||
|
periodMs,
|
||||||
|
getHeartbeatPendingIntent());
|
||||||
|
|
||||||
|
//app.log("scheduleAtFixedRate " + delayMs + ", " + periodMs);
|
||||||
|
|
||||||
|
return new ScheduledFuture<Integer>()
|
||||||
|
{
|
||||||
|
public long getDelay(TimeUnit u2)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int compareTo(Delayed d2)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public Integer get()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public Integer get(long timeout, TimeUnit unit)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public boolean cancel(boolean interrupt)
|
||||||
|
{
|
||||||
|
// doesn't do anything -- alarm cancelled by stopBlocking
|
||||||
|
cancelled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean cancelled;
|
||||||
|
|
||||||
|
public boolean isCancelled()
|
||||||
|
{
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
public boolean isDone()
|
||||||
|
{
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean tryStart(String host, int port, boolean ssl, String vhost, String username, String password, String queue)
|
||||||
|
{
|
||||||
|
app.log("Establishing real-time connection...");
|
||||||
|
|
||||||
|
if (wifiManager.isWifiEnabled())
|
||||||
|
{
|
||||||
|
if (wifiLock == null)
|
||||||
|
{
|
||||||
|
wifiLock = wifiManager.createWifiLock(WIFI_MODE_FULL_HIGH_PERF, "telerivet-amqp");
|
||||||
|
wifiLock.setReferenceCounted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
wifiLock.acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||||
|
|
||||||
|
connectionFactory.setHost(host);
|
||||||
|
connectionFactory.setPort(port);
|
||||||
|
connectionFactory.setVirtualHost(vhost);
|
||||||
|
connectionFactory.setUsername(username);
|
||||||
|
connectionFactory.setPassword(password);
|
||||||
|
|
||||||
|
/*
|
||||||
|
connectionFactory.setLogger(new IILogger() {
|
||||||
|
public void log(String str)
|
||||||
|
{
|
||||||
|
app.log(str);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
connectionFactory.setHeartbeatExecutor(new HeartbeatExecutor());
|
||||||
|
|
||||||
|
TrustManager[] trustManagers = null; // use built-in SSL certificate verification
|
||||||
|
|
||||||
|
if (ssl)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
// could customize SSL certificate verification
|
||||||
|
trustManagers = new TrustManager[]{
|
||||||
|
new X509TrustManager() {
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||||
|
}
|
||||||
|
public void checkServerTrusted(X509Certificate[] certs, String authType)
|
||||||
|
throws CertificateException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
SSLContext c = SSLContext.getInstance("TLS");
|
||||||
|
c.init(null, trustManagers, new SecureRandom());
|
||||||
|
|
||||||
|
connectionFactory.useSslProtocol(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to periodically check if connection is still working
|
||||||
|
// to allow the client and server to detect broken connections
|
||||||
|
// after 2*heartbeat seconds
|
||||||
|
|
||||||
|
// AMQP heartbeat interval has a big effect on battery usage on idle phones.
|
||||||
|
// The CPU will wake up every heartbeat for 5 seconds.
|
||||||
|
// For the rest of the time, the phone stays in deep sleep (CPU off) and is automatically
|
||||||
|
// woken up whenever the server sends a packet.
|
||||||
|
connectionFactory.setRequestedHeartbeat(app.tryGetIntegerSetting("amqp_heartbeat", 300));
|
||||||
|
|
||||||
|
connection = connectionFactory.newConnection();
|
||||||
|
channel = connection.createChannel();
|
||||||
|
|
||||||
|
channel.queueDeclare(queue, true, false, false, null);
|
||||||
|
channel.basicQos(1);
|
||||||
|
|
||||||
|
QueueingConsumer consumer = new QueueingConsumer(channel);
|
||||||
|
|
||||||
|
channel.basicConsume(queue, false, consumer);
|
||||||
|
|
||||||
|
consumeThread = new ConsumeThread(consumer);
|
||||||
|
consumeThread.start();
|
||||||
|
|
||||||
|
HttpTask task = new HttpTask(app,
|
||||||
|
new BasicNameValuePair("action", App.ACTION_AMQP_STARTED),
|
||||||
|
new BasicNameValuePair("consumer_tag", consumer.getConsumerTag())
|
||||||
|
);
|
||||||
|
task.execute();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
app.logError("Error establishing real-time connection", ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void stopBlocking() {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (wifiLock != null && wifiLock.isHeld())
|
||||||
|
{
|
||||||
|
wifiLock.release();
|
||||||
|
}
|
||||||
|
alarmManager.cancel(getHeartbeatPendingIntent());
|
||||||
|
heartbeatRunnable = null;
|
||||||
|
|
||||||
|
if (consumeThread != null)
|
||||||
|
{
|
||||||
|
consumeThread.terminate();
|
||||||
|
consumeThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel != null)
|
||||||
|
{
|
||||||
|
channel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connection.close(10000);
|
||||||
|
}
|
||||||
|
catch (AlreadyClosedException ex) {}
|
||||||
|
catch (ShutdownSignalException ex) {}
|
||||||
|
|
||||||
|
connection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
app.logError("Error stopping real-time connection", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConsumeThread extends Thread
|
||||||
|
{
|
||||||
|
private QueueingConsumer consumer;
|
||||||
|
private boolean terminated = false;
|
||||||
|
|
||||||
|
public ConsumeThread(QueueingConsumer consumer)
|
||||||
|
{
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void terminate()
|
||||||
|
{
|
||||||
|
this.terminated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isTerminated()
|
||||||
|
{
|
||||||
|
return terminated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processMessage(QueueingConsumer.Delivery delivery)
|
||||||
|
{
|
||||||
|
String jsonStr = new String(delivery.getBody());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject json = new JSONObject(jsonStr);
|
||||||
|
JsonUtils.processEvent(json, app, null);
|
||||||
|
}
|
||||||
|
catch (JSONException ex)
|
||||||
|
{
|
||||||
|
app.logError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long startTime = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.log("Real-time connection established.");
|
||||||
|
|
||||||
|
Channel ch = consumer.getChannel();
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
|
||||||
|
|
||||||
|
if (isTerminated())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
|
||||||
|
|
||||||
|
processMessage(delivery);
|
||||||
|
|
||||||
|
if (isTerminated())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.log("Real-time connection stopped.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (!isTerminated())
|
||||||
|
{
|
||||||
|
if (ex instanceof ShutdownSignalException)
|
||||||
|
{
|
||||||
|
//app.logError("Real-time connection interrupted", ex);
|
||||||
|
app.log("Real-time connection interrupted");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.logError("Real-time connection interrupted", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
long age = SystemClock.elapsedRealtime() - startTime;
|
||||||
|
|
||||||
|
stopAsync();
|
||||||
|
if (age < MIN_EXPECTED_ERROR_DELAY)
|
||||||
|
{
|
||||||
|
startDelayed(RETRY_ERROR_DELAY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
startDelayed(RETRY_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Real-time connection stopped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package org.envaya.sms;
|
package org.envaya.sms;
|
||||||
|
|
||||||
|
import org.envaya.sms.service.EnabledChangedService;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
@ -18,8 +19,6 @@ import android.os.AsyncTask;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.telephony.PhoneStateListener;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -54,9 +53,17 @@ public final class App extends Application {
|
|||||||
|
|
||||||
public static final String ACTION_OUTGOING = "outgoing";
|
public static final String ACTION_OUTGOING = "outgoing";
|
||||||
public static final String ACTION_INCOMING = "incoming";
|
public static final String ACTION_INCOMING = "incoming";
|
||||||
|
public static final String ACTION_FORWARD_SENT = "forward_sent";
|
||||||
public static final String ACTION_SEND_STATUS = "send_status";
|
public static final String ACTION_SEND_STATUS = "send_status";
|
||||||
public static final String ACTION_DEVICE_STATUS = "device_status";
|
public static final String ACTION_DEVICE_STATUS = "device_status";
|
||||||
public static final String ACTION_TEST = "test";
|
public static final String ACTION_TEST = "test";
|
||||||
|
public static final String ACTION_AMQP_STARTED = "amqp_started";
|
||||||
|
|
||||||
|
public static final String EVENT_SEND = "send";
|
||||||
|
public static final String EVENT_CANCEL = "cancel";
|
||||||
|
public static final String EVENT_CANCEL_ALL = "cancel_all";
|
||||||
|
public static final String EVENT_LOG = "log";
|
||||||
|
public static final String EVENT_SETTINGS = "settings";
|
||||||
|
|
||||||
public static final String STATUS_QUEUED = "queued";
|
public static final String STATUS_QUEUED = "queued";
|
||||||
public static final String STATUS_FAILED = "failed";
|
public static final String STATUS_FAILED = "failed";
|
||||||
@ -77,7 +84,10 @@ 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_CHANGED_INTENT = "org.envaya.sms.LOG_CHANGED";
|
public static final String LOG_CHANGED_INTENT = "org.envaya.sms.LOG_CHANGED";
|
||||||
|
public static final String SETTINGS_CHANGED_INTENT = "org.envaya.sms.SETTINGS_CHANGED";
|
||||||
|
|
||||||
|
public static final String EXPANSION_PACKS_CHANGED_INTENT = "org.envaya.sms.EXPANSION_PACKS_CHANGED";
|
||||||
|
|
||||||
// signal to PendingMessages activity (if open) that inbox/outbox has changed
|
// signal to PendingMessages activity (if open) that inbox/outbox has changed
|
||||||
public static final String INBOX_CHANGED_INTENT = "org.envaya.sms.INBOX_CHANGED";
|
public static final String INBOX_CHANGED_INTENT = "org.envaya.sms.INBOX_CHANGED";
|
||||||
public static final String OUTBOX_CHANGED_INTENT = "org.envaya.sms.OUTBOX_CHANGED";
|
public static final String OUTBOX_CHANGED_INTENT = "org.envaya.sms.OUTBOX_CHANGED";
|
||||||
@ -134,7 +144,8 @@ public final class App extends Application {
|
|||||||
public final Queue<HttpTask> queuedTasks = new LinkedList<HttpTask>();
|
public final Queue<HttpTask> queuedTasks = new LinkedList<HttpTask>();
|
||||||
|
|
||||||
private SharedPreferences settings;
|
private SharedPreferences settings;
|
||||||
private MmsObserver mmsObserver;
|
private MessagingObserver messagingObserver;
|
||||||
|
|
||||||
private SpannableStringBuilder displayedLog = new SpannableStringBuilder();
|
private SpannableStringBuilder displayedLog = new SpannableStringBuilder();
|
||||||
private long lastLogTime;
|
private long lastLogTime;
|
||||||
|
|
||||||
@ -151,24 +162,39 @@ public final class App extends Application {
|
|||||||
// count to provide round-robin selection of expansion packs
|
// count to provide round-robin selection of expansion packs
|
||||||
private int outgoingMessageCount = -1;
|
private int outgoingMessageCount = -1;
|
||||||
|
|
||||||
private MmsUtils mmsUtils;
|
private MessagingUtils messagingUtils;
|
||||||
private CallListener callListener;
|
private CallListener callListener;
|
||||||
|
private DatabaseHelper dbHelper;
|
||||||
|
private AmqpConsumer amqpConsumer;
|
||||||
|
|
||||||
private boolean connectivityError = false;
|
private boolean connectivityError = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate()
|
public void onCreate()
|
||||||
{
|
{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
// workaround for http://code.google.com/p/android/issues/detail?id=20915
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class.forName("android.os.AsyncTask");
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
mmsUtils = new MmsUtils(this);
|
messagingUtils = new MessagingUtils(this);
|
||||||
|
|
||||||
callListener = new CallListener(this);
|
callListener = new CallListener(this);
|
||||||
|
|
||||||
outgoingMessagePackages.add(getPackageName());
|
outgoingMessagePackages.add(getPackageName());
|
||||||
|
|
||||||
mmsObserver = new MmsObserver(this);
|
messagingObserver = new MessagingObserver(this);
|
||||||
|
|
||||||
|
dbHelper = new DatabaseHelper(this);
|
||||||
|
|
||||||
|
amqpConsumer = new AmqpConsumer(this);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -187,92 +213,30 @@ public final class App extends Application {
|
|||||||
|
|
||||||
public void configuredChanged()
|
public void configuredChanged()
|
||||||
{
|
{
|
||||||
log(Html.fromHtml(
|
if (isConfigured())
|
||||||
isEnabled() ? "<b>SMS gateway running ("+getDisplayString(getPhoneNumber())+").</b>"
|
|
||||||
: "<b>SMS gateway disabled.</b>"));
|
|
||||||
|
|
||||||
log("Server URL: " + getDisplayString(getServerUrl()));
|
|
||||||
log("Keep new messages: " + (getKeepInInbox() ? "YES": "NO"));
|
|
||||||
log("Call notifications: " + (callNotificationsEnabled() ? "ON": "OFF"));
|
|
||||||
log("Network failover: " + (isNetworkFailoverEnabled() ? "ON": "OFF"));
|
|
||||||
|
|
||||||
boolean ignoreShortcodes = ignoreShortcodes();
|
|
||||||
boolean ignoreNonNumeric = ignoreNonNumeric();
|
|
||||||
List<String> ignoredNumbers = getIgnoredPhoneNumbers();
|
|
||||||
|
|
||||||
if (ignoredNumbers.size() > 0 || ignoreShortcodes || ignoreNonNumeric)
|
|
||||||
{
|
{
|
||||||
log("Ignored phone numbers:");
|
sendBroadcast(new Intent(App.SETTINGS_CHANGED_INTENT));
|
||||||
|
enabledChanged();
|
||||||
if (ignoreShortcodes || ignoreNonNumeric)
|
|
||||||
{
|
|
||||||
String ignoreDesc = " ";
|
|
||||||
if (ignoreShortcodes)
|
|
||||||
{
|
|
||||||
ignoreDesc += "all shortcodes";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoreShortcodes && ignoreNonNumeric)
|
|
||||||
{
|
|
||||||
ignoreDesc += ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoreNonNumeric)
|
|
||||||
{
|
|
||||||
ignoreDesc += "all non-numeric";
|
|
||||||
}
|
|
||||||
log(ignoreDesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String sender : ignoredNumbers)
|
|
||||||
{
|
|
||||||
log(" " + sender);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isTestMode())
|
|
||||||
{
|
|
||||||
log("Test mode: ON");
|
|
||||||
log("Test phone numbers:");
|
|
||||||
|
|
||||||
for (String sender : getTestPhoneNumbers())
|
|
||||||
{
|
|
||||||
log(" " + sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log(Html.fromHtml("<b>To change these settings, click Menu, then Settings.</b>"));
|
|
||||||
|
|
||||||
enabledChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void enabledChanged()
|
public void enabledChanged()
|
||||||
{
|
{
|
||||||
TelephonyManager telephony = (TelephonyManager)
|
// startup/shutdown tasks may be slow, so offload them to a worker thread...
|
||||||
getSystemService(Context.TELEPHONY_SERVICE);
|
// IntentService takes care of only running one request at a time
|
||||||
|
startService(new Intent(this, EnabledChangedService.class));
|
||||||
if (isEnabled())
|
}
|
||||||
{
|
|
||||||
mmsObserver.register();
|
|
||||||
|
|
||||||
telephony.listen(callListener, PhoneStateListener.LISTEN_CALL_STATE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mmsObserver.unregister();
|
|
||||||
|
|
||||||
telephony.listen(callListener, PhoneStateListener.LISTEN_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
setOutgoingMessageAlarm();
|
|
||||||
startService(new Intent(this, ForegroundService.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public PackageInfo getPackageInfo()
|
public PackageInfo getPackageInfo()
|
||||||
{
|
{
|
||||||
return packageInfo;
|
return packageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSmsExpansionPackInstalled(String packageName)
|
||||||
|
{
|
||||||
|
return outgoingMessagePackages.contains(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized String chooseOutgoingSmsPackage(int numParts)
|
public synchronized String chooseOutgoingSmsPackage(int numParts)
|
||||||
{
|
{
|
||||||
outgoingMessageCount++;
|
outgoingMessageCount++;
|
||||||
@ -388,8 +352,9 @@ public final class App extends Application {
|
|||||||
|
|
||||||
if (prevLimit != newLimit)
|
if (prevLimit != newLimit)
|
||||||
{
|
{
|
||||||
log("Outgoing SMS limit: " + newLimit + " messages/hour");
|
log("Outgoing SMS rate limit: " + newLimit + " messages/hour");
|
||||||
}
|
}
|
||||||
|
sendBroadcast(new Intent(App.EXPANSION_PACKS_CHANGED_INTENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOutgoingMessageLimit()
|
public int getOutgoingMessageLimit()
|
||||||
@ -442,11 +407,11 @@ public final class App extends Application {
|
|||||||
{
|
{
|
||||||
String serverUrl = getServerUrl();
|
String serverUrl = getServerUrl();
|
||||||
if (serverUrl.length() > 0) {
|
if (serverUrl.length() > 0) {
|
||||||
log("Checking for outgoing messages");
|
log("Checking for messages");
|
||||||
pollActive = true;
|
pollActive = true;
|
||||||
new PollerTask(this).execute();
|
new PollerTask(this).execute();
|
||||||
} else {
|
} else {
|
||||||
log("Can't check outgoing messages; server URL not set");
|
log("Can't check messages; server URL not set");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -490,22 +455,46 @@ public final class App extends Application {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isConfigured()
|
||||||
|
{
|
||||||
|
return getServerUrl().length() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean callNotificationsEnabled()
|
public boolean callNotificationsEnabled()
|
||||||
{
|
{
|
||||||
return tryGetBooleanSetting("call_notifications", false);
|
return tryGetBooleanSetting("call_notifications", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getConfigureServer() {
|
||||||
|
return settings.getString("configure_server", "");
|
||||||
|
}
|
||||||
|
|
||||||
public String getServerUrl() {
|
public String getServerUrl() {
|
||||||
return settings.getString("server_url", "");
|
return settings.getString("server_url", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPhoneNumber() {
|
public String getPhoneNumber() {
|
||||||
return settings.getString("phone_number", "");
|
return settings.getString("phone_number", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOutgoingPollSeconds() {
|
public boolean isAmqpEnabled()
|
||||||
return Integer.parseInt(settings.getString("outgoing_interval", "0"));
|
{
|
||||||
|
return tryGetBooleanSetting("amqp_enabled", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhoneID() {
|
||||||
|
return settings.getString("phone_id", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhoneToken()
|
||||||
|
{
|
||||||
|
return settings.getString("phone_token", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutgoingPollSeconds()
|
||||||
|
{
|
||||||
|
return tryGetIntegerSetting("outgoing_interval", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled()
|
public boolean isEnabled()
|
||||||
@ -513,13 +502,30 @@ public final class App extends Application {
|
|||||||
return tryGetBooleanSetting("enabled", false);
|
return tryGetBooleanSetting("enabled", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String tryGetStringSetting(String name, String defaultValue)
|
||||||
|
{
|
||||||
|
return settings.getString(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int tryGetIntegerSetting(String name, int defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return settings.getInt(name, defaultValue);
|
||||||
|
}
|
||||||
|
catch (ClassCastException ex)
|
||||||
|
{
|
||||||
|
return Integer.parseInt(settings.getString(name, "" + defaultValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean tryGetBooleanSetting(String name, boolean defaultValue)
|
public boolean tryGetBooleanSetting(String name, boolean defaultValue)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return settings.getBoolean(name, defaultValue);
|
return settings.getBoolean(name, defaultValue);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (ClassCastException ex)
|
||||||
{
|
{
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@ -530,10 +536,20 @@ public final class App extends Application {
|
|||||||
return tryGetBooleanSetting("network_failover", false);
|
return tryGetBooleanSetting("network_failover", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForwardingSentMessagesEnabled()
|
||||||
|
{
|
||||||
|
return tryGetBooleanSetting("forward_sent", false);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isTestMode()
|
public boolean isTestMode()
|
||||||
{
|
{
|
||||||
return tryGetBooleanSetting("test_mode", false);
|
return tryGetBooleanSetting("test_mode", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean autoAddTestNumber()
|
||||||
|
{
|
||||||
|
return tryGetBooleanSetting("auto_add_test_number", false);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getKeepInInbox()
|
public boolean getKeepInInbox()
|
||||||
{
|
{
|
||||||
@ -542,12 +558,17 @@ public final class App extends Application {
|
|||||||
|
|
||||||
public boolean ignoreShortcodes()
|
public boolean ignoreShortcodes()
|
||||||
{
|
{
|
||||||
return tryGetBooleanSetting("ignore_shortcodes", true);
|
return tryGetBooleanSetting("ignore_shortcodes", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean ignoreNonNumeric()
|
public boolean ignoreNonNumeric()
|
||||||
{
|
{
|
||||||
return tryGetBooleanSetting("ignore_non_numeric", true);
|
return tryGetBooleanSetting("ignore_non_numeric", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSettingsVersion()
|
||||||
|
{
|
||||||
|
return tryGetIntegerSetting("settings_version", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
@ -628,15 +649,25 @@ public final class App extends Application {
|
|||||||
sendBroadcast(new Intent(App.LOG_CHANGED_INTENT));
|
sendBroadcast(new Intent(App.LOG_CHANGED_INTENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUpgradeAvailable()
|
||||||
|
{
|
||||||
|
return tryGetIntegerSetting("market_version", 0) > packageInfo.versionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMarketVersionName()
|
||||||
|
{
|
||||||
|
return settings.getString("market_version_name", "?");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Changes whenever we change the beginning of the displayed log.
|
* Changes whenever we change the beginning of the displayed log.
|
||||||
* If it doesn't change, the Main activity can update the log view much
|
* If it doesn't change, the Main activity can update the log view much
|
||||||
* faster by using TextView.append() instead of TextView.setText()
|
* faster by using TextView.append() instead of TextView.setText()
|
||||||
*/
|
*/
|
||||||
public int getLogEpoch()
|
public synchronized int getLogEpoch()
|
||||||
{
|
{
|
||||||
return logEpoch;
|
return logEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized CharSequence getDisplayedLog()
|
public synchronized CharSequence getDisplayedLog()
|
||||||
{
|
{
|
||||||
@ -665,9 +696,9 @@ public final class App extends Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MmsUtils getMmsUtils()
|
public MessagingUtils getMessagingUtils()
|
||||||
{
|
{
|
||||||
return mmsUtils;
|
return messagingUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> testPhoneNumbers;
|
private List<String> testPhoneNumbers;
|
||||||
@ -763,20 +794,48 @@ public final class App extends Application {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPhoneNumberInList(String phoneNumber, List<String> phoneNumbers)
|
||||||
|
{
|
||||||
|
int phoneLen = phoneNumber.length();
|
||||||
|
|
||||||
|
for (String otherNumber : phoneNumbers)
|
||||||
|
{
|
||||||
|
if (otherNumber == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phoneNumber.equals(otherNumber))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int otherLen = otherNumber.length();
|
||||||
|
|
||||||
|
// fuzzy matching to account for different versions of same phone number (+, area codes, country codes)
|
||||||
|
if ((otherLen >= 7 && phoneNumber.endsWith(otherNumber)) ||
|
||||||
|
phoneLen >= 7 && otherNumber.endsWith(phoneNumber))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isForwardablePhoneNumber(String phoneNumber)
|
public boolean isForwardablePhoneNumber(String phoneNumber)
|
||||||
{
|
{
|
||||||
if (isTestMode())
|
if (isTestMode())
|
||||||
{
|
{
|
||||||
return getTestPhoneNumbers().contains(phoneNumber);
|
return isPhoneNumberInList(phoneNumber, getTestPhoneNumbers());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getIgnoredPhoneNumbers().contains(phoneNumber))
|
if (isPhoneNumberInList(phoneNumber, getIgnoredPhoneNumbers()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int numDigits = 0;
|
int numDigits = 0;
|
||||||
int length = phoneNumber.length();
|
int length = (phoneNumber == null) ? 0 : phoneNumber.length();
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@ -949,15 +1008,19 @@ public final class App extends Application {
|
|||||||
|
|
||||||
if (networkInfo == null || !networkInfo.isConnected())
|
if (networkInfo == null || !networkInfo.isConnected())
|
||||||
{
|
{
|
||||||
|
amqpConsumer.stopAsync();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
amqpConsumer.startDelayed(5000);
|
||||||
|
|
||||||
int networkType = networkInfo.getType();
|
int networkType = networkInfo.getType();
|
||||||
|
|
||||||
if (networkType == activeNetworkType)
|
if (networkType == activeNetworkType)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
activeNetworkType = networkType;
|
activeNetworkType = networkType;
|
||||||
log("Connected to " + networkInfo.getTypeName());
|
log("Connected to " + networkInfo.getTypeName());
|
||||||
@ -1007,5 +1070,25 @@ public final class App extends Application {
|
|||||||
public synchronized void addQueuedTask(HttpTask task)
|
public synchronized void addQueuedTask(HttpTask task)
|
||||||
{
|
{
|
||||||
queuedTasks.add(task);
|
queuedTasks.add(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DatabaseHelper getDatabaseHelper()
|
||||||
|
{
|
||||||
|
return dbHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessagingObserver getMessagingObserver()
|
||||||
|
{
|
||||||
|
return messagingObserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallListener getCallListener()
|
||||||
|
{
|
||||||
|
return callListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AmqpConsumer getAmqpConsumer()
|
||||||
|
{
|
||||||
|
return amqpConsumer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
|
|
||||||
package org.envaya.sms;
|
|
||||||
|
|
||||||
import android.app.IntentService;
|
|
||||||
import android.content.Intent;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CheckMmsInboxService extends IntentService
|
|
||||||
{
|
|
||||||
private App app;
|
|
||||||
private MmsUtils mmsUtils;
|
|
||||||
|
|
||||||
public CheckMmsInboxService(String name)
|
|
||||||
{
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CheckMmsInboxService()
|
|
||||||
{
|
|
||||||
this("CheckMmsInboxService");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
app = (App)this.getApplicationContext();
|
|
||||||
|
|
||||||
mmsUtils = app.getMmsUtils();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHandleIntent(Intent intent)
|
|
||||||
{
|
|
||||||
List<IncomingMms> messages = mmsUtils.getMessagesInInbox();
|
|
||||||
for (IncomingMms mms : messages)
|
|
||||||
{
|
|
||||||
if (mmsUtils.isNewMms(mms))
|
|
||||||
{
|
|
||||||
app.log("New MMS id=" + mms.getId() + " in inbox");
|
|
||||||
// prevent forwarding MMS messages that existed in inbox
|
|
||||||
// before EnvayaSMS started, or re-forwarding MMS multiple
|
|
||||||
// times if we don't delete them.
|
|
||||||
mmsUtils.markOldMms(mms);
|
|
||||||
|
|
||||||
if (mms.isForwardable())
|
|
||||||
{
|
|
||||||
app.inbox.forwardMessage(mms);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
app.log("Ignoring incoming MMS from " + mms.getFrom());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
270
src/org/envaya/sms/DatabaseHelper.java
Normal file
270
src/org/envaya/sms/DatabaseHelper.java
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
package org.envaya.sms;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.SQLException;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
|
||||||
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
public static final String DATABASE_NAME = "envayasms.db";
|
||||||
|
public static final int DATABASE_VERSION = 4;
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
public DatabaseHelper(App app)
|
||||||
|
{
|
||||||
|
super(app, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
|
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate(SQLiteDatabase db)
|
||||||
|
{
|
||||||
|
// Persisted backup of incoming messages have not been forwarded to server.
|
||||||
|
// allows us to restore pending messages after restart if phone runs out of batteries
|
||||||
|
// or app crashes.
|
||||||
|
|
||||||
|
db.execSQL("CREATE TABLE pending_incoming_messages ("
|
||||||
|
+ "`_id` INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
|
+ "`message_type` VARCHAR,"
|
||||||
|
+ "`messaging_id` INTEGER," // id in Messaging app database (if applicable)
|
||||||
|
+ "`from_number` VARCHAR,"
|
||||||
|
+ "`to_number` VARCHAR,"
|
||||||
|
+ "`message` TEXT,"
|
||||||
|
+ "`direction` INTEGER,"
|
||||||
|
+ "`timestamp` INTEGER"
|
||||||
|
+ ")");
|
||||||
|
|
||||||
|
db.execSQL("CREATE TABLE pending_outgoing_messages ("
|
||||||
|
+ "`_id` INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
|
+ "`message_type` VARCHAR,"
|
||||||
|
+ "`from_number` VARCHAR,"
|
||||||
|
+ "`to_number` VARCHAR,"
|
||||||
|
+ "`message` TEXT,"
|
||||||
|
+ "`priority` INTEGER,"
|
||||||
|
+ "`server_id` TEXT"
|
||||||
|
+ ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
|
||||||
|
{
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS pending_incoming_messages");
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS pending_outgoing_messages");
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void restorePendingMessages()
|
||||||
|
{
|
||||||
|
restorePendingIncomingMessages();
|
||||||
|
restorePendingOutgoingMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void restorePendingIncomingMessages()
|
||||||
|
{
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor c = db.query("pending_incoming_messages", null, null, null, null, null, null);
|
||||||
|
|
||||||
|
int idIndex = c.getColumnIndex("_id");
|
||||||
|
int messageTypeIndex = c.getColumnIndex("message_type");
|
||||||
|
int messagingIdIndex = c.getColumnIndex("messaging_id");
|
||||||
|
int fromIndex = c.getColumnIndex("from_number");
|
||||||
|
int toIndex = c.getColumnIndex("to_number");
|
||||||
|
int messageIndex = c.getColumnIndex("message");
|
||||||
|
int directionIndex = c.getColumnIndex("direction");
|
||||||
|
int timestampIndex = c.getColumnIndex("timestamp");
|
||||||
|
|
||||||
|
while (c.moveToNext())
|
||||||
|
{
|
||||||
|
long id = c.getLong(idIndex);
|
||||||
|
String messageType = c.getString(messageTypeIndex);
|
||||||
|
long messagingId = c.getLong(messagingIdIndex);
|
||||||
|
|
||||||
|
IncomingMessage message;
|
||||||
|
|
||||||
|
if (App.MESSAGE_TYPE_SMS.equals(messageType))
|
||||||
|
{
|
||||||
|
message = new IncomingSms(app);
|
||||||
|
}
|
||||||
|
else if (App.MESSAGE_TYPE_MMS.equals(messageType))
|
||||||
|
{
|
||||||
|
message = new IncomingMms(app);
|
||||||
|
}
|
||||||
|
else if (App.MESSAGE_TYPE_CALL.equals(messageType))
|
||||||
|
{
|
||||||
|
message = new IncomingCall(app);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Unknown message type " + messageType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.setMessagingId(messagingId);
|
||||||
|
message.setPersistedId(id);
|
||||||
|
message.setMessageBody(c.getString(messageIndex));
|
||||||
|
message.setFrom(c.getString(fromIndex));
|
||||||
|
message.setTo(c.getString(toIndex));
|
||||||
|
|
||||||
|
int directionInt = c.getInt(directionIndex);
|
||||||
|
IncomingMessage.Direction direction = (directionInt == IncomingMessage.Direction.Sent.ordinal()) ?
|
||||||
|
IncomingMessage.Direction.Sent : IncomingMessage.Direction.Incoming;
|
||||||
|
|
||||||
|
message.setDirection(direction);
|
||||||
|
message.setTimestamp(c.getLong(timestampIndex));
|
||||||
|
|
||||||
|
app.inbox.forwardMessage(message);
|
||||||
|
}
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void restorePendingOutgoingMessages()
|
||||||
|
{
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor c = db.query("pending_outgoing_messages", null, null, null, null, null, null);
|
||||||
|
|
||||||
|
int idIndex = c.getColumnIndex("_id");
|
||||||
|
int messageTypeIndex = c.getColumnIndex("message_type");
|
||||||
|
int fromIndex = c.getColumnIndex("from_number");
|
||||||
|
int toIndex = c.getColumnIndex("to_number");
|
||||||
|
int messageIndex = c.getColumnIndex("message");
|
||||||
|
int priorityIndex = c.getColumnIndex("priority");
|
||||||
|
int serverIdIndex = c.getColumnIndex("server_id");
|
||||||
|
|
||||||
|
while (c.moveToNext())
|
||||||
|
{
|
||||||
|
long id = c.getLong(idIndex);
|
||||||
|
String messageType = c.getString(messageTypeIndex);
|
||||||
|
|
||||||
|
OutgoingMessage message;
|
||||||
|
|
||||||
|
if (App.MESSAGE_TYPE_SMS.equals(messageType))
|
||||||
|
{
|
||||||
|
message = new OutgoingSms(app);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Unknown message type " + messageType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.setPersistedId(id);
|
||||||
|
message.setMessageBody(c.getString(messageIndex));
|
||||||
|
message.setFrom(c.getString(fromIndex));
|
||||||
|
|
||||||
|
String to = c.getString(toIndex);
|
||||||
|
|
||||||
|
if (to == null || "null".equals(to))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.setTo(to);
|
||||||
|
|
||||||
|
message.setPriority(c.getInt(priorityIndex));
|
||||||
|
message.setServerId(c.getString(serverIdIndex));
|
||||||
|
|
||||||
|
app.outbox.sendMessage(message);
|
||||||
|
}
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void insertPendingMessage(IncomingMessage message)
|
||||||
|
{
|
||||||
|
if (message.isPersisted())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("message_type", message.getMessageType());
|
||||||
|
values.put("messaging_id", message.getMessagingId());
|
||||||
|
values.put("from_number", message.getFrom());
|
||||||
|
values.put("to_number", message.getTo());
|
||||||
|
values.put("message", message.getMessageBody());
|
||||||
|
values.put("direction", message.getDirection().ordinal());
|
||||||
|
values.put("timestamp", message.getTimestamp());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
long messageId = db.insertOrThrow("pending_incoming_messages", null, values);
|
||||||
|
message.setPersistedId(messageId);
|
||||||
|
}
|
||||||
|
catch (SQLException ex)
|
||||||
|
{
|
||||||
|
app.logError("Error saving message to database", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void insertPendingMessage(OutgoingMessage message)
|
||||||
|
{
|
||||||
|
if (message.isPersisted())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("message_type", message.getMessageType());
|
||||||
|
values.put("from_number", message.getFrom());
|
||||||
|
values.put("to_number", message.getTo());
|
||||||
|
values.put("priority", message.getPriority());
|
||||||
|
values.put("message", message.getMessageBody());
|
||||||
|
values.put("server_id", message.getServerId());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
long messageId = db.insertOrThrow("pending_outgoing_messages", null, values);
|
||||||
|
message.setPersistedId(messageId);
|
||||||
|
}
|
||||||
|
catch (SQLException ex)
|
||||||
|
{
|
||||||
|
app.logError("Error saving message to database", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void deletePendingMessage(IncomingMessage message)
|
||||||
|
{
|
||||||
|
if (!message.isPersisted())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
|
||||||
|
int rows = db.delete("pending_incoming_messages", "_id = ?", new String[] {"" + message.getPersistedId() });
|
||||||
|
if (rows == 0)
|
||||||
|
{
|
||||||
|
app.log("Error deleting pending message from database");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message.setPersistedId(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void deletePendingMessage(OutgoingMessage message)
|
||||||
|
{
|
||||||
|
if (!message.isPersisted())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
|
||||||
|
int rows = db.delete("pending_outgoing_messages", "_id = ?", new String[] {"" + message.getPersistedId() });
|
||||||
|
if (rows == 0)
|
||||||
|
{
|
||||||
|
app.log("Error deleting pending message from database");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message.setPersistedId(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,9 @@ public class Inbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
incomingMessages.put(uri, message);
|
incomingMessages.put(uri, message);
|
||||||
app.log("Received "+message.getDescription());
|
app.log("Received "+message.getDisplayType());
|
||||||
|
|
||||||
|
app.getDatabaseHelper().insertPendingMessage(message);
|
||||||
|
|
||||||
enqueueMessage(message);
|
enqueueMessage(message);
|
||||||
}
|
}
|
||||||
@ -83,6 +85,8 @@ public class Inbox {
|
|||||||
{
|
{
|
||||||
numForwardingMessages--;
|
numForwardingMessages--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.getDatabaseHelper().deletePendingMessage(message);
|
||||||
|
|
||||||
app.log(message.getDescription() + " deleted");
|
app.log(message.getDescription() + " deleted");
|
||||||
notifyChanged();
|
notifyChanged();
|
||||||
@ -98,7 +102,8 @@ public class Inbox {
|
|||||||
}
|
}
|
||||||
notifyChanged();
|
notifyChanged();
|
||||||
|
|
||||||
numForwardingMessages--;
|
numForwardingMessages--;
|
||||||
|
|
||||||
maybeDequeueMessage();
|
maybeDequeueMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,17 +116,12 @@ public class Inbox {
|
|||||||
|
|
||||||
notifyChanged();
|
notifyChanged();
|
||||||
|
|
||||||
if (message instanceof IncomingMms)
|
message.onForwardComplete();
|
||||||
{
|
|
||||||
IncomingMms mms = (IncomingMms)message;
|
|
||||||
if (!app.getKeepInInbox())
|
|
||||||
{
|
|
||||||
app.log("Deleting MMS " + mms.getId() + " from inbox...");
|
|
||||||
app.getMmsUtils().deleteFromInbox(mms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
numForwardingMessages--;
|
numForwardingMessages--;
|
||||||
|
|
||||||
|
app.getDatabaseHelper().deletePendingMessage(message);
|
||||||
|
|
||||||
maybeDequeueMessage();
|
maybeDequeueMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,25 +5,17 @@
|
|||||||
package org.envaya.sms;
|
package org.envaya.sms;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import org.envaya.sms.task.ForwarderTask;
|
|
||||||
|
|
||||||
public class IncomingCall extends IncomingMessage {
|
public class IncomingCall extends IncomingMessage {
|
||||||
|
|
||||||
private long id;
|
|
||||||
|
|
||||||
private static long nextId = 1;
|
|
||||||
|
|
||||||
public IncomingCall(App app, String from, long timestampMillis)
|
public IncomingCall(App app, String from, long timestampMillis)
|
||||||
{
|
{
|
||||||
super(app, from, timestampMillis);
|
super(app, from, timestampMillis);
|
||||||
this.id = getNextId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized long getNextId()
|
public IncomingCall(App app)
|
||||||
{
|
{
|
||||||
long id = nextId;
|
super(app);
|
||||||
nextId++;
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayType()
|
public String getDisplayType()
|
||||||
@ -44,6 +36,6 @@ public class IncomingCall extends IncomingMessage {
|
|||||||
|
|
||||||
public Uri getUri()
|
public Uri getUri()
|
||||||
{
|
{
|
||||||
return Uri.withAppendedPath(App.INCOMING_URI, "call/" + id);
|
return Uri.withAppendedPath(App.INCOMING_URI, "call/" + Uri.encode(from) + "/" + timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,29 @@ import org.apache.http.message.BasicNameValuePair;
|
|||||||
|
|
||||||
public abstract class IncomingMessage extends QueuedMessage {
|
public abstract class IncomingMessage extends QueuedMessage {
|
||||||
|
|
||||||
protected String from;
|
protected String from; // phone number of other party, for messages with direction=Direction.Incoming
|
||||||
|
|
||||||
|
protected String to; // phone number of other party, for messages with direction=Direction.Sent
|
||||||
|
|
||||||
|
protected Direction direction = Direction.Incoming;
|
||||||
|
|
||||||
|
protected long messagingId; // _id from Messaging app content provider tables (if applicable)
|
||||||
|
|
||||||
protected String message = "";
|
protected String message = "";
|
||||||
protected long timestamp; // unix timestamp in milliseconds
|
protected long timestamp; // unix timestamp in milliseconds
|
||||||
|
|
||||||
protected long timeReceived; // SystemClock.elapsedRealtime
|
protected long timeCreated; // SystemClock.elapsedRealtime
|
||||||
|
|
||||||
private ProcessingState state = ProcessingState.None;
|
private ProcessingState state = ProcessingState.None;
|
||||||
|
|
||||||
|
public enum Direction
|
||||||
|
{
|
||||||
|
Incoming, // a message that was received by this phone
|
||||||
|
|
||||||
|
Sent // Message was sent via Messaging app (so it's "Incoming" from
|
||||||
|
// the phone to the server, but we don't actually send it)
|
||||||
|
}
|
||||||
|
|
||||||
public enum ProcessingState
|
public enum ProcessingState
|
||||||
{
|
{
|
||||||
None, // not doing anything with this sms now... just sitting around
|
None, // not doing anything with this sms now... just sitting around
|
||||||
@ -25,13 +40,33 @@ public abstract class IncomingMessage extends QueuedMessage {
|
|||||||
Forwarded
|
Forwarded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IncomingMessage(App app)
|
||||||
|
{
|
||||||
|
super(app);
|
||||||
|
}
|
||||||
|
|
||||||
public IncomingMessage(App app, String from, long timestamp)
|
public IncomingMessage(App app, String from, long timestamp)
|
||||||
{
|
{
|
||||||
super(app);
|
super(app);
|
||||||
this.from = from;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
|
|
||||||
this.timeReceived = SystemClock.elapsedRealtime();
|
if (from == null)
|
||||||
|
{
|
||||||
|
from = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.from = from;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.timeCreated = SystemClock.elapsedRealtime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirection(Direction direction)
|
||||||
|
{
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getDirection()
|
||||||
|
{
|
||||||
|
return this.direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageBody()
|
public String getMessageBody()
|
||||||
@ -39,9 +74,14 @@ public abstract class IncomingMessage extends QueuedMessage {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMessageBody(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
public long getAge()
|
public long getAge()
|
||||||
{
|
{
|
||||||
return SystemClock.elapsedRealtime() - timeReceived;
|
return SystemClock.elapsedRealtime() - timeCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimestamp()
|
public long getTimestamp()
|
||||||
@ -49,6 +89,11 @@ public abstract class IncomingMessage extends QueuedMessage {
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long timestamp)
|
||||||
|
{
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public ProcessingState getProcessingState()
|
public ProcessingState getProcessingState()
|
||||||
{
|
{
|
||||||
return state;
|
return state;
|
||||||
@ -61,14 +106,47 @@ public abstract class IncomingMessage extends QueuedMessage {
|
|||||||
|
|
||||||
public boolean isForwardable()
|
public boolean isForwardable()
|
||||||
{
|
{
|
||||||
return app.isForwardablePhoneNumber(from);
|
if (direction == Direction.Sent)
|
||||||
|
{
|
||||||
|
return app.isForwardingSentMessagesEnabled() && app.isForwardablePhoneNumber(to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return app.isForwardablePhoneNumber(from);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFrom()
|
public String getFrom()
|
||||||
{
|
{
|
||||||
return from;
|
return from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFrom(String from)
|
||||||
|
{
|
||||||
|
this.from = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTo()
|
||||||
|
{
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTo(String to)
|
||||||
|
{
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMessagingId()
|
||||||
|
{
|
||||||
|
return messagingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessagingId(long messagingId)
|
||||||
|
{
|
||||||
|
this.messagingId = messagingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Intent getRetryIntent() {
|
protected Intent getRetryIntent() {
|
||||||
Intent intent = new Intent(app, IncomingMessageRetry.class);
|
Intent intent = new Intent(app, IncomingMessageRetry.class);
|
||||||
intent.setData(this.getUri());
|
intent.setData(this.getUri());
|
||||||
@ -92,7 +170,14 @@ public abstract class IncomingMessage extends QueuedMessage {
|
|||||||
|
|
||||||
public String getDescription()
|
public String getDescription()
|
||||||
{
|
{
|
||||||
return getDisplayType() + " from " + getFrom();
|
if (direction == Direction.Sent)
|
||||||
|
{
|
||||||
|
return "Sent " + getDisplayType() + " to " + getTo();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getDisplayType() + " from " + getFrom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tryForwardToServer()
|
public void tryForwardToServer()
|
||||||
@ -107,15 +192,30 @@ public abstract class IncomingMessage extends QueuedMessage {
|
|||||||
|
|
||||||
public abstract String getMessageType();
|
public abstract String getMessageType();
|
||||||
|
|
||||||
|
public void onForwardComplete()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected ForwarderTask getForwarderTask()
|
protected ForwarderTask getForwarderTask()
|
||||||
{
|
{
|
||||||
return new ForwarderTask(this,
|
ForwarderTask task = new ForwarderTask(this,
|
||||||
new BasicNameValuePair("message_type", getMessageType()),
|
new BasicNameValuePair("message_type", getMessageType()),
|
||||||
new BasicNameValuePair("message", getMessageBody()),
|
new BasicNameValuePair("message", getMessageBody()),
|
||||||
new BasicNameValuePair("action", App.ACTION_INCOMING),
|
new BasicNameValuePair("timestamp", "" + getTimestamp())
|
||||||
new BasicNameValuePair("from", getFrom()),
|
|
||||||
new BasicNameValuePair("timestamp", "" + getTimestamp()),
|
|
||||||
new BasicNameValuePair("age", "" + getAge())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (direction == Direction.Sent)
|
||||||
|
{
|
||||||
|
task.addParam("action", App.ACTION_FORWARD_SENT);
|
||||||
|
task.addParam("to", getTo());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
task.addParam("action", App.ACTION_INCOMING);
|
||||||
|
task.addParam("from", getFrom());
|
||||||
|
}
|
||||||
|
|
||||||
|
return task;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,59 +14,60 @@ import org.apache.http.entity.mime.content.ContentBody;
|
|||||||
import org.envaya.sms.task.ForwarderTask;
|
import org.envaya.sms.task.ForwarderTask;
|
||||||
|
|
||||||
public class IncomingMms extends IncomingMessage {
|
public class IncomingMms extends IncomingMessage {
|
||||||
List<MmsPart> parts;
|
private List<MmsPart> parts;
|
||||||
long id;
|
|
||||||
String contentLocation;
|
|
||||||
|
|
||||||
public IncomingMms(App app, String from, long timestamp, long id)
|
public IncomingMms(App app, long timestamp, long messagingId)
|
||||||
{
|
{
|
||||||
super(app, from, timestamp);
|
super(app, null, timestamp);
|
||||||
this.parts = new ArrayList<MmsPart>();
|
this.messagingId = messagingId;
|
||||||
this.id = id;
|
}
|
||||||
}
|
|
||||||
|
public IncomingMms(App app)
|
||||||
|
{
|
||||||
|
super(app);
|
||||||
|
}
|
||||||
|
|
||||||
public String getDisplayType()
|
@Override
|
||||||
|
public String getFrom()
|
||||||
{
|
{
|
||||||
return "MMS";
|
if (from == null || from.length() == 0)
|
||||||
|
{
|
||||||
|
// lazy-load sender number from Messaging database as needed
|
||||||
|
from = app.getMessagingUtils().getMmsSenderNumber(messagingId);
|
||||||
|
}
|
||||||
|
return from;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MmsPart> getParts()
|
public List<MmsPart> getParts()
|
||||||
{
|
{
|
||||||
|
if (parts == null)
|
||||||
|
{
|
||||||
|
// lazy-load mms parts from Messaging database as needed
|
||||||
|
this.parts = new ArrayList<MmsPart>();
|
||||||
|
for (MmsPart part : app.getMessagingUtils().getMmsParts(messagingId))
|
||||||
|
{
|
||||||
|
parts.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
return parts;
|
return parts;
|
||||||
}
|
|
||||||
|
|
||||||
public void addPart(MmsPart part)
|
|
||||||
{
|
|
||||||
parts.add(part);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getId()
|
public String getDisplayType()
|
||||||
{
|
{
|
||||||
return id;
|
return "MMS";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getContentLocation()
|
|
||||||
{
|
|
||||||
return contentLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentLocation(String contentLocation)
|
|
||||||
{
|
|
||||||
this.contentLocation = contentLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append("MMS id=");
|
builder.append("MMS id=");
|
||||||
builder.append(id);
|
builder.append(messagingId);
|
||||||
builder.append(" from=");
|
builder.append(" from=");
|
||||||
builder.append(from);
|
builder.append(from);
|
||||||
builder.append(":\n");
|
builder.append(":\n");
|
||||||
|
|
||||||
for (MmsPart part : parts)
|
for (MmsPart part : getParts())
|
||||||
{
|
{
|
||||||
builder.append(" ");
|
builder.append(" ");
|
||||||
builder.append(part.toString());
|
builder.append(part.toString());
|
||||||
@ -84,7 +85,7 @@ public class IncomingMms extends IncomingMessage {
|
|||||||
|
|
||||||
JSONArray partsMetadata = new JSONArray();
|
JSONArray partsMetadata = new JSONArray();
|
||||||
|
|
||||||
for (MmsPart part : parts)
|
for (MmsPart part : getParts())
|
||||||
{
|
{
|
||||||
String formFieldName = "part" + i;
|
String formFieldName = "part" + i;
|
||||||
String text = part.getText();
|
String text = part.getText();
|
||||||
@ -148,7 +149,7 @@ public class IncomingMms extends IncomingMessage {
|
|||||||
@Override
|
@Override
|
||||||
public String getMessageBody()
|
public String getMessageBody()
|
||||||
{
|
{
|
||||||
for (MmsPart part : parts)
|
for (MmsPart part : getParts())
|
||||||
{
|
{
|
||||||
if ("text/plain".equals(part.getContentType()))
|
if ("text/plain".equals(part.getContentType()))
|
||||||
{
|
{
|
||||||
@ -161,11 +162,21 @@ public class IncomingMms extends IncomingMessage {
|
|||||||
|
|
||||||
public Uri getUri()
|
public Uri getUri()
|
||||||
{
|
{
|
||||||
return Uri.withAppendedPath(App.INCOMING_URI, "mms/" + id);
|
return Uri.withAppendedPath(App.INCOMING_URI, "mms/" + messagingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageType()
|
public String getMessageType()
|
||||||
{
|
{
|
||||||
return App.MESSAGE_TYPE_MMS;
|
return App.MESSAGE_TYPE_MMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onForwardComplete()
|
||||||
|
{
|
||||||
|
if (!app.getKeepInInbox())
|
||||||
|
{
|
||||||
|
app.log("Deleting MMS " + getMessagingId() + " from inbox...");
|
||||||
|
app.getMessagingUtils().deleteFromMmsInbox(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import android.net.Uri;
|
|||||||
import android.telephony.SmsMessage;
|
import android.telephony.SmsMessage;
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.envaya.sms.task.ForwarderTask;
|
|
||||||
|
|
||||||
|
|
||||||
public class IncomingSms extends IncomingMessage {
|
public class IncomingSms extends IncomingMessage {
|
||||||
@ -34,12 +33,11 @@ public class IncomingSms extends IncomingMessage {
|
|||||||
message = message + smsPart.getMessageBody();
|
message = message + smsPart.getMessageBody();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructor for SMS retrieved from Messaging inbox
|
public IncomingSms(App app)
|
||||||
public IncomingSms(App app, String from, String message, long timestampMillis) {
|
{
|
||||||
super(app, from, timestampMillis);
|
super(app);
|
||||||
this.message = message;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayType()
|
public String getDisplayType()
|
||||||
{
|
{
|
||||||
|
196
src/org/envaya/sms/JsonUtils.java
Normal file
196
src/org/envaya/sms/JsonUtils.java
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
|
||||||
|
package org.envaya.sms;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class JsonUtils {
|
||||||
|
|
||||||
|
public static JSONObject parseResponse(HttpResponse response)
|
||||||
|
throws IOException, JSONException
|
||||||
|
{
|
||||||
|
String responseBody = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
|
||||||
|
return new JSONObject(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getErrorText(JSONObject json)
|
||||||
|
{
|
||||||
|
JSONObject errorObject = json.optJSONObject("error");
|
||||||
|
return errorObject != null ? errorObject.optString("message") : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void processEvents(JSONObject json, App app, String defaultTo)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
JSONArray events = json.optJSONArray("events");
|
||||||
|
if (events != null)
|
||||||
|
{
|
||||||
|
int numEvents = events.length();
|
||||||
|
for (int i = 0; i < numEvents; i++)
|
||||||
|
{
|
||||||
|
JsonUtils.processEvent(events.getJSONObject(i), app, defaultTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void processEvent(JSONObject json, App app, String defaultTo)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String event = json.getString("event");
|
||||||
|
|
||||||
|
if (App.EVENT_SEND.equals(event))
|
||||||
|
{
|
||||||
|
for (OutgoingMessage message : JsonUtils.getMessagesList(json, app, defaultTo))
|
||||||
|
{
|
||||||
|
app.outbox.sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (App.EVENT_LOG.equals(event))
|
||||||
|
{
|
||||||
|
app.log(json.getString("message"));
|
||||||
|
}
|
||||||
|
else if (App.EVENT_SETTINGS.equals(event))
|
||||||
|
{
|
||||||
|
JsonUtils.updateSettings(json, app);
|
||||||
|
}
|
||||||
|
else if (App.EVENT_CANCEL.equals(event))
|
||||||
|
{
|
||||||
|
String id = json.getString("id");
|
||||||
|
|
||||||
|
OutgoingMessage message = app.outbox.getMessage(OutgoingMessage.getUriForServerId(id));
|
||||||
|
if (message != null && message.isCancelable())
|
||||||
|
{
|
||||||
|
app.outbox.deleteMessage(message);
|
||||||
|
app.outbox.maybeDequeueMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (App.EVENT_CANCEL_ALL.equals(event))
|
||||||
|
{
|
||||||
|
for (OutgoingMessage message : app.outbox.getMessages())
|
||||||
|
{
|
||||||
|
if (message.isCancelable())
|
||||||
|
{
|
||||||
|
app.outbox.deleteMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Unknown event" + event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateSettings(JSONObject json, App app)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
JSONObject settingsObject = json.optJSONObject("settings");
|
||||||
|
|
||||||
|
if (settingsObject != null && settingsObject.length() > 0)
|
||||||
|
{
|
||||||
|
SharedPreferences.Editor settings = PreferenceManager.getDefaultSharedPreferences(app).edit();
|
||||||
|
|
||||||
|
Iterator it = settingsObject.keys();
|
||||||
|
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
String name = (String)it.next();
|
||||||
|
Object value = settingsObject.get(name);
|
||||||
|
|
||||||
|
if (value instanceof String)
|
||||||
|
{
|
||||||
|
settings.putString(name, (String)value);
|
||||||
|
}
|
||||||
|
else if (value instanceof Boolean)
|
||||||
|
{
|
||||||
|
settings.putBoolean(name, (Boolean)value);
|
||||||
|
}
|
||||||
|
else if (value instanceof Integer)
|
||||||
|
{
|
||||||
|
settings.putInt(name, (Integer)value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Unknown setting type " + value.getClass().getName() + " for name " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.commit();
|
||||||
|
app.log("Updated app settings");
|
||||||
|
app.configuredChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static App app;
|
||||||
|
|
||||||
|
// like JSONObject.optString but doesn't convert null to "null"
|
||||||
|
private static String optString(JSONObject json, String key, String defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object value = json.get(key);
|
||||||
|
|
||||||
|
if (value == null || JSONObject.NULL.equals(value))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JSONException ex)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<OutgoingMessage> getMessagesList(JSONObject json, App app, String defaultTo)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
List<OutgoingMessage> messages = new ArrayList<OutgoingMessage>();
|
||||||
|
JSONArray messagesList = json.optJSONArray("messages");
|
||||||
|
JsonUtils.app = app;
|
||||||
|
if (messagesList != null)
|
||||||
|
{
|
||||||
|
int numMessages = messagesList.length();
|
||||||
|
|
||||||
|
for (int i = 0; i < numMessages; i++)
|
||||||
|
{
|
||||||
|
JSONObject messageObject = messagesList.getJSONObject(i);
|
||||||
|
|
||||||
|
OutgoingMessage message = OutgoingMessage.newFromMessageType(app,
|
||||||
|
optString(messageObject, "type", App.MESSAGE_TYPE_SMS));
|
||||||
|
|
||||||
|
message.setFrom(app.getPhoneNumber());
|
||||||
|
|
||||||
|
String to = optString(messageObject, "to", defaultTo);
|
||||||
|
|
||||||
|
if (to == null || "".equals(to) || "null".equals(to))
|
||||||
|
{
|
||||||
|
app.log("Received invalid SMS from server (missing recipient)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String body = optString(messageObject, "message","");
|
||||||
|
|
||||||
|
message.setTo(to);
|
||||||
|
message.setServerId(optString(messageObject, "id", null));
|
||||||
|
message.setPriority(messageObject.optInt("priority", 0));
|
||||||
|
message.setMessageBody(body);
|
||||||
|
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
}
|
54
src/org/envaya/sms/MessagingObserver.java
Normal file
54
src/org/envaya/sms/MessagingObserver.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package org.envaya.sms;
|
||||||
|
|
||||||
|
import org.envaya.sms.service.CheckMessagingService;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class MessagingObserver extends ContentObserver {
|
||||||
|
|
||||||
|
// constants from android.provider.Telephony
|
||||||
|
public static final Uri OBSERVER_URI = Uri.parse("content://mms-sms/");
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
public MessagingObserver(App app) {
|
||||||
|
super(new Handler());
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register()
|
||||||
|
{
|
||||||
|
app.getContentResolver().registerContentObserver(OBSERVER_URI, true, this);
|
||||||
|
|
||||||
|
MessagingUtils messagingUtils = app.getMessagingUtils();
|
||||||
|
|
||||||
|
for (IncomingMms mms : messagingUtils.getMessagesInMmsInbox())
|
||||||
|
{
|
||||||
|
messagingUtils.markSeenMms(mms);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IncomingSms sms : messagingUtils.getSentSmsMessages())
|
||||||
|
{
|
||||||
|
messagingUtils.markSeenSentSms(sms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister()
|
||||||
|
{
|
||||||
|
app.getContentResolver().unregisterContentObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(final boolean selfChange) {
|
||||||
|
super.onChange(selfChange);
|
||||||
|
if (!selfChange)
|
||||||
|
{
|
||||||
|
// check MMS inbox in an IntentService since it may be slow
|
||||||
|
// and we only want to do one check at a time
|
||||||
|
app.startService(new Intent(app, CheckMessagingService.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
115
src/org/envaya/sms/MmsUtils.java → src/org/envaya/sms/MessagingUtils.java
Executable file → Normal file
115
src/org/envaya/sms/MmsUtils.java → src/org/envaya/sms/MessagingUtils.java
Executable file → Normal file
@ -7,39 +7,42 @@ import android.net.Uri;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Utilities for parsing IncomingMms from the MMS content provider tables,
|
* Utilities for parsing MMS and SMS messages from the content provider tables
|
||||||
* as defined by android.provider.Telephony
|
* of the Messaging app, as defined by android.provider.Telephony
|
||||||
*
|
*
|
||||||
* Analogous to com.google.android.mms.pdu.PduPersister from
|
* MMS parsing is analogous to com.google.android.mms.pdu.PduPersister from
|
||||||
* core/java/com/google/android/mms/pdu in the base Android framework
|
* core/java/com/google/android/mms/pdu in the base Android framework
|
||||||
* (https://github.com/android/platform_frameworks_base)
|
* (https://github.com/android/platform_frameworks_base)
|
||||||
*/
|
*/
|
||||||
public class MmsUtils
|
public class MessagingUtils
|
||||||
{
|
{
|
||||||
// constants from android.provider.Telephony
|
// constants from android.provider.Telephony
|
||||||
public static final Uri OBSERVER_URI = Uri.parse("content://mms-sms/");
|
public static final Uri MMS_INBOX_URI = Uri.parse("content://mms/inbox");
|
||||||
public static final Uri INBOX_URI = Uri.parse("content://mms/inbox");
|
public static final Uri MMS_PART_URI = Uri.parse("content://mms/part");
|
||||||
public static final Uri PART_URI = Uri.parse("content://mms/part");
|
|
||||||
|
public static final Uri SENT_SMS_URI = Uri.parse("content://sms/sent");
|
||||||
|
|
||||||
// constants from com.google.android.mms.pdu.PduHeaders
|
// constants from com.google.android.mms.pdu.PduHeaders
|
||||||
private static final int PDU_HEADER_FROM = 0x89;
|
private static final int PDU_HEADER_FROM = 0x89;
|
||||||
private static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
|
private static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
|
||||||
|
|
||||||
// todo -- prevent unbounded growth?
|
// todo -- prevent (very slow) unbounded growth?
|
||||||
private final Set<Long> seenMmsIds = new HashSet<Long>();
|
private final Set<Long> seenMmsIds = new HashSet<Long>();
|
||||||
|
|
||||||
|
private final Set<Long> seenSentSmsIds = new HashSet<Long>();
|
||||||
|
|
||||||
private App app;
|
private App app;
|
||||||
private ContentResolver contentResolver;
|
private ContentResolver contentResolver;
|
||||||
|
|
||||||
public MmsUtils(App app)
|
public MessagingUtils(App app)
|
||||||
{
|
{
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.contentResolver = app.getContentResolver();
|
this.contentResolver = app.getContentResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MmsPart> getMmsParts(long id)
|
public List<MmsPart> getMmsParts(long id)
|
||||||
{
|
{
|
||||||
Cursor cur = contentResolver.query(PART_URI, new String[] {
|
Cursor cur = contentResolver.query(MMS_PART_URI, new String[] {
|
||||||
"_id", "ct", "name", "text", "cid", "_data"
|
"_id", "ct", "name", "text", "cid", "_data"
|
||||||
}, "mid = ?", new String[] { "" + id }, null);
|
}, "mid = ?", new String[] { "" + id }, null);
|
||||||
|
|
||||||
@ -67,7 +70,8 @@ public class MmsUtils
|
|||||||
|
|
||||||
if (name == null || name.length() == 0)
|
if (name == null || name.length() == 0)
|
||||||
{
|
{
|
||||||
name = UUID.randomUUID().toString();
|
// POST request for incoming MMS will fail if the filename is empty
|
||||||
|
name = UUID.randomUUID().toString().substring(0, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
part.setName(name);
|
part.setName(name);
|
||||||
@ -90,7 +94,7 @@ public class MmsUtils
|
|||||||
/*
|
/*
|
||||||
* see com.google.android.mms.pdu.PduPersister.loadAddress
|
* see com.google.android.mms.pdu.PduPersister.loadAddress
|
||||||
*/
|
*/
|
||||||
private String getSenderNumber(long mmsId) {
|
public String getMmsSenderNumber(long mmsId) {
|
||||||
|
|
||||||
Uri uri = Uri.parse("content://mms/"+mmsId+"/addr");
|
Uri uri = Uri.parse("content://mms/"+mmsId+"/addr");
|
||||||
|
|
||||||
@ -112,44 +116,41 @@ public class MmsUtils
|
|||||||
|
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<IncomingMms> getMessagesInMmsInbox()
|
||||||
|
{
|
||||||
|
return getMessagesInMmsInbox(false);
|
||||||
|
}
|
||||||
|
|
||||||
public List<IncomingMms> getMessagesInInbox()
|
public synchronized List<IncomingMms> getMessagesInMmsInbox(boolean newMessagesOnly)
|
||||||
{
|
{
|
||||||
// the M-Retrieve.conf messages are the 'actual' MMS messages
|
// the M-Retrieve.conf messages are the 'actual' MMS messages
|
||||||
String m_type = "" + MESSAGE_TYPE_RETRIEVE_CONF;
|
String m_type = "" + MESSAGE_TYPE_RETRIEVE_CONF;
|
||||||
|
|
||||||
Cursor c = contentResolver.query(INBOX_URI,
|
Cursor c = contentResolver.query(MMS_INBOX_URI,
|
||||||
new String[] {"_id", "ct_l", "date"},
|
new String[] {"_id", "ct_l", "date"},
|
||||||
"m_type = ? ", new String[] { m_type }, null);
|
"m_type = ? ", new String[] { m_type },
|
||||||
|
"_id desc limit 30");
|
||||||
|
|
||||||
List<IncomingMms> messages = new ArrayList<IncomingMms>();
|
List<IncomingMms> messages = new ArrayList<IncomingMms>();
|
||||||
|
|
||||||
while (c.moveToNext())
|
while (c.moveToNext())
|
||||||
{
|
{
|
||||||
long id = c.getLong(0);
|
long id = c.getLong(0);
|
||||||
long date = c.getLong(2);
|
|
||||||
|
|
||||||
String from = getSenderNumber(id);
|
if (newMessagesOnly && seenMmsIds.contains(id))
|
||||||
|
|
||||||
if (from == null)
|
|
||||||
{
|
{
|
||||||
app.log("Ignoring MMS "+id+" for now because sender number is null");
|
// avoid fetching all the info for old MMS messages if we're only interested in new ones
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long date = c.getLong(2);
|
||||||
|
|
||||||
IncomingMms mms = new IncomingMms(app,
|
IncomingMms mms = new IncomingMms(app,
|
||||||
from,
|
|
||||||
date * 1000, // MMS timestamp is in seconds for some reason,
|
date * 1000, // MMS timestamp is in seconds for some reason,
|
||||||
// while everything else is in ms
|
// while everything else is in ms
|
||||||
id);
|
id);
|
||||||
|
|
||||||
mms.setContentLocation(c.getString(1));
|
|
||||||
|
|
||||||
for (MmsPart part : getMmsParts(id))
|
|
||||||
{
|
|
||||||
mms.addPart(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
messages.add(mms);
|
messages.add(mms);
|
||||||
}
|
}
|
||||||
c.close();
|
c.close();
|
||||||
@ -157,9 +158,9 @@ public class MmsUtils
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean deleteFromInbox(IncomingMms mms)
|
public synchronized boolean deleteFromMmsInbox(IncomingMms mms)
|
||||||
{
|
{
|
||||||
long id = mms.getId();
|
long id = mms.getMessagingId();
|
||||||
|
|
||||||
Uri uri = Uri.parse("content://mms/inbox/" + id);
|
Uri uri = Uri.parse("content://mms/inbox/" + id);
|
||||||
int res = contentResolver.delete(uri, null, null);
|
int res = contentResolver.delete(uri, null, null);
|
||||||
@ -181,15 +182,53 @@ public class MmsUtils
|
|||||||
return res > 0;
|
return res > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void markOldMms(IncomingMms mms)
|
public synchronized void markSeenMms(IncomingMms mms)
|
||||||
{
|
{
|
||||||
long id = mms.getId();
|
long id = mms.getMessagingId();
|
||||||
seenMmsIds.add(id);
|
seenMmsIds.add(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean isNewMms(IncomingMms mms)
|
public synchronized List<IncomingSms> getSentSmsMessages()
|
||||||
{
|
{
|
||||||
long id = mms.getId();
|
return getSentSmsMessages(false);
|
||||||
return !seenMmsIds.contains(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized List<IncomingSms> getSentSmsMessages(boolean newMessagesOnly)
|
||||||
|
{
|
||||||
|
Cursor c = contentResolver.query(SENT_SMS_URI,
|
||||||
|
new String[]{"_id", "address", "body", "date"}, null, null,
|
||||||
|
"_id desc limit 30");
|
||||||
|
|
||||||
|
// SMS messages sent via Messaging app are considered as IncomingSms (with direction=Direction.Sent)
|
||||||
|
// because they're incoming to the server (whereas OutgoingSms would indicate a message we will try to send)
|
||||||
|
List<IncomingSms> messages = new ArrayList<IncomingSms>();
|
||||||
|
|
||||||
|
while (c.moveToNext())
|
||||||
|
{
|
||||||
|
long id = c.getLong(0);
|
||||||
|
|
||||||
|
if (newMessagesOnly && seenSentSmsIds.contains(id))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IncomingSms sms = new IncomingSms(app);
|
||||||
|
sms.setMessagingId(id);
|
||||||
|
sms.setTo(c.getString(1));
|
||||||
|
sms.setMessageBody(c.getString(2));
|
||||||
|
sms.setTimestamp(c.getLong(3));
|
||||||
|
sms.setDirection(IncomingSms.Direction.Sent);
|
||||||
|
|
||||||
|
messages.add(sms);
|
||||||
|
}
|
||||||
|
c.close();
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void markSeenSentSms(IncomingSms sms)
|
||||||
|
{
|
||||||
|
long id = sms.getMessagingId();
|
||||||
|
seenSentSmsIds.add(id);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,46 +0,0 @@
|
|||||||
package org.envaya.sms;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.os.Handler;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
final class MmsObserver extends ContentObserver {
|
|
||||||
|
|
||||||
private App app;
|
|
||||||
|
|
||||||
public MmsObserver(App app) {
|
|
||||||
super(new Handler());
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register()
|
|
||||||
{
|
|
||||||
app.getContentResolver().registerContentObserver(
|
|
||||||
MmsUtils.OBSERVER_URI, true, this);
|
|
||||||
|
|
||||||
MmsUtils mmsUtils = app.getMmsUtils();
|
|
||||||
|
|
||||||
List<IncomingMms> messages = mmsUtils.getMessagesInInbox();
|
|
||||||
for (IncomingMms mms : messages)
|
|
||||||
{
|
|
||||||
mmsUtils.markOldMms(mms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregister()
|
|
||||||
{
|
|
||||||
app.getContentResolver().unregisterContentObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChange(final boolean selfChange) {
|
|
||||||
super.onChange(selfChange);
|
|
||||||
if (!selfChange)
|
|
||||||
{
|
|
||||||
// check MMS inbox in an IntentService since it may be slow
|
|
||||||
// and we only want to do one check at a time
|
|
||||||
app.startService(new Intent(app, CheckMmsInboxService.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.SystemClock;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -18,7 +17,6 @@ import java.util.Queue;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.envaya.sms.receiver.DequeueOutgoingMessageReceiver;
|
import org.envaya.sms.receiver.DequeueOutgoingMessageReceiver;
|
||||||
import org.envaya.sms.receiver.OutgoingMessagePoller;
|
|
||||||
import org.envaya.sms.task.HttpTask;
|
import org.envaya.sms.task.HttpTask;
|
||||||
|
|
||||||
public class Outbox {
|
public class Outbox {
|
||||||
@ -81,7 +79,7 @@ public class Outbox {
|
|||||||
else {
|
else {
|
||||||
logMessage = "queued";
|
logMessage = "queued";
|
||||||
}
|
}
|
||||||
String smsDesc = sms.getLogName();
|
String smsDesc = sms.getDisplayType();
|
||||||
|
|
||||||
if (serverId != null) {
|
if (serverId != null) {
|
||||||
app.log("Notifying server " + smsDesc + " " + logMessage);
|
app.log("Notifying server " + smsDesc + " " + logMessage);
|
||||||
@ -115,31 +113,33 @@ public class Outbox {
|
|||||||
return outgoingMessages.get(uri);
|
return outgoingMessages.get(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void messageSent(OutgoingMessage sms)
|
public synchronized void messageSent(OutgoingMessage message)
|
||||||
{
|
{
|
||||||
sms.setProcessingState(OutgoingMessage.ProcessingState.Sent);
|
message.setProcessingState(OutgoingMessage.ProcessingState.Sent);
|
||||||
|
|
||||||
sms.clearSendTimeout();
|
message.clearSendTimeout();
|
||||||
|
|
||||||
notifyMessageStatus(sms, App.STATUS_SENT, "");
|
notifyMessageStatus(message, App.STATUS_SENT, "");
|
||||||
|
|
||||||
Uri uri = sms.getUri();
|
Uri uri = message.getUri();
|
||||||
|
|
||||||
outgoingMessages.remove(uri);
|
outgoingMessages.remove(uri);
|
||||||
|
|
||||||
addRecentSentMessage(sms);
|
addRecentSentMessage(message);
|
||||||
|
|
||||||
notifyChanged();
|
app.getDatabaseHelper().deletePendingMessage(message);
|
||||||
|
|
||||||
|
notifyChanged();
|
||||||
|
|
||||||
numSendingOutgoingMessages--;
|
numSendingOutgoingMessages--;
|
||||||
maybeDequeueMessage();
|
maybeDequeueMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void addRecentSentMessage(OutgoingMessage sms)
|
private synchronized void addRecentSentMessage(OutgoingMessage message)
|
||||||
{
|
{
|
||||||
if (sms.getServerId() != null)
|
if (message.getServerId() != null)
|
||||||
{
|
{
|
||||||
Uri uri = sms.getUri();
|
Uri uri = message.getUri();
|
||||||
|
|
||||||
recentSentMessageUris.add(uri);
|
recentSentMessageUris.add(uri);
|
||||||
recentSentMessageUriOrder.add(uri);
|
recentSentMessageUriOrder.add(uri);
|
||||||
@ -152,21 +152,23 @@ public class Outbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void messageFailed(OutgoingMessage sms, String error)
|
public synchronized void messageFailed(OutgoingMessage message, String error)
|
||||||
{
|
{
|
||||||
sms.clearSendTimeout();
|
message.clearSendTimeout();
|
||||||
|
|
||||||
if (sms.scheduleRetry())
|
if (message.scheduleRetry())
|
||||||
{
|
{
|
||||||
sms.setProcessingState(OutgoingMessage.ProcessingState.Scheduled);
|
message.setProcessingState(OutgoingMessage.ProcessingState.Scheduled);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sms.setProcessingState(OutgoingMessage.ProcessingState.None);
|
message.setProcessingState(OutgoingMessage.ProcessingState.None);
|
||||||
}
|
}
|
||||||
notifyChanged();
|
notifyChanged();
|
||||||
notifyMessageStatus(sms, App.STATUS_FAILED, error);
|
notifyMessageStatus(message, App.STATUS_FAILED, error);
|
||||||
|
|
||||||
|
app.getDatabaseHelper().deletePendingMessage(message);
|
||||||
|
|
||||||
numSendingOutgoingMessages--;
|
numSendingOutgoingMessages--;
|
||||||
maybeDequeueMessage();
|
maybeDequeueMessage();
|
||||||
}
|
}
|
||||||
@ -185,18 +187,21 @@ public class Outbox {
|
|||||||
|
|
||||||
Uri uri = message.getUri();
|
Uri uri = message.getUri();
|
||||||
if (outgoingMessages.containsKey(uri)) {
|
if (outgoingMessages.containsKey(uri)) {
|
||||||
app.debug("Duplicate outgoing " + message.getLogName() + ", skipping");
|
app.debug("Duplicate outgoing " + message.getDisplayType() + ", skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recentSentMessageUris.contains(uri))
|
if (recentSentMessageUris.contains(uri))
|
||||||
{
|
{
|
||||||
app.debug("Outgoing " + message.getLogName() + " already sent, re-notifying server");
|
app.debug("Outgoing " + message.getDisplayType() + " already sent, re-notifying server");
|
||||||
notifyMessageStatus(message, App.STATUS_SENT, "");
|
notifyMessageStatus(message, App.STATUS_SENT, "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
outgoingMessages.put(uri, message);
|
outgoingMessages.put(uri, message);
|
||||||
|
|
||||||
|
app.getDatabaseHelper().insertPendingMessage(message);
|
||||||
|
|
||||||
enqueueMessage(message);
|
enqueueMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +218,8 @@ public class Outbox {
|
|||||||
numSendingOutgoingMessages--;
|
numSendingOutgoingMessages--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.getDatabaseHelper().deletePendingMessage(message);
|
||||||
|
|
||||||
notifyMessageStatus(message, App.STATUS_CANCELLED,
|
notifyMessageStatus(message, App.STATUS_CANCELLED,
|
||||||
"deleted by user");
|
"deleted by user");
|
||||||
app.log(message.getDescription() + " deleted");
|
app.log(message.getDescription() + " deleted");
|
||||||
|
@ -17,7 +17,7 @@ public abstract class OutgoingMessage extends QueuedMessage {
|
|||||||
private String from;
|
private String from;
|
||||||
private String to;
|
private String to;
|
||||||
private int priority;
|
private int priority;
|
||||||
private int localId;
|
private int localId;
|
||||||
private static int nextLocalId = 1;
|
private static int nextLocalId = 1;
|
||||||
|
|
||||||
private ProcessingState state = ProcessingState.None;
|
private ProcessingState state = ProcessingState.None;
|
||||||
@ -48,6 +48,11 @@ public abstract class OutgoingMessage extends QueuedMessage {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OutgoingMessage newFromMessageType(App app, String type)
|
||||||
|
{
|
||||||
|
return new OutgoingSms(app);
|
||||||
|
}
|
||||||
|
|
||||||
public void setProcessingState(ProcessingState status)
|
public void setProcessingState(ProcessingState status)
|
||||||
{
|
{
|
||||||
this.state = status;
|
this.state = status;
|
||||||
@ -157,12 +162,11 @@ public abstract class OutgoingMessage extends QueuedMessage {
|
|||||||
return getDisplayType() + " to " + getTo();
|
return getDisplayType() + " to " + getTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract String getLogName();
|
|
||||||
|
|
||||||
public void validate() throws ValidationException
|
public void validate() throws ValidationException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract String getMessageType();
|
||||||
abstract ScheduleInfo scheduleSend();
|
abstract ScheduleInfo scheduleSend();
|
||||||
abstract void send(ScheduleInfo schedule);
|
abstract void send(ScheduleInfo schedule);
|
||||||
|
|
||||||
|
@ -11,9 +11,9 @@ public class OutgoingSms extends OutgoingMessage {
|
|||||||
super(app);
|
super(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLogName()
|
public String getMessageType()
|
||||||
{
|
{
|
||||||
return "SMS";
|
return App.MESSAGE_TYPE_SMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<String> _bodyParts;
|
private ArrayList<String> _bodyParts;
|
||||||
@ -65,7 +65,7 @@ public class OutgoingSms extends OutgoingMessage {
|
|||||||
|
|
||||||
if (numRetries == 0)
|
if (numRetries == 0)
|
||||||
{
|
{
|
||||||
app.log("Sending " + getDescription());
|
app.log("Sending " + getDisplayType());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -105,9 +105,16 @@ public class OutgoingSms extends OutgoingMessage {
|
|||||||
|
|
||||||
if (!app.isForwardablePhoneNumber(to))
|
if (!app.isForwardablePhoneNumber(to))
|
||||||
{
|
{
|
||||||
// this is mostly to prevent accidentally sending real messages to
|
if (app.isTestMode() && app.autoAddTestNumber())
|
||||||
// random people while testing...
|
{
|
||||||
throw new ValidationException("Destination address is not allowed");
|
app.addTestPhoneNumber(to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this is mostly to prevent accidentally sending real messages to
|
||||||
|
// random people while testing...
|
||||||
|
throw new ValidationException("Destination address is not allowed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String messageBody = getMessageBody();
|
String messageBody = getMessageBody();
|
||||||
|
@ -14,12 +14,29 @@ public abstract class QueuedMessage
|
|||||||
protected int numRetries = 0;
|
protected int numRetries = 0;
|
||||||
protected Date dateCreated = new Date();
|
protected Date dateCreated = new Date();
|
||||||
public App app;
|
public App app;
|
||||||
|
|
||||||
|
protected long persistedId = 0; // _id of row in pending_incoming_messages or pending_outgoing_messages table (0 if not stored)
|
||||||
|
|
||||||
public QueuedMessage(App app)
|
public QueuedMessage(App app)
|
||||||
{
|
{
|
||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPersisted()
|
||||||
|
{
|
||||||
|
return persistedId != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPersistedId()
|
||||||
|
{
|
||||||
|
return persistedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPersistedId(long id)
|
||||||
|
{
|
||||||
|
this.persistedId = id;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getDateCreated()
|
public Date getDateCreated()
|
||||||
{
|
{
|
||||||
return dateCreated;
|
return dateCreated;
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
package org.envaya.sms;
|
package org.envaya.sms;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class XmlUtils {
|
public class XmlUtils {
|
||||||
|
|
||||||
public static Document parseResponse(HttpResponse response)
|
public static Document parseResponse(HttpResponse response)
|
||||||
throws IOException, ParserConfigurationException, SAXException {
|
throws IOException, ParserConfigurationException, SAXException {
|
||||||
InputStream responseStream = response.getEntity().getContent();
|
InputStream responseStream = response.getEntity().getContent();
|
||||||
@ -38,4 +45,53 @@ public class XmlUtils {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<OutgoingMessage> getMessagesList(Document xml, App app, String defaultTo)
|
||||||
|
{
|
||||||
|
List<OutgoingMessage> messages = new ArrayList<OutgoingMessage>();
|
||||||
|
|
||||||
|
Element messagesElement = (Element) xml.getElementsByTagName("messages").item(0);
|
||||||
|
if (messagesElement != null)
|
||||||
|
{
|
||||||
|
NodeList messageNodes = messagesElement.getChildNodes();
|
||||||
|
int numNodes = messageNodes.getLength();
|
||||||
|
for (int i = 0; i < numNodes; i++)
|
||||||
|
{
|
||||||
|
Element messageElement = (Element) messageNodes.item(i);
|
||||||
|
|
||||||
|
String nodeName = messageElement.getNodeName();
|
||||||
|
|
||||||
|
OutgoingMessage message = OutgoingMessage.newFromMessageType(app, nodeName);
|
||||||
|
|
||||||
|
message.setFrom(app.getPhoneNumber());
|
||||||
|
|
||||||
|
String to = messageElement.getAttribute("to");
|
||||||
|
|
||||||
|
message.setTo("".equals(to) ? defaultTo : to);
|
||||||
|
|
||||||
|
String serverId = messageElement.getAttribute("id");
|
||||||
|
|
||||||
|
message.setServerId("".equals(serverId) ? null : serverId);
|
||||||
|
|
||||||
|
String priorityStr = messageElement.getAttribute("priority");
|
||||||
|
|
||||||
|
if (!priorityStr.equals(""))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
message.setPriority(Integer.parseInt(priorityStr));
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ex)
|
||||||
|
{
|
||||||
|
app.log("Invalid message priority: " + priorityStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message.setMessageBody(XmlUtils.getElementText(messageElement));
|
||||||
|
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
package org.envaya.sms.receiver;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent)
|
|
||||||
{
|
|
||||||
// just want to initialize App class to start outgoing message poll timer
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,6 +14,8 @@ public class ExpansionPackInstallReceiver extends BroadcastReceiver
|
|||||||
|
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
boolean isInstalled = !"android.intent.action.PACKAGE_REMOVED".equals(action);
|
||||||
|
|
||||||
String packageName = intent.getData().getSchemeSpecificPart();
|
String packageName = intent.getData().getSchemeSpecificPart();
|
||||||
|
|
||||||
if (packageName != null)
|
if (packageName != null)
|
||||||
|
22
src/org/envaya/sms/receiver/NudgeReceiver.java
Normal file
22
src/org/envaya/sms/receiver/NudgeReceiver.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
package org.envaya.sms.receiver;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
|
||||||
|
public class NudgeReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent)
|
||||||
|
{
|
||||||
|
// intentional side-effect: initialize App class to start outgoing message poll timer,
|
||||||
|
// and send any pending incoming messages that were persisted to DB before reboot.
|
||||||
|
|
||||||
|
//App app = (App)context.getApplicationContext();
|
||||||
|
|
||||||
|
//app.debug("Nudged by " + intent.getAction());
|
||||||
|
//app.log(".");
|
||||||
|
}
|
||||||
|
}
|
22
src/org/envaya/sms/receiver/StartAmqpConsumer.java
Normal file
22
src/org/envaya/sms/receiver/StartAmqpConsumer.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
package org.envaya.sms.receiver;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
|
||||||
|
public class StartAmqpConsumer extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
final App app = (App) context.getApplicationContext();
|
||||||
|
|
||||||
|
if (!app.isEnabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.getAmqpConsumer().startAsync();
|
||||||
|
}
|
||||||
|
}
|
44
src/org/envaya/sms/service/AmqpConsumerService.java
Normal file
44
src/org/envaya/sms/service/AmqpConsumerService.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package org.envaya.sms.service;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.Intent;
|
||||||
|
import org.envaya.sms.AmqpConsumer;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
|
||||||
|
public class AmqpConsumerService extends IntentService {
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
public AmqpConsumerService(String name)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AmqpConsumerService()
|
||||||
|
{
|
||||||
|
this("AmqpConsumerService");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
app = (App)this.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent)
|
||||||
|
{
|
||||||
|
boolean start = intent.getBooleanExtra("start", false);
|
||||||
|
|
||||||
|
AmqpConsumer consumer = app.getAmqpConsumer();
|
||||||
|
if (start)
|
||||||
|
{
|
||||||
|
consumer.startBlocking();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
consumer.stopBlocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
34
src/org/envaya/sms/service/AmqpHeartbeatService.java
Normal file
34
src/org/envaya/sms/service/AmqpHeartbeatService.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package org.envaya.sms.service;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.Intent;
|
||||||
|
import org.envaya.sms.AmqpConsumer;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
|
||||||
|
public class AmqpHeartbeatService extends IntentService {
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
public AmqpHeartbeatService(String name)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AmqpHeartbeatService()
|
||||||
|
{
|
||||||
|
this("AmqpHeartbeatService");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
app = (App)this.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent)
|
||||||
|
{
|
||||||
|
AmqpConsumer consumer = app.getAmqpConsumer();
|
||||||
|
consumer.sendHeartbeatBlocking();
|
||||||
|
}
|
||||||
|
}
|
91
src/org/envaya/sms/service/CheckMessagingService.java
Normal file
91
src/org/envaya/sms/service/CheckMessagingService.java
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
package org.envaya.sms.service;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.Intent;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.IncomingMms;
|
||||||
|
import org.envaya.sms.IncomingSms;
|
||||||
|
import org.envaya.sms.MessagingUtils;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CheckMessagingService extends IntentService
|
||||||
|
{
|
||||||
|
private App app;
|
||||||
|
private MessagingUtils messagingUtils;
|
||||||
|
|
||||||
|
public CheckMessagingService(String name)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckMessagingService()
|
||||||
|
{
|
||||||
|
this("CheckMessagingService");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
app = (App)this.getApplicationContext();
|
||||||
|
|
||||||
|
messagingUtils = app.getMessagingUtils();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent)
|
||||||
|
{
|
||||||
|
checkNewSentSms();
|
||||||
|
|
||||||
|
checkNewMms();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNewSentSms()
|
||||||
|
{
|
||||||
|
List<IncomingSms> messages = messagingUtils.getSentSmsMessages(true);
|
||||||
|
for (IncomingSms sms : messages)
|
||||||
|
{
|
||||||
|
messagingUtils.markSeenSentSms(sms);
|
||||||
|
|
||||||
|
if (sms.isForwardable())
|
||||||
|
{
|
||||||
|
app.log("SMS id=" + sms.getMessagingId() + " sent via Messaging app");
|
||||||
|
app.inbox.forwardMessage(sms);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Ignoring SMS sent via Messaging app");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNewMms()
|
||||||
|
{
|
||||||
|
List<IncomingMms> messages = messagingUtils.getMessagesInMmsInbox(true);
|
||||||
|
for (IncomingMms mms : messages)
|
||||||
|
{
|
||||||
|
String from = mms.getFrom();
|
||||||
|
if (from == null || from.length() == 0)
|
||||||
|
{
|
||||||
|
// sender phone number may not be written to Messaging database yet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent forwarding MMS messages that existed in inbox
|
||||||
|
// before EnvayaSMS started, or re-forwarding MMS multiple
|
||||||
|
// times if we don't delete them.
|
||||||
|
messagingUtils.markSeenMms(mms);
|
||||||
|
|
||||||
|
if (mms.isForwardable())
|
||||||
|
{
|
||||||
|
app.log("New MMS id=" + mms.getMessagingId() + " in inbox");
|
||||||
|
app.inbox.forwardMessage(mms);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.log("Ignoring incoming MMS from " + mms.getFrom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
src/org/envaya/sms/service/EnabledChangedService.java
Normal file
66
src/org/envaya/sms/service/EnabledChangedService.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package org.envaya.sms.service;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.telephony.PhoneStateListener;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.receiver.NudgeReceiver;
|
||||||
|
|
||||||
|
public class EnabledChangedService extends IntentService {
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
public EnabledChangedService(String name)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnabledChangedService()
|
||||||
|
{
|
||||||
|
this("EnabledChangedService");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
app = (App)this.getApplicationContext();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent)
|
||||||
|
{
|
||||||
|
TelephonyManager telephony = (TelephonyManager)
|
||||||
|
getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
|
|
||||||
|
startService(new Intent(app, ForegroundService.class));
|
||||||
|
|
||||||
|
app.setOutgoingMessageAlarm();
|
||||||
|
|
||||||
|
AlarmManager alarmManager = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
alarmManager.cancel(PendingIntent.getBroadcast(app, 0, new Intent(app, NudgeReceiver.class), 0));
|
||||||
|
|
||||||
|
if (app.isEnabled())
|
||||||
|
{
|
||||||
|
app.getMessagingObserver().register();
|
||||||
|
|
||||||
|
telephony.listen(app.getCallListener(), PhoneStateListener.LISTEN_CALL_STATE);
|
||||||
|
|
||||||
|
app.getDatabaseHelper().restorePendingMessages();
|
||||||
|
|
||||||
|
app.getAmqpConsumer().startAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.getMessagingObserver().unregister();
|
||||||
|
telephony.listen(app.getCallListener(), PhoneStateListener.LISTEN_NONE);
|
||||||
|
|
||||||
|
app.getAmqpConsumer().stopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/org/envaya/sms/ForegroundService.java → src/org/envaya/sms/service/ForegroundService.java
Executable file → Normal file
15
src/org/envaya/sms/ForegroundService.java → src/org/envaya/sms/service/ForegroundService.java
Executable file → Normal file
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.envaya.sms;
|
package org.envaya.sms.service;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@ -23,10 +23,12 @@ import android.app.Service;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.R;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import org.envaya.sms.ui.LogView;
|
import org.envaya.sms.ui.Main;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Service running in foreground to make sure App instance stays
|
* Service running in foreground to make sure App instance stays
|
||||||
@ -159,13 +161,12 @@ public class ForegroundService extends Service {
|
|||||||
System.currentTimeMillis());
|
System.currentTimeMillis());
|
||||||
|
|
||||||
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||||
new Intent(this, LogView.class), 0);
|
new Intent(this, Main.class), 0);
|
||||||
|
|
||||||
notification.setLatestEventInfo(this,
|
CharSequence info = getText(R.string.running);
|
||||||
"EnvayaSMS running",
|
notification.setLatestEventInfo(this, info, text, contentIntent);
|
||||||
text, contentIntent);
|
|
||||||
|
|
||||||
startForegroundCompat(R.string.service_started, notification);
|
startForegroundCompat(R.string.service_started, notification);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
@ -3,6 +3,8 @@ package org.envaya.sms.task;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import org.envaya.sms.App;
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.JsonUtils;
|
||||||
|
import org.envaya.sms.XmlUtils;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -17,6 +19,9 @@ 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.json.JSONObject;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.envaya.sms.R;
|
||||||
|
|
||||||
public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
||||||
|
|
||||||
@ -34,6 +39,8 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
this.app = app;
|
this.app = app;
|
||||||
params = new ArrayList<BasicNameValuePair>(Arrays.asList(paramsArr));
|
params = new ArrayList<BasicNameValuePair>(Arrays.asList(paramsArr));
|
||||||
|
|
||||||
|
params.add(new BasicNameValuePair("version", "" + app.getPackageInfo().versionCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addParam(String name, String value)
|
public void addParam(String name, String value)
|
||||||
@ -51,7 +58,7 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
{
|
{
|
||||||
HttpPost httpPost = new HttpPost(url);
|
HttpPost httpPost = new HttpPost(url);
|
||||||
|
|
||||||
httpPost.setHeader("User-Agent", "EnvayaSMS/" + app.getPackageInfo().versionName + " (Android; SDK "+Build.VERSION.SDK_INT + "; " + Build.MANUFACTURER + "; " + Build.MODEL+")");
|
httpPost.setHeader("User-Agent", app.getText(R.string.app_name) + "/" + app.getPackageInfo().versionName + " (Android; SDK "+Build.VERSION.SDK_INT + "; " + Build.MANUFACTURER + "; " + Build.MODEL+")");
|
||||||
|
|
||||||
if (useMultipartPost)
|
if (useMultipartPost)
|
||||||
{
|
{
|
||||||
@ -83,6 +90,7 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
post = makeHttpPost();
|
post = makeHttpPost();
|
||||||
|
|
||||||
HttpClient client = app.getHttpClient();
|
HttpClient client = app.getHttpClient();
|
||||||
return client.execute(post);
|
return client.execute(post);
|
||||||
}
|
}
|
||||||
@ -97,9 +105,10 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
if ((ex instanceof IOException)
|
if ((ex instanceof IOException)
|
||||||
&& message != null && message.equals("Connection already shutdown"))
|
&& message != null && message.equals("Connection already shutdown"))
|
||||||
{
|
{
|
||||||
//app.log("Retrying request");
|
// app.log("Retrying request");
|
||||||
post = makeHttpPost();
|
post = makeHttpPost();
|
||||||
HttpClient client = app.getHttpClient();
|
HttpClient client = app.getHttpClient();
|
||||||
|
|
||||||
return client.execute(post);
|
return client.execute(post);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,10 +120,35 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValidContentType(String contentType)
|
protected String getErrorText(HttpResponse response)
|
||||||
|
throws Exception
|
||||||
{
|
{
|
||||||
return true; // contentType.startsWith("text/xml");
|
String contentType = getContentType(response);
|
||||||
|
String error = null;
|
||||||
|
|
||||||
|
if (contentType.startsWith("application/json"))
|
||||||
|
{
|
||||||
|
JSONObject json = JsonUtils.parseResponse(response);
|
||||||
|
error = JsonUtils.getErrorText(json);
|
||||||
|
}
|
||||||
|
else if (contentType.startsWith("text/xml"))
|
||||||
|
{
|
||||||
|
Document xml = XmlUtils.parseResponse(response);
|
||||||
|
error = XmlUtils.getErrorText(xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == null)
|
||||||
|
{
|
||||||
|
error = "HTTP " + response.getStatusLine().getStatusCode();
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getContentType(HttpResponse response)
|
||||||
|
{
|
||||||
|
Header contentTypeHeader = response.getFirstHeader("Content-Type");
|
||||||
|
return (contentTypeHeader != null) ? contentTypeHeader.getValue() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -123,24 +157,13 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
Header contentTypeHeader = response.getFirstHeader("Content-Type");
|
|
||||||
String contentType = (contentTypeHeader != null) ? contentTypeHeader.getValue() : "";
|
|
||||||
|
|
||||||
boolean validContentType = isValidContentType(contentType);
|
|
||||||
|
|
||||||
if (statusCode == 200)
|
if (statusCode == 200)
|
||||||
{
|
{
|
||||||
if (validContentType)
|
handleResponse(response);
|
||||||
{
|
|
||||||
handleResponse(response);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception("Invalid response type " + contentType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (statusCode >= 400 && statusCode <= 499 && validContentType)
|
else if (statusCode >= 400 && statusCode <= 499)
|
||||||
{
|
{
|
||||||
handleErrorResponse(response);
|
handleErrorResponse(response);
|
||||||
handleFailure();
|
handleFailure();
|
||||||
@ -174,11 +197,6 @@ public class BaseHttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
|
|
||||||
protected void handleResponse(HttpResponse response) throws Exception
|
protected void handleResponse(HttpResponse response) throws Exception
|
||||||
{
|
{
|
||||||
// if we get a valid server response after a connectivity error, then forward any pending messages
|
|
||||||
if (app.hasConnectivityError())
|
|
||||||
{
|
|
||||||
app.onConnectivityRestored();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleErrorResponse(HttpResponse response) throws Exception
|
protected void handleErrorResponse(HttpResponse response) throws Exception
|
||||||
|
@ -31,11 +31,17 @@ public class CheckConnectivityTask extends AsyncTask<String, Void, Boolean> {
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
InetAddress addr = InetAddress.getByName(hostName);
|
InetAddress addr = InetAddress.getByName(hostName);
|
||||||
if (addr.isReachable(App.HTTP_CONNECTION_TIMEOUT))
|
if (addr.isReachable(App.HTTP_CONNECTION_TIMEOUT))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,6 @@ package org.envaya.sms.task;
|
|||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.envaya.sms.IncomingMessage;
|
import org.envaya.sms.IncomingMessage;
|
||||||
import org.envaya.sms.OutgoingMessage;
|
|
||||||
|
|
||||||
public class ForwarderTask extends HttpTask {
|
public class ForwarderTask extends HttpTask {
|
||||||
|
|
||||||
@ -14,12 +13,6 @@ public class ForwarderTask extends HttpTask {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValidContentType(String contentType)
|
|
||||||
{
|
|
||||||
return contentType.startsWith("text/xml");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDefaultToAddress() {
|
protected String getDefaultToAddress() {
|
||||||
return message.getFrom();
|
return message.getFrom();
|
||||||
@ -27,17 +20,12 @@ public class ForwarderTask extends HttpTask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleResponse(HttpResponse response) throws Exception {
|
protected void handleResponse(HttpResponse response) throws Exception {
|
||||||
|
app.inbox.messageForwarded(message);
|
||||||
for (OutgoingMessage reply : parseResponseXML(response)) {
|
|
||||||
app.outbox.sendMessage(reply);
|
|
||||||
}
|
|
||||||
app.inbox.messageForwarded(message);
|
|
||||||
|
|
||||||
super.handleResponse(response);
|
super.handleResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleFailure() {
|
protected void handleFailure() {
|
||||||
app.inbox.messageFailed(message);
|
app.inbox.messageFailed(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,32 +4,31 @@
|
|||||||
*/
|
*/
|
||||||
package org.envaya.sms.task;
|
package org.envaya.sms.task;
|
||||||
|
|
||||||
import org.envaya.sms.XmlUtils;
|
|
||||||
import org.envaya.sms.OutgoingSms;
|
|
||||||
import org.envaya.sms.OutgoingMessage;
|
import org.envaya.sms.OutgoingMessage;
|
||||||
import org.envaya.sms.App;
|
import org.envaya.sms.JsonUtils;
|
||||||
import org.envaya.sms.Base64Coder;
|
import org.envaya.sms.Base64Coder;
|
||||||
import android.content.SharedPreferences;
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.XmlUtils;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class HttpTask extends BaseHttpTask {
|
public class HttpTask extends BaseHttpTask {
|
||||||
@ -97,11 +96,37 @@ public class HttpTask extends BaseHttpTask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
logEntries = app.getNewLogEntries();
|
logEntries = app.getNewLogEntries();
|
||||||
|
|
||||||
params.add(new BasicNameValuePair("version", "" + app.getPackageInfo().versionCode));
|
|
||||||
params.add(new BasicNameValuePair("phone_number", app.getPhoneNumber()));
|
params.add(new BasicNameValuePair("phone_number", app.getPhoneNumber()));
|
||||||
|
params.add(new BasicNameValuePair("phone_id", app.getPhoneID()));
|
||||||
|
params.add(new BasicNameValuePair("phone_token", app.getPhoneToken()));
|
||||||
params.add(new BasicNameValuePair("send_limit", "" + app.getOutgoingMessageLimit()));
|
params.add(new BasicNameValuePair("send_limit", "" + app.getOutgoingMessageLimit()));
|
||||||
|
params.add(new BasicNameValuePair("now", "" + System.currentTimeMillis()));
|
||||||
|
params.add(new BasicNameValuePair("settings_version", "" + app.getSettingsVersion()));
|
||||||
|
|
||||||
|
Intent lastBatteryIntent = app.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
|
|
||||||
|
if (lastBatteryIntent != null)
|
||||||
|
{
|
||||||
|
// BatteryManager.EXTRA_* constants introduced in API level 5 (2.0)
|
||||||
|
int rawLevel = lastBatteryIntent.getIntExtra("level", -1);
|
||||||
|
int scale = lastBatteryIntent.getIntExtra("scale", -1);
|
||||||
|
|
||||||
|
int pctLevel = (rawLevel > 0 && scale > 0) ? (rawLevel * 100 / scale) : rawLevel;
|
||||||
|
|
||||||
|
if (pctLevel >= 0)
|
||||||
|
{
|
||||||
|
params.add(new BasicNameValuePair("battery", "" + pctLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
int plugged = lastBatteryIntent.getIntExtra("plugged", -1);
|
||||||
|
|
||||||
|
if (plugged >= 0)
|
||||||
|
{
|
||||||
|
params.add(new BasicNameValuePair("power", "" + plugged));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConnectivityManager cm =
|
ConnectivityManager cm =
|
||||||
(ConnectivityManager)app.getSystemService(App.CONNECTIVITY_SERVICE);
|
(ConnectivityManager)app.getSystemService(App.CONNECTIVITY_SERVICE);
|
||||||
@ -133,56 +158,29 @@ public class HttpTask extends BaseHttpTask {
|
|||||||
protected String getDefaultToAddress()
|
protected String getDefaultToAddress()
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleResponseJSON(JSONObject json)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
JsonUtils.processEvents(json, app, getDefaultToAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<OutgoingMessage> parseResponseXML(HttpResponse response)
|
protected void handleResponseXML(Document xml)
|
||||||
throws IOException, ParserConfigurationException, SAXException
|
throws IOException, ParserConfigurationException, SAXException
|
||||||
{
|
{
|
||||||
List<OutgoingMessage> messages = new ArrayList<OutgoingMessage>();
|
for (OutgoingMessage message : XmlUtils.getMessagesList(xml, app, getDefaultToAddress()))
|
||||||
Document xml = XmlUtils.parseResponse(response);
|
|
||||||
|
|
||||||
Element messagesElement = (Element) xml.getElementsByTagName("messages").item(0);
|
|
||||||
if (messagesElement != null)
|
|
||||||
{
|
{
|
||||||
NodeList messageNodes = messagesElement.getChildNodes();
|
app.outbox.sendMessage(message);
|
||||||
int numNodes = messageNodes.getLength();
|
}
|
||||||
for (int i = 0; i < numNodes; i++)
|
}
|
||||||
{
|
|
||||||
Element messageElement = (Element) messageNodes.item(i);
|
protected void handleUnknownContentType(String contentType)
|
||||||
|
throws Exception
|
||||||
OutgoingMessage message = new OutgoingSms(app);
|
{
|
||||||
|
// old server API only mandated valid content type for action=outgoing
|
||||||
message.setFrom(app.getPhoneNumber());
|
app.log("Warning: Unknown response type " + contentType);
|
||||||
|
}
|
||||||
String to = messageElement.getAttribute("to");
|
|
||||||
|
|
||||||
message.setTo(to.equals("") ? getDefaultToAddress() : to);
|
|
||||||
|
|
||||||
String serverId = messageElement.getAttribute("id");
|
|
||||||
|
|
||||||
message.setServerId(serverId.equals("") ? null : serverId);
|
|
||||||
|
|
||||||
String priorityStr = messageElement.getAttribute("priority");
|
|
||||||
|
|
||||||
if (!priorityStr.equals(""))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
message.setPriority(Integer.parseInt(priorityStr));
|
|
||||||
}
|
|
||||||
catch (NumberFormatException ex)
|
|
||||||
{
|
|
||||||
app.log("Invalid message priority: " + priorityStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message.setMessageBody(XmlUtils.getElementText(messageElement));
|
|
||||||
|
|
||||||
messages.add(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleFailure()
|
protected void handleFailure()
|
||||||
@ -221,16 +219,38 @@ public class HttpTask extends BaseHttpTask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleErrorResponse(HttpResponse response) throws Exception
|
public void handleErrorResponse(HttpResponse response) throws Exception
|
||||||
{
|
{
|
||||||
Document xml = XmlUtils.parseResponse(response);
|
app.log(getErrorText(response));
|
||||||
String error = XmlUtils.getErrorText(xml);
|
}
|
||||||
if (error != null)
|
|
||||||
|
@Override
|
||||||
|
protected void handleResponse(HttpResponse response) throws Exception {
|
||||||
|
|
||||||
|
String contentType = getContentType(response);
|
||||||
|
|
||||||
|
if (contentType.startsWith("application/json"))
|
||||||
{
|
{
|
||||||
app.log(error);
|
String responseBody = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
|
||||||
}
|
|
||||||
|
JSONObject json = new JSONObject(responseBody);
|
||||||
|
|
||||||
|
handleResponseJSON(json);
|
||||||
|
}
|
||||||
|
else if (contentType.startsWith("text/xml"))
|
||||||
|
{
|
||||||
|
Document xml = XmlUtils.parseResponse(response);
|
||||||
|
|
||||||
|
handleResponseXML(xml);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
app.log("HTTP " +response.getStatusLine().getStatusCode());
|
handleUnknownContentType(contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we get a valid server response after a connectivity error, then forward any pending messages
|
||||||
|
if (app.hasConnectivityError())
|
||||||
|
{
|
||||||
|
app.onConnectivityRestored();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ package org.envaya.sms.task;
|
|||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.envaya.sms.App;
|
import org.envaya.sms.App;
|
||||||
import org.envaya.sms.OutgoingMessage;
|
|
||||||
|
|
||||||
public class PollerTask extends HttpTask {
|
public class PollerTask extends HttpTask {
|
||||||
|
|
||||||
@ -12,12 +11,6 @@ public class PollerTask extends HttpTask {
|
|||||||
super(app, new BasicNameValuePair("action", App.ACTION_OUTGOING));
|
super(app, new BasicNameValuePair("action", App.ACTION_OUTGOING));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValidContentType(String contentType)
|
|
||||||
{
|
|
||||||
return contentType.startsWith("text/xml");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(HttpResponse response) {
|
protected void onPostExecute(HttpResponse response) {
|
||||||
super.onPostExecute(response);
|
super.onPostExecute(response);
|
||||||
@ -25,9 +18,9 @@ public class PollerTask extends HttpTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleResponse(HttpResponse response) throws Exception {
|
protected void handleUnknownContentType(String contentType)
|
||||||
for (OutgoingMessage reply : parseResponseXML(response)) {
|
throws Exception
|
||||||
app.outbox.sendMessage(reply);
|
{
|
||||||
}
|
throw new Exception("Invalid response type " + contentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
81
src/org/envaya/sms/ui/ExpansionPacks.java
Normal file
81
src/org/envaya/sms/ui/ExpansionPacks.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package org.envaya.sms.ui;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.preference.PreferenceScreen;
|
||||||
|
import android.view.Menu;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.R;
|
||||||
|
|
||||||
|
public class ExpansionPacks extends PreferenceActivity {
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
private BroadcastReceiver installReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
updateInstallStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.expansion_packs);
|
||||||
|
|
||||||
|
app = (App) getApplication();
|
||||||
|
|
||||||
|
IntentFilter installReceiverFilter = new IntentFilter();
|
||||||
|
installReceiverFilter.addAction(App.EXPANSION_PACKS_CHANGED_INTENT);
|
||||||
|
registerReceiver(installReceiver, installReceiverFilter);
|
||||||
|
|
||||||
|
updateInstallStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy()
|
||||||
|
{
|
||||||
|
unregisterReceiver(installReceiver);
|
||||||
|
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateInstallStatus()
|
||||||
|
{
|
||||||
|
PreferenceScreen screen = this.getPreferenceScreen();
|
||||||
|
int numPrefs = screen.getPreferenceCount();
|
||||||
|
|
||||||
|
String basePackageName = app.getPackageName();
|
||||||
|
|
||||||
|
this.setTitle("Telerivet : SMS Rate Limit ("+app.getOutgoingMessageLimit()+")");
|
||||||
|
|
||||||
|
for(int i=0; i < numPrefs;i++)
|
||||||
|
{
|
||||||
|
Preference p = screen.getPreference(i);
|
||||||
|
|
||||||
|
String packageNum = p.getKey();
|
||||||
|
|
||||||
|
String packageName = basePackageName + "." + packageNum;
|
||||||
|
|
||||||
|
if (app.isSmsExpansionPackInstalled(packageName))
|
||||||
|
{
|
||||||
|
p.setSummary("Installed.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.setSummary("Not installed.\nInstall to increase limit by 100 SMS/hour...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
this.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ public class Help extends Activity {
|
|||||||
|
|
||||||
app = (App)getApplication();
|
app = (App)getApplication();
|
||||||
|
|
||||||
String html = "<b>EnvayaSMS " + app.getPackageInfo().versionName + "</b><br /><br />"
|
String html = "<b>"+getText(R.string.app_name)+" " + app.getPackageInfo().versionName + "</b><br /><br />"
|
||||||
+ "Menu icons cc/by www.androidicons.com<br /><br />";
|
+ "Menu icons cc/by www.androidicons.com<br /><br />";
|
||||||
|
|
||||||
help.setText(Html.fromHtml(html));
|
help.setText(Html.fromHtml(html));
|
||||||
|
@ -2,21 +2,28 @@ package org.envaya.sms.ui;
|
|||||||
|
|
||||||
import org.envaya.sms.task.HttpTask;
|
import org.envaya.sms.task.HttpTask;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
|
import android.content.DialogInterface.OnCancelListener;
|
||||||
|
import android.content.DialogInterface.OnClickListener;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.Html;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.envaya.sms.App;
|
import org.envaya.sms.App;
|
||||||
import org.envaya.sms.R;
|
import org.envaya.sms.R;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class LogView extends Activity {
|
public class LogView extends Activity {
|
||||||
|
|
||||||
@ -29,8 +36,25 @@ public class LogView extends Activity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private BroadcastReceiver settingsReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
updateUpgradeButton();
|
||||||
|
updateInfo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private BroadcastReceiver expansionPacksReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
updateInfo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private ScrollView scrollView;
|
private ScrollView scrollView;
|
||||||
private TextView info;
|
private TextView info;
|
||||||
|
private TextView log;
|
||||||
|
private TextView heading;
|
||||||
|
|
||||||
private class TestTask extends HttpTask
|
private class TestTask extends HttpTask
|
||||||
{
|
{
|
||||||
@ -47,14 +71,31 @@ public class LogView extends Activity {
|
|||||||
|
|
||||||
private int lastLogEpoch = -1;
|
private int lastLogEpoch = -1;
|
||||||
|
|
||||||
public void updateLogView()
|
public void updateUpgradeButton()
|
||||||
|
{
|
||||||
|
Button upgradeButton = (Button) this.findViewById(R.id.upgrade_button);
|
||||||
|
boolean isUpgradeAvailable = app.isUpgradeAvailable();
|
||||||
|
if (isUpgradeAvailable)
|
||||||
|
{
|
||||||
|
upgradeButton.setText("New version of app available ("+app.getMarketVersionName()+").\nClick to install...");
|
||||||
|
upgradeButton.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upgradeButton.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void updateLogView()
|
||||||
{
|
{
|
||||||
int logEpoch = app.getLogEpoch();
|
int logEpoch = app.getLogEpoch();
|
||||||
CharSequence displayedLog = app.getDisplayedLog();
|
CharSequence displayedLog = app.getDisplayedLog();
|
||||||
|
int logEpoch2 = app.getLogEpoch();
|
||||||
|
|
||||||
if (lastLogEpoch == logEpoch)
|
if (lastLogEpoch == logEpoch && logEpoch == logEpoch2)
|
||||||
{
|
{
|
||||||
int beforeLen = info.getText().length();
|
int beforeLen = log.getText().length();
|
||||||
int afterLen = displayedLog.length();
|
int afterLen = displayedLog.length();
|
||||||
|
|
||||||
if (beforeLen == afterLen)
|
if (beforeLen == afterLen)
|
||||||
@ -62,11 +103,11 @@ public class LogView extends Activity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.append(displayedLog, beforeLen, afterLen);
|
log.append(displayedLog, beforeLen, afterLen);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info.setText(displayedLog);
|
log.setText(displayedLog);
|
||||||
lastLogEpoch = logEpoch;
|
lastLogEpoch = logEpoch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,24 +126,227 @@ public class LogView extends Activity {
|
|||||||
setContentView(R.layout.log_view);
|
setContentView(R.layout.log_view);
|
||||||
PreferenceManager.setDefaultValues(this, R.xml.prefs, false);
|
PreferenceManager.setDefaultValues(this, R.xml.prefs, false);
|
||||||
|
|
||||||
scrollView = (ScrollView) this.findViewById(R.id.info_scroll);
|
scrollView = (ScrollView) this.findViewById(R.id.log_scroll);
|
||||||
info = (TextView) this.findViewById(R.id.info);
|
|
||||||
|
|
||||||
info.setMovementMethod(LinkMovementMethod.getInstance());
|
heading = (TextView) this.findViewById(R.id.heading);
|
||||||
|
info = (TextView) this.findViewById(R.id.info);
|
||||||
|
|
||||||
//info.setMovementMethod(new ScrollingMovementMethod());
|
updateInfo();
|
||||||
|
|
||||||
|
log = (TextView) this.findViewById(R.id.log);
|
||||||
|
|
||||||
|
log.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
|
||||||
|
updateUpgradeButton();
|
||||||
updateLogView();
|
updateLogView();
|
||||||
|
|
||||||
IntentFilter logReceiverFilter = new IntentFilter();
|
registerReceiver(logReceiver, new IntentFilter(App.LOG_CHANGED_INTENT));
|
||||||
logReceiverFilter.addAction(App.LOG_CHANGED_INTENT);
|
registerReceiver(settingsReceiver, new IntentFilter(App.SETTINGS_CHANGED_INTENT));
|
||||||
registerReceiver(logReceiver, logReceiverFilter);
|
registerReceiver(expansionPacksReceiver, new IntentFilter(App.EXPANSION_PACKS_CHANGED_INTENT));
|
||||||
|
|
||||||
|
if (savedInstanceState == null)
|
||||||
|
{
|
||||||
|
if (getIntent().getBooleanExtra("configured", false))
|
||||||
|
{
|
||||||
|
showConfigureSuccessDialog();
|
||||||
|
}
|
||||||
|
else if (app.isUpgradeAvailable())
|
||||||
|
{
|
||||||
|
showUpgradeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
curDialog = savedInstanceState.getInt("cur_dialog", 0);
|
||||||
|
if (curDialog == UPGRADE_DIALOG)
|
||||||
|
{
|
||||||
|
showUpgradeDialog();
|
||||||
|
}
|
||||||
|
else if (curDialog == CONFIGURE_SUCCESS_DIALOG)
|
||||||
|
{
|
||||||
|
showConfigureSuccessDialog();
|
||||||
|
}
|
||||||
|
else if (curDialog == SETTINGS_DIALOG)
|
||||||
|
{
|
||||||
|
showSettingsDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final int NO_DIALOG = 0;
|
||||||
|
public static final int UPGRADE_DIALOG = 1;
|
||||||
|
public static final int CONFIGURE_SUCCESS_DIALOG = 2;
|
||||||
|
public static final int SETTINGS_DIALOG = 3;
|
||||||
|
|
||||||
|
private int curDialog = NO_DIALOG;
|
||||||
|
|
||||||
|
public void updateInfo()
|
||||||
|
{
|
||||||
|
boolean enabled = app.isEnabled();
|
||||||
|
|
||||||
|
heading.setText(Html.fromHtml(
|
||||||
|
enabled ? "<b>" + getText(R.string.running) + " ("+app.getPhoneNumber()+")</b>"
|
||||||
|
: "<b>" +getText(R.string.disabled) + "</b>"));
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
info.setText("New messages will be forwarded to server");
|
||||||
|
|
||||||
|
if (app.isTestMode())
|
||||||
|
{
|
||||||
|
info.append("\n(Test mode enabled)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info.setText("New messages will not be forwarded to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void infoClicked(View v)
|
||||||
|
{
|
||||||
|
startActivity(new Intent(this, Prefs.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState)
|
||||||
|
{
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.putInt("cur_dialog", curDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showUpgradeDialog()
|
||||||
|
{
|
||||||
|
curDialog = UPGRADE_DIALOG;
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Upgrade available")
|
||||||
|
.setMessage("A new version of the app is available ("+app.getMarketVersionName()+"). Do you want to upgrade now?")
|
||||||
|
.setPositiveButton("OK", new OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int i)
|
||||||
|
{
|
||||||
|
upgradeClicked(null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("Not Now", new DismissDialogListener())
|
||||||
|
.setOnCancelListener(new DismissDialogListener())
|
||||||
|
.setCancelable(true)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showConfigureSuccessDialog()
|
||||||
|
{
|
||||||
|
curDialog = CONFIGURE_SUCCESS_DIALOG;
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("App configured successfully!")
|
||||||
|
.setMessage("Now try using another mobile phone to send a SMS to this phone.\n\nYou should be able to see the message on your Messages page on telerivet.com, and send replies.")
|
||||||
|
.setPositiveButton("OK", new OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int i)
|
||||||
|
{
|
||||||
|
showSettingsDialog();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setOnCancelListener(new DismissDialogListener())
|
||||||
|
.setCancelable(true)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSettingsSummary()
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
if (app.getKeepInInbox())
|
||||||
|
{
|
||||||
|
builder.append("- New messages kept in Messaging inbox\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.append("- New messages not kept in Messaging inbox\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.callNotificationsEnabled())
|
||||||
|
{
|
||||||
|
builder.append("- Call notifications enabled\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.append("- Call notifications disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> ignoredNumbers = app.getIgnoredPhoneNumbers();
|
||||||
|
boolean ignoreShortcodes = app.ignoreShortcodes();
|
||||||
|
boolean ignoreNonNumeric = app.ignoreNonNumeric();
|
||||||
|
boolean testMode = app.isTestMode();
|
||||||
|
|
||||||
|
builder.append("- Send up to " + app.getOutgoingMessageLimit()+ " SMS/hour\n");
|
||||||
|
|
||||||
|
if (ignoredNumbers.isEmpty() && !ignoreShortcodes && !ignoreNonNumeric && !testMode)
|
||||||
|
{
|
||||||
|
builder.append("- Forward messages from all phone numbers");
|
||||||
|
}
|
||||||
|
else if (testMode)
|
||||||
|
{
|
||||||
|
builder.append("- Forward messages only from certain phone numbers");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.append("- Ignore messages from some phone numbers");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showSettingsDialog()
|
||||||
|
{
|
||||||
|
curDialog = SETTINGS_DIALOG;
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Verify Settings")
|
||||||
|
.setMessage(getSettingsSummary())
|
||||||
|
.setPositiveButton("OK", new DismissDialogListener())
|
||||||
|
.setNegativeButton("Change", new OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int i)
|
||||||
|
{
|
||||||
|
curDialog = NO_DIALOG;
|
||||||
|
startActivity(new Intent(LogView.this, Prefs.class));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setOnCancelListener(new DismissDialogListener())
|
||||||
|
.setCancelable(true)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DismissDialogListener implements OnClickListener, OnCancelListener
|
||||||
|
{
|
||||||
|
public void onCancel(DialogInterface dialog)
|
||||||
|
{
|
||||||
|
curDialog = NO_DIALOG;
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick(DialogInterface dialog, int i)
|
||||||
|
{
|
||||||
|
curDialog = NO_DIALOG;
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upgradeClicked(View v)
|
||||||
|
{
|
||||||
|
startActivity(new Intent(Intent.ACTION_VIEW,
|
||||||
|
Uri.parse("market://details?id=" + app.getPackageInfo().applicationInfo.packageName)));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy()
|
public void onDestroy()
|
||||||
{
|
{
|
||||||
this.unregisterReceiver(logReceiver);
|
unregisterReceiver(logReceiver);
|
||||||
|
unregisterReceiver(settingsReceiver);
|
||||||
|
unregisterReceiver(expansionPacksReceiver);
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +363,8 @@ public class LogView extends Activity {
|
|||||||
case R.id.retry_now:
|
case R.id.retry_now:
|
||||||
app.retryStuckMessages();
|
app.retryStuckMessages();
|
||||||
return true;
|
return true;
|
||||||
case R.id.forward_inbox:
|
case R.id.forward_saved:
|
||||||
startActivity(new Intent(this, MessagingInbox.class));
|
startActivity(new Intent(this, MessagingSmsInbox.class));
|
||||||
return true;
|
return true;
|
||||||
case R.id.pending:
|
case R.id.pending:
|
||||||
startActivity(new Intent(this, PendingMessages.class));
|
startActivity(new Intent(this, PendingMessages.class));
|
||||||
|
23
src/org/envaya/sms/ui/Main.java
Normal file
23
src/org/envaya/sms/ui/Main.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package org.envaya.sms.ui;
|
||||||
|
|
||||||
|
import org.envaya.sms.ui.Prefs;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import org.envaya.sms.App;
|
||||||
|
import org.envaya.sms.ui.LogView;
|
||||||
|
|
||||||
|
public class Main extends Activity {
|
||||||
|
|
||||||
|
private App app;
|
||||||
|
|
||||||
|
/** Called when the activity is first created. */
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
app = (App)getApplication();
|
||||||
|
|
||||||
|
startActivity(new Intent(this, LogView.class));
|
||||||
|
}
|
||||||
|
}
|
115
src/org/envaya/sms/ui/MessagingInbox.java → src/org/envaya/sms/ui/MessagingForwarder.java
Executable file → Normal file
115
src/org/envaya/sms/ui/MessagingInbox.java → src/org/envaya/sms/ui/MessagingForwarder.java
Executable file → Normal file
@ -5,29 +5,28 @@ import android.app.AlertDialog;
|
|||||||
import android.app.ListActivity;
|
import android.app.ListActivity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.DialogInterface.OnClickListener;
|
import android.content.DialogInterface.OnClickListener;
|
||||||
import android.database.Cursor;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.*;
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
import android.widget.ListView;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
import android.widget.SimpleCursorAdapter;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import org.envaya.sms.App;
|
import org.envaya.sms.App;
|
||||||
import org.envaya.sms.IncomingMessage;
|
import org.envaya.sms.IncomingMessage;
|
||||||
import org.envaya.sms.IncomingSms;
|
|
||||||
import org.envaya.sms.R;
|
import org.envaya.sms.R;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
public class MessagingInbox extends ListActivity {
|
public abstract class MessagingForwarder extends ListActivity {
|
||||||
|
|
||||||
private App app;
|
protected App app;
|
||||||
|
|
||||||
private Cursor cur;
|
abstract int getMessageCount();
|
||||||
|
abstract IncomingMessage getMessageAtPosition(int position);
|
||||||
|
abstract void initListAdapter();
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
/** Called when the activity is first created. */
|
||||||
@Override
|
@Override
|
||||||
@ -37,24 +36,59 @@ public class MessagingInbox extends ListActivity {
|
|||||||
app = (App) getApplication();
|
app = (App) getApplication();
|
||||||
|
|
||||||
setContentView(R.layout.inbox);
|
setContentView(R.layout.inbox);
|
||||||
|
|
||||||
|
final String[] inboxTypeClasses = new String[] {
|
||||||
|
"org.envaya.sms.ui.MessagingSmsInbox",
|
||||||
|
"org.envaya.sms.ui.MessagingMmsInbox",
|
||||||
|
"org.envaya.sms.ui.MessagingSentSms",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] inboxTypeNames = new String[] {
|
||||||
|
"SMS Inbox",
|
||||||
|
"MMS Inbox",
|
||||||
|
"Sent SMS"
|
||||||
|
};
|
||||||
|
|
||||||
// undocumented API; see
|
Spinner spinner = (Spinner) findViewById(R.id.inbox_selector);
|
||||||
// core/java/android/provider/Telephony.java
|
|
||||||
|
|
||||||
Uri inboxUri = Uri.parse("content://sms/inbox");
|
ArrayAdapter<String> inboxTypeAdapter = new ArrayAdapter<String>(this,
|
||||||
|
android.R.layout.simple_spinner_item, inboxTypeNames);
|
||||||
|
inboxTypeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(inboxTypeAdapter);
|
||||||
|
|
||||||
cur = getContentResolver().query(inboxUri,
|
final String className = this.getClass().getCanonicalName();
|
||||||
new String[] { "_id", "address", "body", "date" }, null, null,
|
int classIndex = Arrays.asList(inboxTypeClasses).indexOf(className);
|
||||||
"_id desc limit 50");
|
if (classIndex != -1)
|
||||||
|
{
|
||||||
|
spinner.setSelection(classIndex);
|
||||||
|
}
|
||||||
|
|
||||||
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
|
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
R.layout.inbox_item,
|
public void onItemSelected(AdapterView<?> parent,
|
||||||
cur,
|
View view, int pos, long id) {
|
||||||
new String[] {"address","body"},
|
String cls = inboxTypeClasses[pos];
|
||||||
new int[] {R.id.inbox_address, R.id.inbox_body});
|
|
||||||
|
if (!className.equals(cls))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
finish();
|
||||||
|
startActivity(new Intent(app, Class.forName(cls)));
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex)
|
||||||
|
{
|
||||||
|
app.logError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNothingSelected(AdapterView parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setListAdapter(adapter);
|
|
||||||
|
|
||||||
|
initListAdapter();
|
||||||
|
|
||||||
ListView listView = getListView();
|
ListView listView = getListView();
|
||||||
|
|
||||||
listView.setOnItemClickListener(new OnItemClickListener() {
|
listView.setOnItemClickListener(new OnItemClickListener() {
|
||||||
@ -63,9 +97,9 @@ public class MessagingInbox extends ListActivity {
|
|||||||
{
|
{
|
||||||
final IncomingMessage message = getMessageAtPosition(position);
|
final IncomingMessage message = getMessageAtPosition(position);
|
||||||
|
|
||||||
final CharSequence[] options = {"Forward", "Cancel"};
|
final CharSequence[] options = {"Forward to server", "Cancel"};
|
||||||
|
|
||||||
new AlertDialog.Builder(MessagingInbox.this)
|
new AlertDialog.Builder(MessagingForwarder.this)
|
||||||
.setTitle(message.getDescription())
|
.setTitle(message.getDescription())
|
||||||
.setItems(options, new OnClickListener() {
|
.setItems(options, new OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which)
|
public void onClick(DialogInterface dialog, int which)
|
||||||
@ -73,44 +107,29 @@ public class MessagingInbox extends ListActivity {
|
|||||||
if (which == 0)
|
if (which == 0)
|
||||||
{
|
{
|
||||||
app.inbox.forwardMessage(message);
|
app.inbox.forwardMessage(message);
|
||||||
showToast("Forwarding " + message.getDescription());
|
showToast("Forwarding " + message.getDescription() + " to server");
|
||||||
}
|
}
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showToast(String text)
|
public void showToast(String text)
|
||||||
{
|
{
|
||||||
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingMessage getMessageAtPosition(int position)
|
|
||||||
{
|
|
||||||
int addressIndex = cur.getColumnIndex("address");
|
|
||||||
int bodyIndex = cur.getColumnIndex("body");
|
|
||||||
int dateIndex = cur.getColumnIndex("date");
|
|
||||||
|
|
||||||
cur.moveToPosition(position);
|
|
||||||
|
|
||||||
String address = cur.getString(addressIndex);
|
|
||||||
String body = cur.getString(bodyIndex);
|
|
||||||
long date = cur.getLong(dateIndex);
|
|
||||||
|
|
||||||
return new IncomingSms(app, address, body, date);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void forwardAllClicked() {
|
public void forwardAllClicked() {
|
||||||
final int count = cur.getCount();
|
final int count = getMessageCount();
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
app.inbox.forwardMessage(getMessageAtPosition(i));
|
app.inbox.forwardMessage(getMessageAtPosition(i));
|
||||||
}
|
}
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -137,9 +156,9 @@ public class MessagingInbox extends ListActivity {
|
|||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
MenuItem forwardItem = menu.findItem(R.id.forward_all);
|
MenuItem forwardItem = menu.findItem(R.id.forward_all);
|
||||||
|
|
||||||
int numMessages = cur.getCount();
|
int numMessages = getMessageCount();
|
||||||
forwardItem.setEnabled(numMessages > 0);
|
forwardItem.setEnabled(numMessages > 0);
|
||||||
forwardItem.setTitle("Forward All (" + numMessages + ")");
|
forwardItem.setTitle("Forward all to server (" + numMessages + ")");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
67
src/org/envaya/sms/ui/MessagingMmsInbox.java
Normal file
67
src/org/envaya/sms/ui/MessagingMmsInbox.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package org.envaya.sms.ui;
|
||||||
|
|
||||||
|
import org.envaya.sms.IncomingMms;
|
||||||
|
import org.envaya.sms.IncomingMessage;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import org.envaya.sms.R;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MessagingMmsInbox extends MessagingForwarder
|
||||||
|
{
|
||||||
|
private List<IncomingMms> messages;
|
||||||
|
|
||||||
|
public IncomingMessage getMessageAtPosition(int position)
|
||||||
|
{
|
||||||
|
return messages.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMessageCount()
|
||||||
|
{
|
||||||
|
return messages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initListAdapter() {
|
||||||
|
// undocumented API; see
|
||||||
|
// core/java/android/provider/Telephony.java
|
||||||
|
|
||||||
|
messages = app.getMessagingUtils().getMessagesInMmsInbox();
|
||||||
|
|
||||||
|
final LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
final DateFormat dateFormat = new SimpleDateFormat("dd MMM HH:mm:ss");
|
||||||
|
|
||||||
|
ArrayAdapter<IncomingMms> arrayAdapter = new ArrayAdapter<IncomingMms>(this,
|
||||||
|
R.layout.inbox_item,
|
||||||
|
messages.toArray(new IncomingMms[]{})) {
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
View v = convertView;
|
||||||
|
if (v == null) {
|
||||||
|
v = inflater.inflate(R.layout.inbox_item, null);
|
||||||
|
}
|
||||||
|
IncomingMms mms = messages.get(position);
|
||||||
|
if (mms == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView addrText = (TextView) v.findViewById(R.id.inbox_address);
|
||||||
|
TextView bodyText = (TextView) v.findViewById(R.id.inbox_body);
|
||||||
|
|
||||||
|
addrText.setText(mms.getFrom() + " (" + dateFormat.format(new Date(mms.getTimestamp())) + ")");
|
||||||
|
bodyText.setText(mms.getMessageBody());
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setListAdapter(arrayAdapter);
|
||||||
|
}
|
||||||
|
}
|
79
src/org/envaya/sms/ui/MessagingSentSms.java
Normal file
79
src/org/envaya/sms/ui/MessagingSentSms.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package org.envaya.sms.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import org.envaya.sms.IncomingMessage;
|
||||||
|
import org.envaya.sms.IncomingSms;
|
||||||
|
import org.envaya.sms.R;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class MessagingSentSms extends MessagingForwarder {
|
||||||
|
|
||||||
|
private Cursor cur;
|
||||||
|
|
||||||
|
public IncomingMessage getMessageAtPosition(int position)
|
||||||
|
{
|
||||||
|
int addressIndex = cur.getColumnIndex("address");
|
||||||
|
int bodyIndex = cur.getColumnIndex("body");
|
||||||
|
int dateIndex = cur.getColumnIndex("date");
|
||||||
|
|
||||||
|
cur.moveToPosition(position);
|
||||||
|
|
||||||
|
IncomingSms sms = new IncomingSms(app);
|
||||||
|
|
||||||
|
sms.setDirection(IncomingMessage.Direction.Sent);
|
||||||
|
sms.setTo(cur.getString(addressIndex));
|
||||||
|
sms.setTimestamp(cur.getLong(dateIndex));
|
||||||
|
sms.setMessageBody(cur.getString(bodyIndex));
|
||||||
|
|
||||||
|
return sms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMessageCount()
|
||||||
|
{
|
||||||
|
return cur.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initListAdapter() {
|
||||||
|
// undocumented API; see
|
||||||
|
// core/java/android/provider/Telephony.java
|
||||||
|
|
||||||
|
Uri inboxUri = Uri.parse("content://sms/sent");
|
||||||
|
|
||||||
|
cur = getContentResolver().query(inboxUri,
|
||||||
|
new String[]{"_id", "address", "body", "date"}, null, null,
|
||||||
|
"_id desc limit 50");
|
||||||
|
|
||||||
|
final LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
final DateFormat dateFormat = new SimpleDateFormat("dd MMM HH:mm:ss");
|
||||||
|
|
||||||
|
CursorAdapter adapter = new CursorAdapter(this, cur) {
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return inflater.inflate(R.layout.inbox_item, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bindView(View view, Context context, Cursor cursor)
|
||||||
|
{
|
||||||
|
TextView addrText = (TextView) view.findViewById(R.id.inbox_address);
|
||||||
|
TextView bodyText = (TextView) view.findViewById(R.id.inbox_body);
|
||||||
|
|
||||||
|
String address = cursor.getString(1);
|
||||||
|
String body = cursor.getString(2);
|
||||||
|
long date = cursor.getLong(3);
|
||||||
|
|
||||||
|
addrText.setText(address + " (" + dateFormat.format(new Date(date)) + ")");
|
||||||
|
bodyText.setText(body);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setListAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
78
src/org/envaya/sms/ui/MessagingSmsInbox.java
Normal file
78
src/org/envaya/sms/ui/MessagingSmsInbox.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package org.envaya.sms.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import org.envaya.sms.IncomingMessage;
|
||||||
|
import org.envaya.sms.IncomingSms;
|
||||||
|
import org.envaya.sms.R;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class MessagingSmsInbox extends MessagingForwarder {
|
||||||
|
|
||||||
|
private Cursor cur;
|
||||||
|
|
||||||
|
public IncomingMessage getMessageAtPosition(int position)
|
||||||
|
{
|
||||||
|
int addressIndex = cur.getColumnIndex("address");
|
||||||
|
int bodyIndex = cur.getColumnIndex("body");
|
||||||
|
int dateIndex = cur.getColumnIndex("date");
|
||||||
|
|
||||||
|
cur.moveToPosition(position);
|
||||||
|
|
||||||
|
IncomingSms sms = new IncomingSms(app);
|
||||||
|
|
||||||
|
sms.setFrom(cur.getString(addressIndex));
|
||||||
|
sms.setTimestamp(cur.getLong(dateIndex));
|
||||||
|
sms.setMessageBody(cur.getString(bodyIndex));
|
||||||
|
|
||||||
|
return sms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMessageCount()
|
||||||
|
{
|
||||||
|
return cur.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initListAdapter() {
|
||||||
|
// undocumented API; see
|
||||||
|
// core/java/android/provider/Telephony.java
|
||||||
|
|
||||||
|
Uri inboxUri = Uri.parse("content://sms/inbox");
|
||||||
|
|
||||||
|
cur = getContentResolver().query(inboxUri,
|
||||||
|
new String[]{"_id", "address", "body", "date"}, null, null,
|
||||||
|
"_id desc limit 50");
|
||||||
|
|
||||||
|
final LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
final DateFormat dateFormat = new SimpleDateFormat("dd MMM HH:mm:ss");
|
||||||
|
|
||||||
|
CursorAdapter adapter = new CursorAdapter(this, cur) {
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return inflater.inflate(R.layout.inbox_item, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bindView(View view, Context context, Cursor cursor)
|
||||||
|
{
|
||||||
|
TextView addrText = (TextView) view.findViewById(R.id.inbox_address);
|
||||||
|
TextView bodyText = (TextView) view.findViewById(R.id.inbox_body);
|
||||||
|
|
||||||
|
String address = cursor.getString(1);
|
||||||
|
String body = cursor.getString(2);
|
||||||
|
long date = cursor.getLong(3);
|
||||||
|
|
||||||
|
addrText.setText(address + " (" + dateFormat.format(new Date(date)) + ")");
|
||||||
|
bodyText.setText(body);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setListAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
@ -130,7 +130,7 @@ public class PendingMessages extends ListActivity {
|
|||||||
|
|
||||||
displayedMessages = messages;
|
displayedMessages = messages;
|
||||||
|
|
||||||
this.setTitle("EnvayaSMS : Pending Messages ("+messages.size()+")");
|
this.setTitle(getText(R.string.pending_messages_title) + " ("+messages.size()+")");
|
||||||
|
|
||||||
final LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
final LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
final DateFormat longFormat = new SimpleDateFormat("dd MMM hh:mm:ss");
|
final DateFormat longFormat = new SimpleDateFormat("dd MMM hh:mm:ss");
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package org.envaya.sms.ui;
|
package org.envaya.sms.ui;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.*;
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.preference.PreferenceScreen;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.provider.Settings.SettingNotFoundException;
|
import android.provider.Settings.SettingNotFoundException;
|
||||||
|
import android.text.method.PasswordTransformationMethod;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import org.envaya.sms.App;
|
import org.envaya.sms.App;
|
||||||
import org.envaya.sms.R;
|
import org.envaya.sms.R;
|
||||||
@ -18,6 +19,14 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
|||||||
|
|
||||||
private App app;
|
private App app;
|
||||||
|
|
||||||
|
private BroadcastReceiver installReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
PreferenceScreen screen = getPreferenceScreen();
|
||||||
|
updatePrefSummary(screen.findPreference("send_limit"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -32,8 +41,19 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
|||||||
{
|
{
|
||||||
updatePrefSummary(screen.getPreference(i));
|
updatePrefSummary(screen.getPreference(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IntentFilter installReceiverFilter = new IntentFilter();
|
||||||
|
installReceiverFilter.addAction(App.EXPANSION_PACKS_CHANGED_INTENT);
|
||||||
|
registerReceiver(installReceiver, installReceiverFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy()
|
||||||
|
{
|
||||||
|
unregisterReceiver(installReceiver);
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume(){
|
protected void onResume(){
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@ -50,11 +70,21 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
|||||||
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
|
||||||
|
|
||||||
if (key.equals("outgoing_interval"))
|
if (key.equals("outgoing_interval"))
|
||||||
{
|
{
|
||||||
app.setOutgoingMessageAlarm();
|
app.setOutgoingMessageAlarm();
|
||||||
}
|
}
|
||||||
|
else if (key.startsWith("amqp_"))
|
||||||
|
{
|
||||||
|
if (app.isAmqpEnabled())
|
||||||
|
{
|
||||||
|
app.getAmqpConsumer().startAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.getAmqpConsumer().stopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (key.equals("wifi_sleep_policy"))
|
else if (key.equals("wifi_sleep_policy"))
|
||||||
{
|
{
|
||||||
int value;
|
int value;
|
||||||
@ -107,15 +137,21 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
|||||||
}
|
}
|
||||||
else if (key.equals("enabled"))
|
else if (key.equals("enabled"))
|
||||||
{
|
{
|
||||||
app.log(app.isEnabled() ? "SMS Gateway started." : "SMS Gateway stopped.");
|
app.log(app.isEnabled() ? getText(R.string.started) : getText(R.string.stopped));
|
||||||
app.enabledChanged();
|
app.enabledChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendBroadcast(new Intent(App.SETTINGS_CHANGED_INTENT));
|
||||||
updatePrefSummary(findPreference(key));
|
updatePrefSummary(findPreference(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePrefSummary(Preference p)
|
private void updatePrefSummary(Preference p)
|
||||||
{
|
{
|
||||||
|
if (p == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String key = p.getKey();
|
String key = p.getKey();
|
||||||
|
|
||||||
if ("wifi_sleep_policy".equals(key))
|
if ("wifi_sleep_policy".equals(key))
|
||||||
@ -144,11 +180,32 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
|||||||
p.setSummary("Wi-Fi will stay connected when the phone sleeps");
|
p.setSummary("Wi-Fi will stay connected when the phone sleeps");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ("send_limit".equals(key))
|
||||||
|
{
|
||||||
|
int limit = app.getOutgoingMessageLimit();
|
||||||
|
String limitStr = "Send up to " + limit + " SMS per hour.";
|
||||||
|
|
||||||
|
if (limit < 300)
|
||||||
|
{
|
||||||
|
limitStr += "\nClick to increase limit...";
|
||||||
|
}
|
||||||
|
|
||||||
|
p.setSummary(limitStr);
|
||||||
|
}
|
||||||
else if ("help".equals(key))
|
else if ("help".equals(key))
|
||||||
{
|
{
|
||||||
p.setSummary(app.getPackageInfo().versionName);
|
p.setSummary(app.getPackageInfo().versionName);
|
||||||
}
|
}
|
||||||
|
else if (p instanceof PreferenceCategory)
|
||||||
|
{
|
||||||
|
PreferenceCategory category = (PreferenceCategory)p;
|
||||||
|
int numPreferences = category.getPreferenceCount();
|
||||||
|
for (int i = 0; i < numPreferences; i++)
|
||||||
|
{
|
||||||
|
updatePrefSummary(category.getPreference(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (p instanceof ListPreference) {
|
else if (p instanceof ListPreference) {
|
||||||
p.setSummary(((ListPreference)p).getEntry());
|
p.setSummary(((ListPreference)p).getEntry());
|
||||||
}
|
}
|
||||||
@ -160,7 +217,7 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
|
|||||||
{
|
{
|
||||||
p.setSummary("(not set)");
|
p.setSummary("(not set)");
|
||||||
}
|
}
|
||||||
else if (p.getKey().equals("password"))
|
else if (textPref.getEditText().getTransformationMethod() instanceof PasswordTransformationMethod)
|
||||||
{
|
{
|
||||||
p.setSummary("********");
|
p.setSummary("********");
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import android.os.Bundle;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@ -20,6 +21,8 @@ public class TestPhoneNumbers extends ListActivity {
|
|||||||
|
|
||||||
private App app;
|
private App app;
|
||||||
|
|
||||||
|
private CheckBox autoAddOutgoing;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
@ -27,6 +30,9 @@ public class TestPhoneNumbers extends ListActivity {
|
|||||||
|
|
||||||
app = (App)getApplication();
|
app = (App)getApplication();
|
||||||
|
|
||||||
|
autoAddOutgoing = (CheckBox)findViewById(R.id.auto_add_outgoing);
|
||||||
|
autoAddOutgoing.setChecked(app.autoAddTestNumber());
|
||||||
|
|
||||||
ListView lv = getListView();
|
ListView lv = getListView();
|
||||||
lv.setOnItemClickListener(new OnItemClickListener() {
|
lv.setOnItemClickListener(new OnItemClickListener() {
|
||||||
public void onItemClick(AdapterView<?> parent, View view,
|
public void onItemClick(AdapterView<?> parent, View view,
|
||||||
@ -63,6 +69,13 @@ public class TestPhoneNumbers extends ListActivity {
|
|||||||
updateTestPhoneNumbers();
|
updateTestPhoneNumbers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void autoAddOutgoingClicked(View v)
|
||||||
|
{
|
||||||
|
boolean checked = autoAddOutgoing.isChecked();
|
||||||
|
app.log("Test Mode: automatically add outgoing message recipients set to " + (checked ? "YES" : "NO"));
|
||||||
|
app.saveBooleanSetting("auto_add_test_number", checked);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateTestPhoneNumbers()
|
public void updateTestPhoneNumbers()
|
||||||
{
|
{
|
||||||
String[] senders = app.getTestPhoneNumbers().toArray(new String[]{});
|
String[] senders = app.getTestPhoneNumbers().toArray(new String[]{});
|
||||||
|
Loading…
Reference in New Issue
Block a user