diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 1bec35e..ce889bd 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -3,6 +3,115 @@
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..40ed937
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 15a15b2..ada92a5 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -1,4 +1,6 @@
-
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 37a7509..a7f38b3 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 0f88a04..3b31867 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,6 +16,14 @@ android {
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ javaCompileOptions {
+ annotationProcessorOptions {
+ arguments = [
+ "room.schemaLocation" : "$projectDir/schemas".toString(),
+ "room.incremental" : "true",
+ "room.expandProjection": "true"]
+ }
+ }
}
buildTypes {
release {
@@ -31,17 +39,24 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
+ implementation 'com.google.android.material:material:1.0.0'
+ implementation 'androidx.preference:preference:1.1.0-rc01'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- implementation 'khttp:khttp:0.1.0'
+ implementation 'org.greenrobot:eventbus:3.1.1'
+
+// implementation 'khttp:khttp:0.1.0'
+ implementation 'io.karn:khttp-android:0.1.2'
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
- //implementation "androidx.room:room-ktx:$room_version"
+ implementation "androidx.room:room-ktx:$room_version"
// optional - RxJava support for Room
///implementation "androidx.room:room-rxjava2:$room_version"
diff --git a/app/schemas/hu.yvan.smsproxy.db.AppDatabase/1.json b/app/schemas/hu.yvan.smsproxy.db.AppDatabase/1.json
new file mode 100644
index 0000000..388e7db
--- /dev/null
+++ b/app/schemas/hu.yvan.smsproxy.db.AppDatabase/1.json
@@ -0,0 +1,58 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 1,
+ "identityHash": "3c41cc5248bccd081b1049c7971f1456",
+ "entities": [
+ {
+ "tableName": "pending_sms",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `contact` TEXT NOT NULL, `message` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `direction` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "contact",
+ "columnName": "contact",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "message",
+ "columnName": "message",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "direction",
+ "columnName": "direction",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3c41cc5248bccd081b1049c7971f1456')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/hu.yvan.smsproxy.db.AppDatabase/2.json b/app/schemas/hu.yvan.smsproxy.db.AppDatabase/2.json
new file mode 100644
index 0000000..007f3ef
--- /dev/null
+++ b/app/schemas/hu.yvan.smsproxy.db.AppDatabase/2.json
@@ -0,0 +1,58 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "3c41cc5248bccd081b1049c7971f1456",
+ "entities": [
+ {
+ "tableName": "pending_sms",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `contact` TEXT NOT NULL, `message` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `direction` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "contact",
+ "columnName": "contact",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "message",
+ "columnName": "message",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "direction",
+ "columnName": "direction",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3c41cc5248bccd081b1049c7971f1456')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c8d3d2e..7686b8b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,7 +3,6 @@
package="hu.yvan.smsproxy">
-
@@ -14,6 +13,26 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
+
+
+
+
+
+
+
+
+
+
+
@@ -24,7 +43,7 @@
android:enabled="true"
android:exported="true">
-
+
diff --git a/app/src/main/java/hu/yvan/smsproxy/SmsFragment.kt b/app/src/main/java/hu/yvan/smsproxy/SmsFragment.kt
new file mode 100644
index 0000000..2fad5ac
--- /dev/null
+++ b/app/src/main/java/hu/yvan/smsproxy/SmsFragment.kt
@@ -0,0 +1,107 @@
+package hu.yvan.smsproxy
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.room.Room
+import hu.yvan.smsproxy.db.AppDatabase
+import hu.yvan.smsproxy.db.SMS
+
+/**
+ * A fragment representing a list of Items.
+ * Activities containing this fragment MUST implement the
+ * [SmsFragment.OnListFragmentInteractionListener] interface.
+ */
+class SmsFragment : Fragment() {
+
+ // TODO: Customize parameters
+ private var columnCount = 1
+
+ private var listener: OnListFragmentInteractionListener? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ arguments?.let {
+ columnCount = it.getInt(ARG_COLUMN_COUNT)
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val view = inflater.inflate(R.layout.fragment_sms_list, container, false)
+
+ // Set the adapter
+ if (view is RecyclerView) {
+ with(view) {
+ layoutManager = when {
+ columnCount <= 1 -> LinearLayoutManager(context)
+ else -> GridLayoutManager(context, columnCount)
+ }
+ val db = Room.databaseBuilder(context, AppDatabase::class.java, "sms_storage")
+ .allowMainThreadQueries()
+ .fallbackToDestructiveMigration()
+ .build()
+ val smses = db.smsDao().getAll().toMutableList()
+ Log.d(SmsFragment::class.java.simpleName, db.smsDao().getAll().count().toString())
+
+ adapter = SmsRecyclerViewAdapter(smses, listener)
+ }
+ }
+ return view
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ if (context is OnListFragmentInteractionListener) {
+ listener = context
+ } else {
+ throw RuntimeException(context.toString() + " must implement OnListFragmentInteractionListener")
+ }
+ }
+
+ override fun onDetach() {
+ super.onDetach()
+ listener = null
+ }
+
+ /**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ *
+ *
+ * See the Android Training lesson
+ * [Communicating with Other Fragments](http://developer.android.com/training/basics/fragments/communicating.html)
+ * for more information.
+ */
+ interface OnListFragmentInteractionListener {
+ // TODO: Update argument type and name
+ fun onListFragmentInteraction(sms: SMS?, adapter: SmsRecyclerViewAdapter)
+ }
+
+ companion object {
+
+ // TODO: Customize parameter argument names
+ const val ARG_COLUMN_COUNT = "column-count"
+
+ // TODO: Customize parameter initialization
+ @JvmStatic
+ fun newInstance(columnCount: Int) =
+ SmsFragment().apply {
+ arguments = Bundle().apply {
+ putInt(ARG_COLUMN_COUNT, columnCount)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/hu/yvan/smsproxy/SmsPersistedEvent.kt b/app/src/main/java/hu/yvan/smsproxy/SmsPersistedEvent.kt
new file mode 100644
index 0000000..9dc66e6
--- /dev/null
+++ b/app/src/main/java/hu/yvan/smsproxy/SmsPersistedEvent.kt
@@ -0,0 +1,3 @@
+package hu.yvan.smsproxy
+
+class SmsPersistedEvent(public val smsId: Long) {}
\ No newline at end of file
diff --git a/app/src/main/java/hu/yvan/smsproxy/SmsRecyclerViewAdapter.kt b/app/src/main/java/hu/yvan/smsproxy/SmsRecyclerViewAdapter.kt
new file mode 100644
index 0000000..5d9b1a1
--- /dev/null
+++ b/app/src/main/java/hu/yvan/smsproxy/SmsRecyclerViewAdapter.kt
@@ -0,0 +1,67 @@
+package hu.yvan.smsproxy
+
+import androidx.recyclerview.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+
+
+import hu.yvan.smsproxy.SmsFragment.OnListFragmentInteractionListener
+import hu.yvan.smsproxy.db.SMS
+
+import kotlinx.android.synthetic.main.fragment_sms.view.*
+
+/**
+ * [RecyclerView.Adapter] that can display a [SMS] and makes a call to the
+ * specified [OnListFragmentInteractionListener].
+ * TODO: Replace the implementation with code for your data type.
+ */
+class SmsRecyclerViewAdapter(
+ private val mValues: MutableList,
+ private val mListener: OnListFragmentInteractionListener?
+) : RecyclerView.Adapter() {
+
+ private val mOnClickListener: View.OnClickListener
+
+ init {
+ mOnClickListener = View.OnClickListener { v ->
+ val item = v.tag as SMS
+ // Notify the active callbacks interface (the activity, if the fragment is attached to
+ // one) that an item has been selected.
+ mListener?.onListFragmentInteraction(item, this)
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.fragment_sms, parent, false)
+ return ViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = mValues[position]
+ holder.mIdView.text = item.id.toString()
+ holder.mContentView.text = item.message
+
+ with(holder.mView) {
+ tag = item
+ setOnClickListener(mOnClickListener)
+ }
+ }
+
+ override fun getItemCount(): Int = mValues.size
+
+ inner class ViewHolder(val mView: View) : RecyclerView.ViewHolder(mView) {
+ val mIdView: TextView = mView.item_number
+ val mContentView: TextView = mView.content
+
+ override fun toString(): String {
+ return super.toString() + " '" + mContentView.text + "'"
+ }
+ }
+
+ fun removeItem(sms: SMS) {
+ mValues.remove(sms)
+ }
+}
diff --git a/app/src/main/java/hu/yvan/smsproxy/activity/MainActivity.kt b/app/src/main/java/hu/yvan/smsproxy/activity/MainActivity.kt
new file mode 100644
index 0000000..607bff2
--- /dev/null
+++ b/app/src/main/java/hu/yvan/smsproxy/activity/MainActivity.kt
@@ -0,0 +1,81 @@
+package hu.yvan.smsproxy.activity
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.preference.PreferenceManager
+import androidx.room.Room
+import hu.yvan.smsproxy.SmsRecyclerViewAdapter
+import hu.yvan.smsproxy.R
+import hu.yvan.smsproxy.SmsFragment
+import hu.yvan.smsproxy.SmsPersistedEvent
+import hu.yvan.smsproxy.db.AppDatabase
+import hu.yvan.smsproxy.db.SMS
+import hu.yvan.smsproxy.service.SMSManager
+import kotlinx.android.synthetic.main.activity_main.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class MainActivity : AppCompatActivity(), SmsFragment.OnListFragmentInteractionListener {
+
+ lateinit var savedAdapter: SmsRecyclerViewAdapter
+
+ override fun onListFragmentInteraction(sms: SMS?, adapter: SmsRecyclerViewAdapter) {
+ savedAdapter = adapter
+ Log.d(TAG, "interact: $sms")
+ if (sms != null) {
+ SMSManager.startActionSMSResend(this, sms)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ setSupportActionBar(toolbar)
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this,
+ arrayOf(Manifest.permission.RECEIVE_SMS),
+ MY_PERMISSIONS_REQUEST_READ_CONTACTS)
+ }
+ fab.setOnClickListener { view ->
+ startActivity(Intent(this, SettingsActivity::class.java))
+ }
+
+ val prefs = PreferenceManager.getDefaultSharedPreferences(this)
+ Log.d(TAG, "preferences: ${prefs.all}")
+ }
+
+ override fun onStart() {
+ super.onStart()
+ EventBus.getDefault().register(this)
+ }
+
+ override fun onStop() {
+ super.onStop()
+ EventBus.getDefault().unregister(this)
+ }
+
+ //Subscribe, run it on UI
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEvent(event: SmsPersistedEvent) {
+ val db = Room.databaseBuilder(this, AppDatabase::class.java, "sms_storage")
+ .allowMainThreadQueries()
+ .build()
+ .smsDao()
+ val sms = db.findById(event.smsId)
+ savedAdapter.removeItem(sms)
+ savedAdapter.notifyDataSetChanged()
+ db.delete(sms)
+ }
+
+ companion object {
+ private val TAG = MainActivity::class.java.simpleName
+ const val MY_PERMISSIONS_REQUEST_READ_CONTACTS = 1
+ }
+}
diff --git a/app/src/main/java/hu/yvan/smsproxy/activity/SettingsActivity.kt b/app/src/main/java/hu/yvan/smsproxy/activity/SettingsActivity.kt
new file mode 100644
index 0000000..3a2ccdc
--- /dev/null
+++ b/app/src/main/java/hu/yvan/smsproxy/activity/SettingsActivity.kt
@@ -0,0 +1,25 @@
+package hu.yvan.smsproxy.activity
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.preference.PreferenceFragmentCompat
+import hu.yvan.smsproxy.R
+
+class SettingsActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.settings_activity)
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.settings, SettingsFragment())
+ .commit()
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
+ class SettingsFragment : PreferenceFragmentCompat() {
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.root_preferences, rootKey)
+ }
+ }
+}
diff --git a/app/src/main/java/hu/yvan/smsproxy/db/AppDatabase.kt b/app/src/main/java/hu/yvan/smsproxy/db/AppDatabase.kt
index cd74e41..8089879 100644
--- a/app/src/main/java/hu/yvan/smsproxy/db/AppDatabase.kt
+++ b/app/src/main/java/hu/yvan/smsproxy/db/AppDatabase.kt
@@ -3,7 +3,7 @@ package hu.yvan.smsproxy.db
import androidx.room.Database
import androidx.room.RoomDatabase
-@Database(entities = arrayOf(SMS::class), version = 1)
+@Database(entities = arrayOf(SMS::class), version = 2)
abstract class AppDatabase : RoomDatabase() {
abstract fun smsDao(): SMSDao
}
diff --git a/app/src/main/java/hu/yvan/smsproxy/db/SMS.kt b/app/src/main/java/hu/yvan/smsproxy/db/SMS.kt
index 95280d2..b480f4e 100644
--- a/app/src/main/java/hu/yvan/smsproxy/db/SMS.kt
+++ b/app/src/main/java/hu/yvan/smsproxy/db/SMS.kt
@@ -5,15 +5,17 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "pending_sms")
-data class SMS (
- @ColumnInfo(name = "contact") val contact: String?,
- @ColumnInfo(name = "message") val message: String?,
- @ColumnInfo(name = "timestamp") val timestamp: String?,
- @ColumnInfo(name = "direction") val direction: Boolean?
+data class SMS(
+ @ColumnInfo(name = "contact") val contact: String,
+ @ColumnInfo(name = "message") val message: String,
+ @ColumnInfo(name = "timestamp") val timestamp: Long,
+ @ColumnInfo(name = "direction") val direction: Boolean
) {
- @PrimaryKey(autoGenerate = true) val id: Int? = null
+ @PrimaryKey(autoGenerate = true) var id: Long = 0
+
companion object {
const val DIRECTION_SENT = false
const val DIRECTION_RECEIVED = true
+
}
}
diff --git a/app/src/main/java/hu/yvan/smsproxy/db/SMSDao.kt b/app/src/main/java/hu/yvan/smsproxy/db/SMSDao.kt
index cda8177..3c42c07 100644
--- a/app/src/main/java/hu/yvan/smsproxy/db/SMSDao.kt
+++ b/app/src/main/java/hu/yvan/smsproxy/db/SMSDao.kt
@@ -13,15 +13,18 @@ interface SMSDao {
@Query("SELECT * FROM pending_sms WHERE id IN (:smsIds)")
fun loadAllByIds(smsIds: IntArray): List
-// @Query("SELECT * FROM sms WHERE first_name LIKE :first AND last_name LIKE :last LIMIT 1")
-// fun findByName(first: String, last: String): SMS
-
@Query("SELECT * FROM pending_sms WHERE id = :id")
- fun findById(id: Int): SMS
+ fun findById(id: Long): SMS
+
+ @Insert
+ fun insert(sms: SMS): Long
@Insert
fun insertAll(vararg sms: SMS)
@Delete
fun delete(sms: SMS)
+
+ @Query("DELETE FROM pending_sms WHERE id = :id")
+ fun delete(id: Long)
}
diff --git a/app/src/main/java/hu/yvan/smsproxy/service/SMSManager.kt b/app/src/main/java/hu/yvan/smsproxy/service/SMSManager.kt
index bf07c7f..a188acf 100644
--- a/app/src/main/java/hu/yvan/smsproxy/service/SMSManager.kt
+++ b/app/src/main/java/hu/yvan/smsproxy/service/SMSManager.kt
@@ -5,18 +5,23 @@ import android.content.Intent
import android.content.Context
import android.telephony.SmsMessage
import android.util.Log
+import androidx.preference.PreferenceManager
import androidx.room.Room
+import hu.yvan.smsproxy.SmsPersistedEvent
import hu.yvan.smsproxy.db.AppDatabase
import hu.yvan.smsproxy.db.SMS
+import org.greenrobot.eventbus.EventBus
import java.lang.Exception
private const val SMS_RECEIVED_ACTION = "hu.yvan.smsproxy.service.action.SMS_RECEIVED"
+private const val SMS_RESEND_ACTION = "hu.yvan.smsproxy.service.action.SMS_RESEND"
+private const val EXTRA_PARAM_ID = "hu.yvan.smsproxy.service.extra.ID"
private const val EXTRA_PARAM_CONTACT = "hu.yvan.smsproxy.service.extra.CONTACT"
private const val EXTRA_PARAM_MESSAGE = "hu.yvan.smsproxy.service.extra.MESSAGE"
private const val EXTRA_PARAM_TIMESTAMP = "hu.yvan.smsproxy.service.extra.TIMESTAMP"
-private const val SMS_ENPOINT = "https://sms-store.yvan.hu/store/sent/{{uniquehash}}"
+private const val API_URI_BASE = "%s://%s/store/received/%s"
/**
* An [IntentService] subclass for handling asynchronous task requests in
@@ -27,42 +32,68 @@ class SMSManager : IntentService("SMSManager") {
override fun onHandleIntent(intent: Intent?) {
when (intent?.action) {
SMS_RECEIVED_ACTION -> {
- val contact = intent.getStringExtra(EXTRA_PARAM_CONTACT)
- val message = intent.getStringExtra(EXTRA_PARAM_MESSAGE)
- val timestamp = intent.getStringExtra(EXTRA_PARAM_TIMESTAMP)
+ val contact = intent.getStringExtra(EXTRA_PARAM_CONTACT) as String
+ val message = intent.getStringExtra(EXTRA_PARAM_MESSAGE) as String
+ val timestamp = intent.getLongExtra(EXTRA_PARAM_TIMESTAMP, 0)
handleActionSMSReceived(contact, message, timestamp)
}
+ SMS_RESEND_ACTION -> {
+ val id = intent.getLongExtra(EXTRA_PARAM_ID, 0)
+ val contact = intent.getStringExtra(EXTRA_PARAM_CONTACT) as String
+ val message = intent.getStringExtra(EXTRA_PARAM_MESSAGE) as String
+ val timestamp = intent.getLongExtra(EXTRA_PARAM_TIMESTAMP, 0)
+ handleActionSMSReceived(id, contact, message, timestamp)
+ }
}
}
- private fun handleActionSMSReceived(contact: String?, message: String?, timestamp: String?) {
+ private fun handleActionSMSReceived(id: Long, contact: String, message: String, timestamp: Long) {
+ if (sendHttpRequest(id, contact, message, timestamp)) {
+ Log.d(TAG, "messageid: $id")
+ EventBus.getDefault().post(SmsPersistedEvent(id))
+ }
+ }
+
+ private fun handleActionSMSReceived(contact: String, message: String, timestamp: Long) {
val sms = SMS(contact, message, timestamp, SMS.DIRECTION_RECEIVED)
val db = Room.databaseBuilder(this, AppDatabase::class.java, "sms_storage").build()
- db.smsDao().insertAll(sms)
+ val smsId = db.smsDao().insert(sms)
+ if (sendHttpRequest(smsId, sms.contact, sms.message, sms.timestamp)) {
+ db.smsDao().delete(smsId)
+ }
+ }
+
+ private fun sendHttpRequest(id: Long, contact: String, message: String, timestamp: Long): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(this)
try {
+ Log.d(TAG, "sending POST request")
val response = khttp.post(
- SMS_ENPOINT,
+ API_URI_BASE.format(
+ if (prefs.getBoolean("api_secure", true)) "https" else "http",
+ prefs.getString("api_url",""),
+ prefs.getString("api_key","")
+ ),
headers = mapOf("content-type" to "application/json"),
json = mapOf(
- "contactName" to sms.contact,
- "contactNumber" to sms.contact,
- "when" to sms.timestamp,
- "text" to sms.message
+ "id" to id,
+ "contactName" to contact,
+ "contactNumber" to contact,
+ "when" to "@" + timestamp.div(1000),
+ "text" to message
)
)
- if (response.statusCode == 200) {
- db.smsDao().delete(sms)
- }
+ return response.statusCode == 200
} catch (e: Exception) {
Log.e(TAG, "khttp: " + e.localizedMessage)
}
+ return false
}
companion object {
private val TAG = SMSManager::class.java.simpleName
/**
- * Starts this service to perform action Foo with the given parameters. If
+ * Starts this service to perform action SMSRecieved with the given parameters. If
* the service is already performing a task this action will be queued.
*
* @see IntentService
@@ -77,5 +108,23 @@ class SMSManager : IntentService("SMSManager") {
}
context.startService(intent)
}
+
+ /**
+ * Starts this service to perform action SMSResend with the given parameters. If
+ * the service is already performing a task this action will be queued.
+ *
+ * @see IntentService
+ */
+ @JvmStatic
+ fun startActionSMSResend(context: Context, sms: SMS) {
+ val intent = Intent(context, SMSManager::class.java).apply {
+ action = SMS_RESEND_ACTION
+ putExtra(EXTRA_PARAM_ID, sms.id)
+ putExtra(EXTRA_PARAM_CONTACT, sms.contact)
+ putExtra(EXTRA_PARAM_MESSAGE, sms.message)
+ putExtra(EXTRA_PARAM_TIMESTAMP, sms.timestamp)
+ }
+ context.startService(intent)
+ }
}
}
diff --git a/app/src/main/java/hu/yvan/smsproxy/service/SMSReceiver.kt b/app/src/main/java/hu/yvan/smsproxy/service/SMSReceiver.kt
index 0fe2e4d..6f34e86 100644
--- a/app/src/main/java/hu/yvan/smsproxy/service/SMSReceiver.kt
+++ b/app/src/main/java/hu/yvan/smsproxy/service/SMSReceiver.kt
@@ -10,11 +10,11 @@ import android.util.Log
class SMSReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- Log.i(TAG, "Broadcast received: " + intent.action)
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION == intent.action) {
+ Log.i(TAG, "Processing incoming SMS")
val intentExtras = intent.extras
val format = intentExtras?.getString("format")
- val pdus: Array? = intentExtras?.get(PDU_TYPE) as Array?
+ @Suppress("UNCHECKED_CAST") val pdus: Array? = intentExtras?.get(PDU_TYPE) as Array?
(0 until pdus!!.size).forEach { i ->
val smsMessage = SmsMessage.createFromPdu(pdus[i] as ByteArray, format)
SMSManager.startActionSMSRecieved(context, smsMessage)
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4a26786
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
new file mode 100644
index 0000000..fd079b9
--- /dev/null
+++ b/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_sms.xml b/app/src/main/res/layout/fragment_sms.xml
new file mode 100644
index 0000000..e7ed6e4
--- /dev/null
+++ b/app/src/main/res/layout/fragment_sms.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_sms_list.xml b/app/src/main/res/layout/fragment_sms_list.xml
new file mode 100644
index 0000000..d00b8dc
--- /dev/null
+++ b/app/src/main/res/layout/fragment_sms_list.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml
new file mode 100644
index 0000000..54ad190
--- /dev/null
+++ b/app/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..266930a
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,9 @@
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..da62b5f
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+ 180dp
+ 16dp
+ 16dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4b3c834..b785c0c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,17 @@
SMS Proxy Application
+ SMS Proxy
+
+ Settings
+ Settings
+
+
+ API
+
+
+ Server address
+ User key
+ Use HTTPS
+ Yes
+ No
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5885930..16dbab3 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -7,5 +7,11 @@
- @color/colorPrimaryDark
- @color/colorAccent
+
+
+
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
new file mode 100644
index 0000000..a5e6367
--- /dev/null
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
index a87f8fa..dc15882 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,14 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.31'
+ ext.kotlin_version = '1.3.41'
repositories {
google()
jcenter()
-
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.2'
+ classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -19,7 +18,7 @@ allprojects {
repositories {
google()
jcenter()
-
+ maven { url 'https://jitpack.io' }
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index be9c322..a726c39 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Aug 16 09:44:48 CEST 2019
+#Wed Aug 21 14:27:08 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip