Reorganize the code
Make it more readable Prepare non-SSO ApiProvider
This commit is contained in:
parent
76521cac7f
commit
9db96919bd
|
@ -2,6 +2,9 @@ package org.unifiedpush.distributor.nextpush.account
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.util.Linkify
|
||||
|
@ -16,12 +19,8 @@ import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException
|
|||
import com.nextcloud.android.sso.helper.SingleAccountHelper
|
||||
import com.nextcloud.android.sso.model.SingleSignOnAccount
|
||||
import com.nextcloud.android.sso.ui.UiExceptionManager
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.unifiedpush.distributor.nextpush.R
|
||||
|
||||
private const val TAG = "AccountUtils"
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
|
||||
private const val PREF_NAME = "NextPush"
|
||||
private const val PREF_DEVICE_ID = "deviceId"
|
||||
|
@ -99,7 +98,7 @@ object AccountUtils {
|
|||
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(PREF_DEVICE_ID, deviceId)
|
||||
.commit()
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun getDeviceId(context: Context): String? {
|
||||
|
@ -111,14 +110,14 @@ object AccountUtils {
|
|||
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.remove(PREF_DEVICE_ID)
|
||||
.commit()
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun saveUrl(context: Context, url: String) {
|
||||
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(PREF_URL, url)
|
||||
.commit()
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun getUrl(context: Context): String? {
|
||||
|
@ -130,6 +129,6 @@ object AccountUtils {
|
|||
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.remove(PREF_URL)
|
||||
.commit()
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,36 +12,35 @@ import android.util.Log
|
|||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import android.widget.* // ktlint-disable no-wildcard-imports
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.nextcloud.android.sso.AccountImporter
|
||||
import com.nextcloud.android.sso.ui.UiExceptionManager
|
||||
|
||||
import com.nextcloud.android.sso.helper.SingleAccountHelper
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.nextcloud.android.sso.AccountImporter
|
||||
import com.nextcloud.android.sso.AccountImporter.clearAllAuthTokens
|
||||
import com.nextcloud.android.sso.exceptions.*
|
||||
import com.nextcloud.android.sso.exceptions.AccountImportCancelledException
|
||||
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException
|
||||
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException
|
||||
import com.nextcloud.android.sso.helper.SingleAccountHelper
|
||||
import com.nextcloud.android.sso.ui.UiExceptionManager
|
||||
import org.unifiedpush.distributor.nextpush.R
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.connect
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.isConnected
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.nextcloudAppNotInstalledDialog
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.ssoAccount
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiDeleteApp
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiDeleteDevice
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendUnregistered
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.getDb
|
||||
import org.unifiedpush.distributor.nextpush.services.*
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteApp
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteDevice
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.getDb
|
||||
import org.unifiedpush.distributor.nextpush.services.RestartWorker
|
||||
import org.unifiedpush.distributor.nextpush.services.StartService
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
import java.lang.String.format
|
||||
|
||||
private const val TAG = "NextPush-MainActivity"
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var listView : ListView
|
||||
private lateinit var listView: ListView
|
||||
private var showLogout = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -106,7 +105,8 @@ class MainActivity : AppCompatActivity() {
|
|||
private fun requestPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
Log.d(TAG, "Requesting POST_NOTIFICATIONS permission")
|
||||
registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
|
@ -132,7 +132,7 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if(hasFocus) {
|
||||
if (hasFocus) {
|
||||
setListView()
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,8 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
private fun logout() {
|
||||
val alert: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(
|
||||
this)
|
||||
this
|
||||
)
|
||||
alert.setTitle(getString(R.string.logout_alert_title))
|
||||
alert.setMessage(R.string.logout_alert_content)
|
||||
alert.setPositiveButton(R.string.ok) { dialog, _ ->
|
||||
|
@ -185,7 +186,7 @@ class MainActivity : AppCompatActivity() {
|
|||
.edit()
|
||||
.remove("PREF_CURRENT_ACCOUNT_STRING")
|
||||
.apply()
|
||||
apiDeleteDevice(this)
|
||||
deleteDevice(this)
|
||||
showStart()
|
||||
finish()
|
||||
startActivity(intent)
|
||||
|
@ -194,42 +195,46 @@ class MainActivity : AppCompatActivity() {
|
|||
alert.show()
|
||||
}
|
||||
|
||||
private fun setListView(){
|
||||
private fun setListView() {
|
||||
listView = findViewById(R.id.applications_list)
|
||||
val db = getDb(this)
|
||||
val tokenList = db.listTokens().toMutableList()
|
||||
val appList = emptyArray<String>().toMutableList()
|
||||
tokenList.forEach {
|
||||
appList.add(db.getPackageName(it))
|
||||
|
||||
val tokenList = emptyList<String>().toMutableList()
|
||||
val appList = emptyList<String>().toMutableList()
|
||||
|
||||
getDb(this).let { db ->
|
||||
db.listTokens().forEach {
|
||||
tokenList.add(it)
|
||||
appList.add(db.getPackageName(it) ?: it)
|
||||
}
|
||||
}
|
||||
|
||||
listView.adapter = ArrayAdapter(
|
||||
this,
|
||||
android.R.layout.simple_list_item_1,
|
||||
appList
|
||||
this,
|
||||
android.R.layout.simple_list_item_1,
|
||||
appList
|
||||
)
|
||||
|
||||
listView.setOnItemLongClickListener(
|
||||
fun(_: AdapterView<*>, _: View, position: Int, _: Long): Boolean {
|
||||
val alert: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(
|
||||
this)
|
||||
alert.setTitle("Unregistering")
|
||||
alert.setMessage("Are you sure to unregister ${appList[position]} ?")
|
||||
alert.setPositiveButton("YES") { dialog, _ ->
|
||||
val connectorToken = tokenList[position]
|
||||
sendUnregistered(this, connectorToken)
|
||||
val database = getDb(this)
|
||||
val appToken = database.getAppToken(connectorToken)
|
||||
database.unregisterApp(connectorToken)
|
||||
apiDeleteApp(this, appToken) {
|
||||
Log.d(TAG,"Unregistration is finished")
|
||||
fun(_: AdapterView<*>, _: View, position: Int, _: Long): Boolean {
|
||||
val alert: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(
|
||||
this
|
||||
)
|
||||
alert.setTitle("Unregistering")
|
||||
alert.setMessage("Are you sure to unregister ${appList[position]} ?")
|
||||
alert.setPositiveButton("YES") { dialog, _ ->
|
||||
val connectorToken = tokenList[position]
|
||||
deleteApp(this, connectorToken) {
|
||||
Log.d(TAG, "Unregistration is finished")
|
||||
this@MainActivity.runOnUiThread {
|
||||
setListView()
|
||||
}
|
||||
tokenList.removeAt(position)
|
||||
appList.removeAt(position)
|
||||
dialog.dismiss()
|
||||
}
|
||||
alert.setNegativeButton("NO") { dialog, _ -> dialog.dismiss() }
|
||||
alert.show()
|
||||
return true
|
||||
dialog.dismiss()
|
||||
}
|
||||
alert.setNegativeButton("NO") { dialog, _ -> dialog.dismiss() }
|
||||
alert.show()
|
||||
return true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
package org.unifiedpush.distributor.nextpush.api
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.sse.EventSource
|
||||
import okhttp3.sse.EventSources
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.getDeviceId
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.removeDeviceId
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.removeUrl
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.saveDeviceId
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.saveUrl
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.ssoAccount
|
||||
import org.unifiedpush.distributor.nextpush.api.provider.ApiProvider
|
||||
import org.unifiedpush.distributor.nextpush.api.provider.ApiProvider.Companion.mApiEndpoint
|
||||
import org.unifiedpush.distributor.nextpush.api.provider.ApiProviderFactory
|
||||
import org.unifiedpush.distributor.nextpush.api.provider.ApiSSOFactory
|
||||
import org.unifiedpush.distributor.nextpush.api.response.ApiResponse
|
||||
import org.unifiedpush.distributor.nextpush.services.SSEListener
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object Api {
|
||||
|
||||
private val TAG = Api::class.java.simpleName
|
||||
private var provider: ApiProviderFactory? = null
|
||||
private var source: EventSource? = null
|
||||
|
||||
private fun Context.withApiProvider(block: (ApiProvider) -> Unit) {
|
||||
(
|
||||
provider ?: run {
|
||||
Log.d(TAG, "Setting SSOProvider")
|
||||
ApiSSOFactory(this).apply {
|
||||
provider = this
|
||||
}
|
||||
}
|
||||
).getProviderAndExecute(block)
|
||||
}
|
||||
|
||||
fun apiDestroy() {
|
||||
provider?.destroyProvider()
|
||||
provider = null
|
||||
source?.cancel()
|
||||
source = null
|
||||
}
|
||||
|
||||
fun Context.apiSync() {
|
||||
getDeviceId(this)?.let {
|
||||
syncDevice(it)
|
||||
}
|
||||
?: run {
|
||||
Log.d(TAG, "No deviceId found.")
|
||||
var deviceId: String? = null
|
||||
|
||||
val parameters = mapOf("deviceName" to Build.MODEL)
|
||||
|
||||
withApiProvider { apiProvider ->
|
||||
apiProvider.createDevice(parameters)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "onSubscribe")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
response.deviceId.let {
|
||||
saveDeviceId(this@apiSync, it)
|
||||
deviceId = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
saveUrl(this@apiSync, "${ssoAccount.url}$mApiEndpoint")
|
||||
// Sync once it is registered
|
||||
deviceId?.let {
|
||||
syncDevice(it)
|
||||
}
|
||||
Log.d(TAG, "mApi register: onComplete")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.syncDevice(deviceId: String) {
|
||||
val client = OkHttpClient.Builder()
|
||||
.readTimeout(0, TimeUnit.SECONDS)
|
||||
.retryOnConnectionFailure(false)
|
||||
.build()
|
||||
val url = "${ssoAccount.url}$mApiEndpoint/device/$deviceId"
|
||||
|
||||
val request = Request.Builder().url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
source = EventSources.createFactory(client).newEventSource(request, SSEListener(this))
|
||||
Log.d(TAG, "cSync done.")
|
||||
}
|
||||
|
||||
fun Context.apiDeleteDevice() {
|
||||
val deviceId = getDeviceId(this) ?: return
|
||||
|
||||
withApiProvider { apiProvider ->
|
||||
apiProvider.deleteDevice(deviceId)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "Subscribed to deleteDevice.")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
if (response.success) {
|
||||
Log.d(TAG, "Device successfully deleted.")
|
||||
} else {
|
||||
Log.d(TAG, "An error occurred while deleting the device.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
removeUrl(this@apiDeleteDevice)
|
||||
}
|
||||
})
|
||||
removeDeviceId(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.apiCreateApp(
|
||||
appName: String,
|
||||
block: (String?) -> Unit
|
||||
) {
|
||||
// The unity of connector token must already be checked here
|
||||
val parameters = getDeviceId(this)?.let {
|
||||
mutableMapOf(
|
||||
"deviceId" to it,
|
||||
"appName" to appName
|
||||
)
|
||||
} ?: return
|
||||
|
||||
withApiProvider { apiProvider ->
|
||||
apiProvider.createApp(parameters)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "Subscribed to createApp.")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
val nextpushToken = if (response.success) {
|
||||
Log.d(TAG, "App successfully created.")
|
||||
response.token
|
||||
} else {
|
||||
Log.d(TAG, "An error occurred while creating the application.")
|
||||
null
|
||||
}
|
||||
block(nextpushToken)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
block(null)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.apiDeleteApp(nextpushToken: String, block: () -> Unit) {
|
||||
withApiProvider { apiProvider ->
|
||||
apiProvider.deleteApp(nextpushToken)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "Subscribed to deleteApp.")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
if (response.success) {
|
||||
Log.d(TAG, "App successfully deleted.")
|
||||
} else {
|
||||
Log.d(TAG, "An error occurred while deleting the application.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
block()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
package org.unifiedpush.distributor.nextpush.api
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.nextcloud.android.sso.api.NextcloudAPI
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.sse.EventSource
|
||||
import okhttp3.sse.EventSources
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.getDeviceId
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.removeDeviceId
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.removeUrl
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.saveDeviceId
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.saveUrl
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.ssoAccount
|
||||
import org.unifiedpush.distributor.nextpush.api.ProviderApi.Companion.mApiEndpoint
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.getDb
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendUnregistered
|
||||
import org.unifiedpush.distributor.nextpush.services.SSEListener
|
||||
import retrofit2.NextcloudRetrofitApiBuilder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val TAG = "ApiUtils"
|
||||
|
||||
object ApiUtils {
|
||||
|
||||
val createQueue = emptyList<String>().toMutableList()
|
||||
val delQueue = emptyList<String>().toMutableList()
|
||||
|
||||
private var mApi: ProviderApi? = null
|
||||
private var nextcloudAPI: NextcloudAPI? = null
|
||||
private var source: EventSource? = null
|
||||
|
||||
fun apiDestroy() {
|
||||
nextcloudAPI?.stop()
|
||||
source?.cancel()
|
||||
nextcloudAPI = null
|
||||
source = null
|
||||
mApi = null
|
||||
}
|
||||
|
||||
private fun cApi(context: Context, callback: () -> Unit) {
|
||||
mApi?.let {
|
||||
callback()
|
||||
} ?: run {
|
||||
val nCallback = object : NextcloudAPI.ApiConnectedListener {
|
||||
override fun onConnected() {
|
||||
Log.d(TAG, "Api connected.")
|
||||
callback()
|
||||
}
|
||||
|
||||
override fun onError(ex: Exception) {
|
||||
Log.d(TAG, "Cannot connect to API: ex = [$ex]")
|
||||
}
|
||||
}
|
||||
NextcloudAPI(context, ssoAccount, GsonBuilder().create(), nCallback).let {
|
||||
nextcloudAPI = it
|
||||
mApi = NextcloudRetrofitApiBuilder(it, mApiEndpoint)
|
||||
.create(ProviderApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun apiSync(context: Context) {
|
||||
cApi(context) { cSync(context) }
|
||||
}
|
||||
|
||||
private fun cSync(context: Context) {
|
||||
var deviceId = getDeviceId(context)
|
||||
// Register the device if it is not yet registered
|
||||
if (deviceId.isNullOrEmpty()) {
|
||||
Log.d(TAG, "No deviceId found.")
|
||||
val parameters: MutableMap<String, String> = HashMap()
|
||||
parameters["deviceName"] = Build.MODEL
|
||||
mApi?.createDevice(parameters)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "onSubscribe")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
response.deviceId.let {
|
||||
saveDeviceId(context, it)
|
||||
deviceId = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
saveUrl(context, "${ssoAccount.url}${mApiEndpoint}")
|
||||
// Sync once it is registered
|
||||
deviceId?.let {
|
||||
cSync(context, it)
|
||||
}
|
||||
Log.d(TAG, "mApi register: onComplete")
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Sync directly
|
||||
Log.d(TAG, "Found saved deviceId")
|
||||
deviceId?.let {
|
||||
cSync(context, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun cSync(context: Context, deviceId: String) {
|
||||
val client = OkHttpClient.Builder()
|
||||
.readTimeout(0, TimeUnit.SECONDS)
|
||||
.retryOnConnectionFailure(false)
|
||||
.build()
|
||||
val url = "${ssoAccount.url}$mApiEndpoint/device/$deviceId"
|
||||
|
||||
val request = Request.Builder().url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
source = EventSources.createFactory(client).newEventSource(request, SSEListener(context))
|
||||
Log.d(TAG, "cSync done.")
|
||||
}
|
||||
|
||||
fun apiDeleteDevice(context: Context) {
|
||||
val db = getDb(context)
|
||||
db.listTokens().forEach {
|
||||
sendUnregistered(context, it)
|
||||
db.unregisterApp(it)
|
||||
}
|
||||
cApi(context) { cDeleteDevice(context) }
|
||||
}
|
||||
|
||||
private fun cDeleteDevice(context: Context) {
|
||||
val deviceId = getDeviceId(context) ?: return
|
||||
|
||||
mApi?.deleteDevice(deviceId)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "Subscribed to deleteDevice.")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
if (response.success) {
|
||||
Log.d(TAG, "Device successfully deleted.")
|
||||
} else {
|
||||
Log.d(TAG, "An error occurred while deleting the device.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
removeUrl(context)
|
||||
}
|
||||
})
|
||||
removeDeviceId(context)
|
||||
}
|
||||
|
||||
fun apiCreateApp(
|
||||
context: Context,
|
||||
appName: String,
|
||||
connectorToken: String,
|
||||
callback: () -> Unit
|
||||
) {
|
||||
cApi(context) {
|
||||
cCreateApp(context, appName, connectorToken) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cCreateApp(
|
||||
context: Context,
|
||||
appName: String,
|
||||
connectorToken: String,
|
||||
callback: () -> Unit
|
||||
) {
|
||||
// The unity of connector token must already be checked here
|
||||
val parameters = getDeviceId(context)?.let {
|
||||
mutableMapOf(
|
||||
"deviceId" to it,
|
||||
"appName" to appName
|
||||
)
|
||||
} ?: return
|
||||
|
||||
mApi?.createApp(parameters)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "Subscribed to createApp.")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
if (response.success) {
|
||||
Log.d(TAG, "App successfully created.")
|
||||
/**
|
||||
* Ignore printed error for SQLiteContstraintException.
|
||||
* It is printed and not thrown by SQLiteDatabase.java
|
||||
* So we can't catch it
|
||||
*/
|
||||
val db = getDb(context)
|
||||
db.registerApp(appName, connectorToken, response.token)
|
||||
} else {
|
||||
Log.d(TAG, "An error occurred while creating the application.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
createQueue.remove(connectorToken)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
createQueue.remove(connectorToken)
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun apiDeleteApp(context: Context, connectorToken: String, callback: () -> Unit) {
|
||||
cApi(context) {
|
||||
cDeleteApp(context, connectorToken) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cDeleteApp(context: Context, connectorToken: String, callback: () -> Unit) {
|
||||
val appToken = getDb(context).getAppToken(connectorToken)
|
||||
mApi?.deleteApp(appToken)
|
||||
?.subscribeOn(Schedulers.newThread())
|
||||
?.observeOn(Schedulers.newThread())
|
||||
?.subscribe(object : Observer<ApiResponse?> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
Log.d(TAG, "Subscribed to deleteApp.")
|
||||
}
|
||||
|
||||
override fun onNext(response: ApiResponse) {
|
||||
if (response.success) {
|
||||
Log.d(TAG, "App successfully deleted.")
|
||||
} else {
|
||||
Log.d(TAG, "An error occurred while deleting the application.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
delQueue.remove(connectorToken)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
delQueue.remove(connectorToken)
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
package org.unifiedpush.distributor.nextpush.api
|
||||
package org.unifiedpush.distributor.nextpush.api.provider
|
||||
|
||||
import io.reactivex.Observable
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.DELETE
|
||||
import org.unifiedpush.distributor.nextpush.api.response.ApiResponse
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
interface ProviderApi {
|
||||
interface ApiProvider {
|
||||
|
||||
@PUT("/device/")
|
||||
fun createDevice(
|
||||
@Body subscribeMap: MutableMap<String, String>
|
||||
@Body subscribeMap: Map<String, String>
|
||||
): Observable<ApiResponse>?
|
||||
|
||||
@DELETE("/device/{deviceId}")
|
||||
|
@ -18,7 +19,7 @@ interface ProviderApi {
|
|||
|
||||
@PUT("/app/")
|
||||
fun createApp(
|
||||
@Body authorizeMap: MutableMap<String, String>
|
||||
@Body authorizeMap: Map<String, String>
|
||||
): Observable<ApiResponse>?
|
||||
|
||||
@DELETE("/app/{token}")
|
|
@ -0,0 +1,6 @@
|
|||
package org.unifiedpush.distributor.nextpush.api.provider
|
||||
|
||||
interface ApiProviderFactory {
|
||||
fun getProviderAndExecute(block: (ApiProvider) -> Unit)
|
||||
fun destroyProvider()
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.unifiedpush.distributor.nextpush.api.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.nextcloud.android.sso.api.NextcloudAPI
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils
|
||||
import retrofit2.NextcloudRetrofitApiBuilder
|
||||
|
||||
class ApiSSOFactory(val context: Context) : ApiProviderFactory {
|
||||
|
||||
private val TAG = ApiSSOFactory::class.java.simpleName
|
||||
private var apiProvider: ApiProvider? = null
|
||||
private lateinit var nextcloudAPI: NextcloudAPI
|
||||
|
||||
override fun getProviderAndExecute(block: (ApiProvider) -> Unit) {
|
||||
apiProvider?.let(block)
|
||||
?: run {
|
||||
Log.d(TAG, "Creating new provider")
|
||||
val ssoApiCallback = object : NextcloudAPI.ApiConnectedListener {
|
||||
override fun onConnected() {
|
||||
Log.d(TAG, "Api connected.")
|
||||
NextcloudRetrofitApiBuilder(nextcloudAPI, ApiProvider.mApiEndpoint)
|
||||
.create(ApiProvider::class.java).let {
|
||||
apiProvider = it
|
||||
block(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(ex: Exception) {
|
||||
Log.d(TAG, "Cannot connect to API: ex = [$ex]")
|
||||
}
|
||||
}
|
||||
nextcloudAPI = NextcloudAPI(
|
||||
context,
|
||||
AccountUtils.ssoAccount,
|
||||
GsonBuilder().create(),
|
||||
ssoApiCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun destroyProvider() {
|
||||
nextcloudAPI.stop()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package org.unifiedpush.distributor.nextpush.api
|
||||
package org.unifiedpush.distributor.nextpush.api.response
|
||||
|
||||
data class ApiResponse(
|
||||
val success: Boolean = false,
|
||||
val deviceId: String = "",
|
||||
val token: String = "",
|
||||
val token: String = ""
|
||||
)
|
|
@ -1,8 +1,8 @@
|
|||
package org.unifiedpush.distributor.nextpush.api
|
||||
package org.unifiedpush.distributor.nextpush.api.response
|
||||
|
||||
data class SSEResponse (
|
||||
data class SSEResponse(
|
||||
val type: String = "",
|
||||
val token: String = "",
|
||||
val message: String = "",
|
||||
val keepalive: Int = 900
|
||||
)
|
||||
)
|
|
@ -13,12 +13,12 @@ private const val FIELD_PACKAGE_NAME = "packageName"
|
|||
private const val FIELD_CONNECTOR_TOKEN = "connectorToken"
|
||||
private const val FIELD_APP_TOKEN = "appToken"
|
||||
private const val CREATE_TABLE_APPS = "CREATE TABLE apps (" +
|
||||
"$FIELD_PACKAGE_NAME TEXT," +
|
||||
"$FIELD_CONNECTOR_TOKEN TEXT," +
|
||||
"$FIELD_APP_TOKEN TEXT," +
|
||||
"PRIMARY KEY ($FIELD_CONNECTOR_TOKEN));"
|
||||
"$FIELD_PACKAGE_NAME TEXT," +
|
||||
"$FIELD_CONNECTOR_TOKEN TEXT," +
|
||||
"$FIELD_APP_TOKEN TEXT," +
|
||||
"PRIMARY KEY ($FIELD_CONNECTOR_TOKEN));"
|
||||
|
||||
class MessagingDatabase(context: Context):
|
||||
class ConnectionsDatabase(context: Context) :
|
||||
SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
|
@ -51,19 +51,19 @@ class MessagingDatabase(context: Context):
|
|||
val selection = "$FIELD_PACKAGE_NAME = ? AND $FIELD_CONNECTOR_TOKEN = ?"
|
||||
val selectionArgs = arrayOf(packageName, connectorToken)
|
||||
return db.query(
|
||||
TABLE_APPS,
|
||||
null,
|
||||
selection,
|
||||
selectionArgs,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
TABLE_APPS,
|
||||
null,
|
||||
selection,
|
||||
selectionArgs,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
(cursor != null && cursor.count > 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPackageName(connectorToken: String): String {
|
||||
fun getPackageName(connectorToken: String): String? {
|
||||
val db = readableDatabase
|
||||
val projection = arrayOf(FIELD_PACKAGE_NAME)
|
||||
val selection = "$FIELD_CONNECTOR_TOKEN = ?"
|
||||
|
@ -78,11 +78,11 @@ class MessagingDatabase(context: Context):
|
|||
null
|
||||
).use { cursor ->
|
||||
val column = cursor.getColumnIndex(FIELD_PACKAGE_NAME)
|
||||
if (cursor.moveToFirst() && column >= 0) cursor.getString(column) else ""
|
||||
if (cursor.moveToFirst() && column >= 0) cursor.getString(column) else null
|
||||
}
|
||||
}
|
||||
|
||||
fun getAppToken(connectorToken: String): String {
|
||||
fun getAppToken(connectorToken: String): String? {
|
||||
val db = readableDatabase
|
||||
val projection = arrayOf(FIELD_APP_TOKEN)
|
||||
val selection = "$FIELD_CONNECTOR_TOKEN = ?"
|
||||
|
@ -97,11 +97,11 @@ class MessagingDatabase(context: Context):
|
|||
null
|
||||
).use { cursor ->
|
||||
val column = cursor.getColumnIndex(FIELD_APP_TOKEN)
|
||||
if (cursor.moveToFirst() && column >= 0) cursor.getString(column) else ""
|
||||
if (cursor.moveToFirst() && column >= 0) cursor.getString(column) else null
|
||||
}
|
||||
}
|
||||
|
||||
fun getConnectorToken(appToken: String): String {
|
||||
fun getConnectorToken(appToken: String): String? {
|
||||
val db = readableDatabase
|
||||
val projection = arrayOf(FIELD_CONNECTOR_TOKEN)
|
||||
val selection = "$FIELD_APP_TOKEN = ?"
|
||||
|
@ -116,7 +116,7 @@ class MessagingDatabase(context: Context):
|
|||
null
|
||||
).use { cursor ->
|
||||
val column = cursor.getColumnIndex(FIELD_CONNECTOR_TOKEN)
|
||||
if (cursor.moveToFirst() && column >= 0) cursor.getString(column) else ""
|
||||
if (cursor.moveToFirst() && column >= 0) cursor.getString(column) else null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,11 +131,12 @@ class MessagingDatabase(context: Context):
|
|||
null,
|
||||
null,
|
||||
null
|
||||
).use{ cursor ->
|
||||
).use { cursor ->
|
||||
generateSequence { if (cursor.moveToNext()) cursor else null }
|
||||
.mapNotNull{
|
||||
.mapNotNull {
|
||||
val column = cursor.getColumnIndex(FIELD_CONNECTOR_TOKEN)
|
||||
if (column >= 0) it.getString(column) else null }
|
||||
if (column >= 0) it.getString(column) else null
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}
|
|
@ -4,35 +4,35 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.getUrl
|
||||
import org.unifiedpush.distributor.nextpush.api.Api.apiCreateApp
|
||||
import org.unifiedpush.distributor.nextpush.api.Api.apiDeleteApp
|
||||
import org.unifiedpush.distributor.nextpush.api.Api.apiDeleteDevice
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
|
||||
/**
|
||||
* These functions are used to send messages to other apps
|
||||
*/
|
||||
|
||||
private const val TAG = "DistributorUtils"
|
||||
|
||||
object DistributorUtils {
|
||||
object Distributor {
|
||||
|
||||
const val TOKEN_NEW = "token_new"
|
||||
const val TOKEN_REGISTERED_OK = "token_registered_ok"
|
||||
const val TOKEN_NOK = "token_nok"
|
||||
|
||||
private lateinit var db: MessagingDatabase
|
||||
private lateinit var db: ConnectionsDatabase
|
||||
|
||||
fun getDb(context: Context): MessagingDatabase {
|
||||
fun getDb(context: Context): ConnectionsDatabase {
|
||||
if (!this::db.isInitialized) {
|
||||
db = MessagingDatabase(context)
|
||||
db = ConnectionsDatabase(context)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
fun sendMessage(context: Context, appToken: String, message: ByteArray) {
|
||||
val db = getDb(context)
|
||||
val connectorToken = db.getConnectorToken(appToken)
|
||||
val connectorToken = db.getConnectorToken(appToken) ?: return
|
||||
val application = getApp(context, connectorToken)
|
||||
if (application.isNullOrBlank()) {
|
||||
return
|
||||
}
|
||||
|
||||
val broadcastIntent = Intent()
|
||||
broadcastIntent.`package` = application
|
||||
broadcastIntent.action = ACTION_MESSAGE
|
||||
|
@ -73,7 +73,7 @@ object DistributorUtils {
|
|||
context.sendBroadcast(broadcastIntent)
|
||||
}
|
||||
|
||||
fun sendUnregistered(context: Context, connectorToken: String) {
|
||||
private fun sendUnregistered(context: Context, connectorToken: String) {
|
||||
val application = getApp(context, connectorToken)
|
||||
if (application.isNullOrBlank()) {
|
||||
return
|
||||
|
@ -88,10 +88,7 @@ object DistributorUtils {
|
|||
private fun getApp(context: Context, connectorToken: String): String? {
|
||||
val db = getDb(context)
|
||||
val app = db.getPackageName(connectorToken)
|
||||
return app.ifBlank {
|
||||
Log.w(TAG, "No app found for this token")
|
||||
null
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
private fun getEndpoint(context: Context, connectorToken: String): String {
|
||||
|
@ -110,4 +107,41 @@ object DistributorUtils {
|
|||
}
|
||||
return TOKEN_NOK
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteDevice(context: Context) {
|
||||
val db = getDb(context)
|
||||
db.listTokens().forEach {
|
||||
sendUnregistered(context, it)
|
||||
db.unregisterApp(it)
|
||||
}
|
||||
context.apiDeleteDevice()
|
||||
}
|
||||
|
||||
fun createApp(context: Context, appName: String, connectorToken: String, block: () -> Unit) {
|
||||
context.apiCreateApp(appName) { nextpushToken ->
|
||||
nextpushToken?.let {
|
||||
getDb(context).registerApp(appName, connectorToken, it)
|
||||
}
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteApp(context: Context, connectorToken: String, block: () -> Unit) {
|
||||
sendUnregistered(context, connectorToken)
|
||||
val db = getDb(context)
|
||||
db.getAppToken(
|
||||
connectorToken
|
||||
)?.let { nextpushToken ->
|
||||
context.apiDeleteApp(nextpushToken) {
|
||||
db.unregisterApp(connectorToken)
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteAppFromAppToken(context: Context, appToken: String) {
|
||||
getDb(context).getConnectorToken(appToken)?.let {
|
||||
deleteApp(context, it) {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,33 +6,33 @@ import android.content.Intent
|
|||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.isConnected
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiCreateApp
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiDeleteApp
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.createQueue
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.delQueue
|
||||
|
||||
import org.unifiedpush.distributor.nextpush.distributor.*
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.TOKEN_NEW
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.TOKEN_NOK
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.TOKEN_REGISTERED_OK
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.checkToken
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.getDb
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendEndpoint
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendRegistrationFailed
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendUnregistered
|
||||
import org.unifiedpush.distributor.nextpush.distributor.* // ktlint-disable no-wildcard-imports
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.TOKEN_NEW
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.TOKEN_NOK
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.TOKEN_REGISTERED_OK
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.checkToken
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.createApp
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteApp
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.getDb
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.sendEndpoint
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.sendRegistrationFailed
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
import java.lang.Exception
|
||||
import java.util.Timer
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
/**
|
||||
* THIS SERVICE IS USED BY OTHER APPS TO REGISTER
|
||||
*/
|
||||
|
||||
private const val TAG = "RegisterBroadcastReceiver"
|
||||
private val createQueue = emptyList<String>().toMutableList()
|
||||
private val delQueue = emptyList<String>().toMutableList()
|
||||
|
||||
class RegisterBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
private const val WAKE_LOCK_TAG = "NextPush:RegisterBroadcastReceiver:lock"
|
||||
private var wakeLock : PowerManager.WakeLock? = null
|
||||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
|
@ -41,25 +41,27 @@ class RegisterBroadcastReceiver : BroadcastReceiver() {
|
|||
}
|
||||
wakeLock?.acquire(30000L /*30 secs*/)
|
||||
when (intent?.action) {
|
||||
ACTION_REGISTER ->{
|
||||
Log.i(TAG,"REGISTER")
|
||||
val connectorToken = intent.getStringExtra(EXTRA_TOKEN)?: ""
|
||||
val application = intent.getStringExtra(EXTRA_APPLICATION)?: ""
|
||||
if (application.isBlank()) {
|
||||
Log.w(TAG,"Trying to register an app without packageName")
|
||||
ACTION_REGISTER -> {
|
||||
Log.i(TAG, "REGISTER")
|
||||
val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: run {
|
||||
Log.w(TAG, "Trying to register an app without connector token")
|
||||
return
|
||||
}
|
||||
when (checkToken(context, connectorToken, application)) {
|
||||
val application = intent.getStringExtra(EXTRA_APPLICATION) ?: run {
|
||||
Log.w(TAG, "Trying to register an app without packageName")
|
||||
return
|
||||
}
|
||||
when (checkToken(context.applicationContext, connectorToken, application)) {
|
||||
TOKEN_REGISTERED_OK -> sendEndpoint(context.applicationContext, connectorToken)
|
||||
TOKEN_NOK -> sendRegistrationFailed(
|
||||
context,
|
||||
context.applicationContext,
|
||||
application,
|
||||
connectorToken
|
||||
)
|
||||
TOKEN_NEW -> {
|
||||
if (!isConnected(context, showDialog = false)) {
|
||||
if (!isConnected(context.applicationContext, showDialog = false)) {
|
||||
sendRegistrationFailed(
|
||||
context,
|
||||
context.applicationContext,
|
||||
application,
|
||||
connectorToken,
|
||||
message = "NextPush is not connected"
|
||||
|
@ -68,12 +70,14 @@ class RegisterBroadcastReceiver : BroadcastReceiver() {
|
|||
}
|
||||
if (connectorToken !in createQueue) {
|
||||
createQueue.add(connectorToken)
|
||||
apiCreateApp(
|
||||
delayRemove(createQueue, connectorToken)
|
||||
createApp(
|
||||
context.applicationContext,
|
||||
application,
|
||||
connectorToken
|
||||
) {
|
||||
sendEndpoint(context.applicationContext, connectorToken)
|
||||
createQueue.remove(connectorToken)
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Already registering this token")
|
||||
|
@ -81,21 +85,18 @@ class RegisterBroadcastReceiver : BroadcastReceiver() {
|
|||
}
|
||||
}
|
||||
}
|
||||
ACTION_UNREGISTER ->{
|
||||
Log.i(TAG,"UNREGISTER")
|
||||
val connectorToken = intent.getStringExtra(EXTRA_TOKEN)?: ""
|
||||
val application = getDb(context).getPackageName(connectorToken)
|
||||
if (application.isBlank()) {
|
||||
return
|
||||
}
|
||||
ACTION_UNREGISTER -> {
|
||||
Log.i(TAG, "UNREGISTER")
|
||||
val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: ""
|
||||
getDb(context.applicationContext).getPackageName(connectorToken) ?: return
|
||||
|
||||
if (connectorToken !in delQueue) {
|
||||
delQueue.add(connectorToken)
|
||||
sendUnregistered(context.applicationContext, connectorToken)
|
||||
delayRemove(delQueue, connectorToken)
|
||||
try {
|
||||
apiDeleteApp(context.applicationContext, connectorToken) {
|
||||
val db = getDb(context.applicationContext)
|
||||
db.unregisterApp(connectorToken)
|
||||
deleteApp(context.applicationContext, connectorToken) {
|
||||
Log.d(TAG, "Unregistration is finished")
|
||||
delQueue.remove(connectorToken)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "Could not delete app")
|
||||
|
@ -111,4 +112,10 @@ class RegisterBroadcastReceiver : BroadcastReceiver() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun delayRemove(list: MutableList<String>, token: String) {
|
||||
Timer().schedule(1_000L /* 1sec */) {
|
||||
list.remove(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,3 @@ class StartReceiver : BroadcastReceiver() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,32 +3,33 @@ package org.unifiedpush.distributor.nextpush.services
|
|||
import android.content.Context
|
||||
import android.util.Log
|
||||
import okhttp3.sse.EventSource
|
||||
import org.unifiedpush.distributor.nextpush.services.NotificationUtils.createWarningNotification
|
||||
import org.unifiedpush.distributor.nextpush.services.NotificationUtils.deleteWarningNotification
|
||||
|
||||
private const val TAG = "FailureHandler"
|
||||
import org.unifiedpush.distributor.nextpush.utils.NotificationUtils.createWarningNotification
|
||||
import org.unifiedpush.distributor.nextpush.utils.NotificationUtils.deleteWarningNotification
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
|
||||
interface FailureHandler {
|
||||
|
||||
var nFails: Int
|
||||
|
||||
// This is the last eventSource opened
|
||||
var eventSource: EventSource?
|
||||
|
||||
fun newEventSource(context: Context, eventSource: EventSource) {
|
||||
Log.d(TAG,"newEvent/Eventsource: $eventSource")
|
||||
Log.d(TAG, "newEvent/Eventsource: $eventSource")
|
||||
this.eventSource = eventSource
|
||||
nFails = 0
|
||||
deleteWarningNotification(context)
|
||||
}
|
||||
|
||||
fun newFail(context: Context, eventSource: EventSource?) {
|
||||
Log.d(TAG,"newFail/Eventsource: $eventSource")
|
||||
Log.d(TAG, "newFail/Eventsource: $eventSource")
|
||||
// no eventSource or the last opened
|
||||
if (this.eventSource == null || this.eventSource == eventSource) {
|
||||
Log.d(TAG, "EventSource is known or null")
|
||||
nFails++
|
||||
if (hasFailed(twice = true))
|
||||
if (hasFailed(twice = true)) {
|
||||
createWarningNotification(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,10 +45,10 @@ interface FailureHandler {
|
|||
nFails = 0
|
||||
eventSource = null
|
||||
}
|
||||
|
||||
|
||||
fun hasFailed(twice: Boolean = false, orNeverStart: Boolean = true): Boolean {
|
||||
// nFails > 0 to be sure it is not actually restarting
|
||||
return if (orNeverStart) { eventSource == null } else { false } ||
|
||||
nFails > if (twice) { 1 } else { 0 }
|
||||
nFails > if (twice) { 1 } else { 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,18 @@ package org.unifiedpush.distributor.nextpush.services
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.*
|
||||
import androidx.work.* // ktlint-disable no-wildcard-imports
|
||||
import org.unifiedpush.distributor.nextpush.services.SSEListener.Companion.keepalive
|
||||
import org.unifiedpush.distributor.nextpush.services.SSEListener.Companion.lastEventDate
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val UNIQUE_WORK_TAG = "nextpush::RestartWorker::unique"
|
||||
|
||||
class RestartWorker (ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
|
||||
class RestartWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RestartWorker"
|
||||
fun start(context: Context, delay: Long? = null) {
|
||||
val work = PeriodicWorkRequestBuilder<RestartWorker>(16, TimeUnit.MINUTES)
|
||||
if (delay != null) {
|
||||
|
@ -42,10 +42,10 @@ class RestartWorker (ctx: Context, params: WorkerParameters) : Worker(ctx, param
|
|||
StartService.setMaxFails(applicationContext) // Max, will keep using the current worker
|
||||
StartService.startListener(applicationContext)
|
||||
}
|
||||
}?:run {
|
||||
} ?: run {
|
||||
Log.d(TAG, "Restarting")
|
||||
StartService.startListener(applicationContext)
|
||||
}
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,24 +2,22 @@ package org.unifiedpush.distributor.nextpush.services
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Base64
|
||||
import okhttp3.sse.EventSourceListener
|
||||
import okhttp3.sse.EventSource
|
||||
import android.util.Log
|
||||
import okhttp3.Response
|
||||
import java.lang.Exception
|
||||
import com.google.gson.Gson
|
||||
import org.unifiedpush.distributor.nextpush.api.SSEResponse
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.getDb
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendMessage
|
||||
import org.unifiedpush.distributor.nextpush.distributor.DistributorUtils.sendUnregistered
|
||||
import java.util.*
|
||||
import okhttp3.Response
|
||||
import okhttp3.sse.EventSource
|
||||
import okhttp3.sse.EventSourceListener
|
||||
import org.unifiedpush.distributor.nextpush.api.response.SSEResponse
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteAppFromAppToken
|
||||
import org.unifiedpush.distributor.nextpush.distributor.Distributor.sendMessage
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
import java.lang.Exception
|
||||
import java.util.Calendar
|
||||
|
||||
private const val TAG = "SSEListener"
|
||||
|
||||
class SSEListener (val context: Context) : EventSourceListener() {
|
||||
class SSEListener(val context: Context) : EventSourceListener() {
|
||||
|
||||
companion object {
|
||||
var lastEventDate : Calendar? = null
|
||||
var lastEventDate: Calendar? = null
|
||||
var keepalive = 900
|
||||
}
|
||||
|
||||
|
@ -58,12 +56,7 @@ class SSEListener (val context: Context) : EventSourceListener() {
|
|||
}
|
||||
"deleteApp" -> {
|
||||
val message = Gson().fromJson(data, SSEResponse::class.java)
|
||||
val db = getDb(context.applicationContext)
|
||||
val connectorToken = db.getConnectorToken(message.token)
|
||||
if (connectorToken.isEmpty())
|
||||
return
|
||||
sendUnregistered(context.applicationContext, connectorToken)
|
||||
db.unregisterApp(connectorToken)
|
||||
deleteAppFromAppToken(context, message.token)
|
||||
}
|
||||
}
|
||||
StartService.wakeLock?.let {
|
||||
|
@ -74,16 +67,18 @@ class SSEListener (val context: Context) : EventSourceListener() {
|
|||
}
|
||||
|
||||
override fun onClosed(eventSource: EventSource) {
|
||||
if (!StartService.isServiceStarted)
|
||||
if (!StartService.isServiceStarted) {
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "onClosed: $eventSource")
|
||||
StartService.newFail(context, eventSource)
|
||||
RestartWorker.start(context, delay = 0)
|
||||
}
|
||||
|
||||
override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
|
||||
if (!StartService.isServiceStarted)
|
||||
if (!StartService.isServiceStarted) {
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "onFailure")
|
||||
t?.let {
|
||||
Log.d(TAG, "An error occurred: $t")
|
||||
|
@ -93,12 +88,12 @@ class SSEListener (val context: Context) : EventSourceListener() {
|
|||
}
|
||||
StartService.newFail(context, eventSource)
|
||||
val delay = when (StartService.nFails) {
|
||||
1 -> 2 // 2sec
|
||||
2 -> 20 // 20sec
|
||||
3 -> 60 // 1min
|
||||
4 -> 300 // 5min
|
||||
5 -> 600 // 10min
|
||||
else -> return // else keep the worker with its 16min
|
||||
1 -> 2 // 2sec
|
||||
2 -> 20 // 20sec
|
||||
3 -> 60 // 1min
|
||||
4 -> 300 // 5min
|
||||
5 -> 600 // 10min
|
||||
else -> return // else keep the worker with its 16min
|
||||
}.toLong()
|
||||
Log.d(TAG, "Retrying in $delay s")
|
||||
RestartWorker.start(context, delay = delay)
|
||||
|
|
|
@ -3,41 +3,37 @@ package org.unifiedpush.distributor.nextpush.services
|
|||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
|
||||
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException
|
||||
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException
|
||||
import com.nextcloud.android.sso.helper.SingleAccountHelper
|
||||
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.ssoAccount
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.nextcloudAppNotInstalledDialog
|
||||
|
||||
import android.net.Network
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.NetworkCapabilities
|
||||
import okhttp3.sse.EventSource
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiDestroy
|
||||
import org.unifiedpush.distributor.nextpush.api.ApiUtils.apiSync
|
||||
import org.unifiedpush.distributor.nextpush.services.NotificationUtils.createForegroundNotification
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.nextcloudAppNotInstalledDialog
|
||||
import org.unifiedpush.distributor.nextpush.account.AccountUtils.ssoAccount
|
||||
import org.unifiedpush.distributor.nextpush.api.Api.apiDestroy
|
||||
import org.unifiedpush.distributor.nextpush.api.Api.apiSync
|
||||
import org.unifiedpush.distributor.nextpush.utils.NOTIFICATION_ID_FOREGROUND
|
||||
import org.unifiedpush.distributor.nextpush.utils.NotificationUtils.createForegroundNotification
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
import java.lang.Exception
|
||||
|
||||
private const val TAG = "StartService"
|
||||
class StartService : Service() {
|
||||
|
||||
class StartService: Service(){
|
||||
|
||||
companion object: FailureHandler {
|
||||
companion object : FailureHandler {
|
||||
private const val WAKE_LOCK_TAG = "NextPush:StartService:lock"
|
||||
const val SERVICE_STOPPED_ACTION = "org.unifiedpush.distributor.nextpush.services.STOPPED"
|
||||
|
||||
var isServiceStarted = false
|
||||
var wakeLock: PowerManager.WakeLock? = null
|
||||
|
||||
fun startListener(context: Context){
|
||||
fun startListener(context: Context) {
|
||||
if (isServiceStarted && !hasFailed()) return
|
||||
Log.d(TAG, "Starting the Listener")
|
||||
Log.d(TAG, "Service is started: $isServiceStarted")
|
||||
|
@ -45,7 +41,7 @@ class StartService: Service(){
|
|||
val serviceIntent = Intent(context, StartService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent)
|
||||
}else{
|
||||
} else {
|
||||
context.startService(serviceIntent)
|
||||
}
|
||||
}
|
||||
|
@ -59,9 +55,9 @@ class StartService: Service(){
|
|||
return null
|
||||
}
|
||||
|
||||
override fun onCreate(){
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.i(TAG,"StartService created")
|
||||
Log.i(TAG, "StartService created")
|
||||
val notification = createForegroundNotification(this)
|
||||
startForeground(NOTIFICATION_ID_FOREGROUND, notification)
|
||||
}
|
||||
|
@ -124,12 +120,13 @@ class StartService: Service(){
|
|||
}
|
||||
}
|
||||
|
||||
apiSync(this)
|
||||
apiSync()
|
||||
}
|
||||
|
||||
private var connectivityManager = null as ConnectivityManager?
|
||||
|
||||
private val networkCallback = object : NetworkCallback() {
|
||||
val TAG = this@StartService.TAG
|
||||
override fun onAvailable(network: Network) {
|
||||
Log.d(TAG, "Network is CONNECTED")
|
||||
if (StartService.hasFailed(twice = true, orNeverStart = false)) {
|
||||
|
@ -154,13 +151,12 @@ class StartService: Service(){
|
|||
Log.d(TAG, "Registering Network Callback")
|
||||
try {
|
||||
connectivityManager = (
|
||||
this.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
).apply {
|
||||
registerDefaultNetworkCallback(networkCallback)
|
||||
}
|
||||
this.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
).apply {
|
||||
registerDefaultNetworkCallback(networkCallback)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.unifiedpush.distributor.nextpush.services
|
||||
package org.unifiedpush.distributor.nextpush.utils
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Notification
|
||||
|
@ -50,10 +50,14 @@ object NotificationUtils {
|
|||
)
|
||||
|
||||
val builder: Notification.Builder =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
|
||||
context,
|
||||
notificationChannelId
|
||||
) else Notification.Builder(context)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Notification.Builder(
|
||||
context,
|
||||
notificationChannelId
|
||||
)
|
||||
} else {
|
||||
Notification.Builder(context)
|
||||
}
|
||||
|
||||
return builder
|
||||
.setContentTitle(context.getString(R.string.app_name))
|
||||
|
@ -67,8 +71,9 @@ object NotificationUtils {
|
|||
}
|
||||
|
||||
fun createWarningNotification(context: Context) {
|
||||
if (warningShown)
|
||||
if (warningShown) {
|
||||
return
|
||||
}
|
||||
val notificationChannelId = "${context.getString(R.string.app_name)}.Warning"
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
@ -97,12 +102,15 @@ object NotificationUtils {
|
|||
)
|
||||
|
||||
val builder: Notification.Builder = (
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Notification.Builder(
|
||||
context,
|
||||
notificationChannelId
|
||||
)
|
||||
else Notification.Builder(context)
|
||||
).setContentTitle(context.getString(R.string.app_name))
|
||||
} else {
|
||||
Notification.Builder(context)
|
||||
}
|
||||
).setContentTitle(context.getString(R.string.app_name))
|
||||
.setContentText(context.getString(R.string.warning_notif_description))
|
||||
.setSmallIcon(R.drawable.ic_launcher_notification)
|
||||
.setTicker(context.getString(R.string.warning_notif_ticker))
|
||||
|
@ -128,4 +136,4 @@ object NotificationUtils {
|
|||
notificationManager.cancel(NOTIFICATION_ID_WARNING)
|
||||
warningShown = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.unifiedpush.distributor.nextpush.utils
|
||||
|
||||
val Any.TAG: String
|
||||
get() {
|
||||
val tag = javaClass.simpleName
|
||||
return if (tag.length <= 23) tag else tag.substring(0, 23)
|
||||
}
|
|
@ -4,11 +4,17 @@ buildscript {
|
|||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
content {
|
||||
includeModule 'org.jlleitschuh.gradle', 'ktlint-gradle'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.4.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:11.2.0"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
@ -25,6 +31,7 @@ allprojects {
|
|||
}
|
||||
}
|
||||
}
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
|
Loading…
Reference in New Issue