Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
davidstoneham committed Mar 12, 2018
1 parent 7d902e1 commit 098c035
Show file tree
Hide file tree
Showing 42 changed files with 1,373 additions and 1 deletion.
33 changes: 33 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/runConfigurations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
# SMSNotify
Android app to automatically send text messages from any API endpoint
An Android app to automatically send text messages from any API endpoint. The app pols the server(s) every minute for new messages. If there are any messages it will send them one by one and hit the completed API endpoint with the message id once it has been sent successfully.

*Please note this app has been thrown together and is not fully tested. It is designed to work running on a phone that is plugged in 24/7. Android 6+ has some power management features that may cause this app to stop running in the background if the device is not plugged in. The app also has no UI level logging or error messages and should be used as more of a starting point rather than a complete solution*

*Android has an anti-spam feature to protect your phone from sending too many messages too quickly so nasty apps can't rack up excessive carrier charges. You will need to disable this feature if you intend to send many messages quickly. A guide is available at https://www.xda-developers.com/change-sms-limit-android/ on how to do this.*

*Always check your carrier limits and fact sheets so your are not violating your terms of service*

## API
You can configure your own urls for this app to communicate to but you must have the 2 following endpoints at each url.

### Get Pending Messages

`http://yoururl.com/anyOtherSuffix/SMSPending?key={optional}`

The key is optional to check on your server to add some level of security that only you can retrieve the pending sms. This endpoint should return a JSON array of any pending SMS to send. The JSON should be in the following format
```
{
id: integer,
mobile: string,
msg: string
}
```

* id: A unique id for the sms which will be sent back to the server once the message has been sent.
* mobile: The mobile number to send the message to, this doesn't need the country code if sent locally.
* msg: The sms message to be sent.

### Message Sent

`http://yoururl.com/anyOtherSuffix/SMSSent?id={msgId}&key={optional}`

This endpoint doesn't need to return anything and can be used for your server to mark the SMS as sent so you're not sending duplicates. Each time an SMS is sent via the app this URL will be called with the id of the sms that was sent and the optional API Key.

## Using The App
1. Download and install the latest release, or compile it yourself.
2. Enter the endpoints you want the app to check. You must fully qualify these endpoings with http:// or https://. You can enter as many as you like the app will check all the endpoints each time is runs
3. enter an optional API key to be used in the requests to your server. If you leave this blank no API key will be sent

## Going Further
Feel free to download and edit the app as required if you need something more specific or just want to make some changes!
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
28 changes: 28 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 26
defaultConfig {
applicationId "com.smsnotify"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile 'com.android.support:design:26.1.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# 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 *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
1 change: 1 addition & 0 deletions app/release/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.smsnotify","split":"","minSdkVersion":"19"}}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.smsnotify;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();

assertEquals("com.smsnotify", appContext.getPackageName());
}
}
39 changes: 39 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smsnotify">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service android:name=".ServiceTest" />

<receiver android:name=".ReceiverCall">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

<activity
android:name=".MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
163 changes: 163 additions & 0 deletions app/src/main/java/com/intelliguard/smsnotify/MainActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.smsnotify;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.design.widget.TextInputEditText;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

public class MainActivity extends AppCompatActivity {
ArrayList<String> listItems = new ArrayList<String>();
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addListeners();
setupListview();
setupApiKey();
checkPermission();
}

private void checkPermission() {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.SEND_SMS)
!= PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SEND_SMS}, 0);
} else {
startService(new Intent(this, ServiceTest.class));
}
}

private void setupListview() {
//add adapter
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
listItems);
final ListView lstEndpoints = findViewById(R.id.lstEndpoints);
lstEndpoints.setAdapter(adapter);

//add items to list
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.preferences_endpoints), Context.MODE_PRIVATE);
Map<String, ?> prefs = sharedPref.getAll();
for (Map.Entry<String, ?> entry : prefs.entrySet()) {
listItems.add(entry.getValue().toString());
}

//link on long hold
lstEndpoints.setLongClickable(true);
lstEndpoints.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
return listItem_onLongClick(position);
}
});
}

private boolean listItem_onLongClick(int position) {
//remove endpoint from shared pref
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.preferences_endpoints), Context.MODE_PRIVATE);
Map<String, ?> prefs = sharedPref.getAll();
Set keys = prefs.keySet();
String key = keys.toArray()[position].toString();
SharedPreferences.Editor editor = sharedPref.edit();
editor.remove(key);
editor.commit();
//update list
listItems.remove(position);
adapter.notifyDataSetChanged();
return true;
}

private void setupApiKey() {
final Button btnSaveKey = findViewById(R.id.btnSaveKey);
btnSaveKey.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
btnSaveKey_onClick();
}
});

SharedPreferences sharedPref = getApplicationContext().getSharedPreferences(getString(R.string.preferences_key), Context.MODE_PRIVATE);
String apiKey = sharedPref.getString(getString(R.string.preferences_key), "");
final EditText txtKey = findViewById(R.id.txtKey);
txtKey.setText(apiKey);
}

private void addListeners() {
final Button btnAddSource = findViewById(R.id.btnAddSource);
btnAddSource.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
btnAddSource_onClick();
}
});
}

private void btnAddSource_onClick() {
//get endpoint text
final EditText txtEndpoint = findViewById(R.id.txtEndpoint);
String endpoint = txtEndpoint.getText().toString();

//add to shared preferences
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.preferences_endpoints), Context.MODE_PRIVATE);
Map<String, ?> prefs = sharedPref.getAll();
String nextId = "1";
//get next preference key
if (prefs.size() > 0) {
Set keys = prefs.keySet();
int lastId = Integer.parseInt(keys.toArray()[keys.size() - 1].toString());
lastId++;
nextId = Integer.toString(lastId);
}
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(nextId, endpoint);
editor.commit();
//add to list
listItems.add(endpoint);
adapter.notifyDataSetChanged();
}

private void btnSaveKey_onClick() {
//get endpoint text
final EditText txtKey = findViewById(R.id.txtKey);
String endpoint = txtKey.getText().toString();

//add to shared preferences
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.preferences_key), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(getString(R.string.preferences_key), endpoint);
editor.commit();
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startService(new Intent(this, ServiceTest.class));
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
}

}
Loading

0 comments on commit 098c035

Please sign in to comment.