From 7d60afaac48694adfbac9c12b437ce1c9e997ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danyi=20D=C3=A1vid?= Date: Tue, 27 Aug 2019 08:57:45 +0200 Subject: [PATCH] * working first release --- .idea/codeStyles/Project.xml | 109 ++++++++++++++++++ .idea/compiler.xml | 15 +++ .idea/encodings.xml | 4 +- .idea/misc.xml | 2 +- .idea/vcs.xml | 6 + app/build.gradle | 19 ++- .../hu.yvan.smsproxy.db.AppDatabase/1.json | 58 ++++++++++ .../hu.yvan.smsproxy.db.AppDatabase/2.json | 58 ++++++++++ app/src/main/AndroidManifest.xml | 23 +++- .../main/java/hu/yvan/smsproxy/SmsFragment.kt | 107 +++++++++++++++++ .../hu/yvan/smsproxy/SmsPersistedEvent.kt | 3 + .../yvan/smsproxy/SmsRecyclerViewAdapter.kt | 67 +++++++++++ .../hu/yvan/smsproxy/activity/MainActivity.kt | 81 +++++++++++++ .../smsproxy/activity/SettingsActivity.kt | 25 ++++ .../java/hu/yvan/smsproxy/db/AppDatabase.kt | 2 +- app/src/main/java/hu/yvan/smsproxy/db/SMS.kt | 14 ++- .../main/java/hu/yvan/smsproxy/db/SMSDao.kt | 11 +- .../hu/yvan/smsproxy/service/SMSManager.kt | 79 ++++++++++--- .../hu/yvan/smsproxy/service/SMSReceiver.kt | 4 +- app/src/main/res/layout/activity_main.xml | 48 ++++++++ app/src/main/res/layout/content_main.xml | 18 +++ app/src/main/res/layout/fragment_sms.xml | 20 ++++ app/src/main/res/layout/fragment_sms_list.xml | 14 +++ app/src/main/res/layout/settings_activity.xml | 9 ++ app/src/main/res/menu/menu_main.xml | 9 ++ app/src/main/res/values/dimens.xml | 5 + app/src/main/res/values/strings.xml | 14 +++ app/src/main/res/values/styles.xml | 6 + app/src/main/res/xml/root_preferences.xml | 43 +++++++ build.gradle | 7 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 31 files changed, 844 insertions(+), 40 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/vcs.xml create mode 100644 app/schemas/hu.yvan.smsproxy.db.AppDatabase/1.json create mode 100644 app/schemas/hu.yvan.smsproxy.db.AppDatabase/2.json create mode 100644 app/src/main/java/hu/yvan/smsproxy/SmsFragment.kt create mode 100644 app/src/main/java/hu/yvan/smsproxy/SmsPersistedEvent.kt create mode 100644 app/src/main/java/hu/yvan/smsproxy/SmsRecyclerViewAdapter.kt create mode 100644 app/src/main/java/hu/yvan/smsproxy/activity/MainActivity.kt create mode 100644 app/src/main/java/hu/yvan/smsproxy/activity/SettingsActivity.kt create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/content_main.xml create mode 100644 app/src/main/res/layout/fragment_sms.xml create mode 100644 app/src/main/res/layout/fragment_sms_list.xml create mode 100644 app/src/main/res/layout/settings_activity.xml create mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/xml/root_preferences.xml 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 + +