Supprimer les StaticFieldLeak

This commit is contained in:
odrling 2018-03-21 00:05:03 +01:00
parent bb9f87be5c
commit 7699397611
9 changed files with 253 additions and 207 deletions

View file

@ -1,6 +1,7 @@
package xyz.johnny.norntalk
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.AsyncTask
import android.telephony.PhoneNumberUtils
@ -18,6 +19,7 @@ import xyz.johnny.norntalk.ui_components.ComposeView
import xyz.johnny.norntalk.ui_components.NornCaptureActivity
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.lang.ref.WeakReference
import java.util.*
@ -26,32 +28,23 @@ import java.util.*
*/
class ComposeActivity : ComposeView.SendActivity() {
override val layout = R.layout.activity_compose
companion object {
@SuppressLint("StaticFieldLeak")
override fun sendMessage(text: String) {
// trouver le destinataire saisi par l'utilisateur
val sender = number.text.toString()
class SendTask(context: Context, val number: String, val text: String) : AsyncTask<Void, Void, NornMessage?>() {
// vérifie que le numéro de téléphone est correct
if (!PhoneNumberUtils.isWellFormedSmsAddress(sender)) {
Toast.makeText(this, getString(R.string.invalid_number), Toast.LENGTH_SHORT).show()
return
}
// envoyer le message en parallèle
object: AsyncTask<Void, Void, NornMessage?>() {
val context = WeakReference<Context>(context)
override fun doInBackground(vararg params: Void): NornMessage? {
try {
// liste des contacts
val contacts = arrayOf(NornContact.getContact(number.text.toString(), this@ComposeActivity))
val context = this.context.get()!!
val contacts = arrayOf(NornContact.getContact(this.number, context))
// créer la conversation
val conversation = NornConversation.Companion.getConversation(contacts, this@ComposeActivity)
val conversation = NornConversation.Companion.getConversation(contacts, context)
// créer le message
val msg = NornMessage(text, conversation, Date(), this@ComposeActivity)
val msg = NornMessage(this.text, conversation, Date(), context)
// envoyer le message
msg.send(this@ComposeActivity)
msg.send(context)
return msg
} catch (e: Exception) {
val stream = ByteArrayOutputStream()
@ -63,8 +56,10 @@ class ComposeActivity : ComposeView.SendActivity() {
}
override fun onPostExecute(message: NornMessage?) {
val context = this.context.get()!!
if (message == null) {
Toast.makeText(this@ComposeActivity, getString(R.string.invalid_number), Toast.LENGTH_SHORT).show()
Toast.makeText(context, context.getString(R.string.invalid_number), Toast.LENGTH_SHORT).show()
return
}
@ -72,15 +67,31 @@ class ComposeActivity : ComposeView.SendActivity() {
NornMessageDispatcher.send(message)
// ouvrir la conversation
val intent = Intent(this@ComposeActivity, ConversationActivity::class.java)
val intent = Intent(context, ConversationActivity::class.java)
intent.putExtra("conversationId", message.conversation.id)
this@ComposeActivity.startActivity(intent)
context.startActivity(intent)
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
}
override val layout = R.layout.activity_compose
override fun sendMessage(text: String) {
// trouver le destinataire saisi par l'utilisateur
val dest = number.text.toString()
// vérifie que le numéro de téléphone est correct
if (!PhoneNumberUtils.isWellFormedSmsAddress(dest)) {
Toast.makeText(this, getString(R.string.invalid_number), Toast.LENGTH_SHORT).show()
return
}
// envoyer le message en parallèle
SendTask(this, dest, text).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
override fun startActivity(intent: Intent) {
super.startActivity(intent)
this.finish()

View file

@ -1,6 +1,5 @@
package xyz.johnny.norntalk
import android.annotation.SuppressLint
import android.os.AsyncTask
import android.util.Log
import android.view.ContextMenu
@ -9,7 +8,6 @@ import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.ListView
import kotlinx.android.synthetic.main.content_conversation.*
import kotlinx.android.synthetic.main.fragment_message.view.*
import kotlinx.android.synthetic.main.view_compose.*
import xyz.johnny.norntalk.R.id.action_mute
@ -22,6 +20,7 @@ import xyz.johnny.norntalk.messages.NornMessageDispatcher
import xyz.johnny.norntalk.ui_components.ComposeView
import xyz.johnny.norntalk.ui_components.DismissableActivity
import xyz.johnny.norntalk.ui_components.NornMessageAdapter
import java.lang.ref.WeakReference
import java.net.URLDecoder
import java.util.*
@ -37,6 +36,60 @@ class ConversationActivity: ComposeView.SendActivity() {
*/
val foreground_activities = HashSet<Int>()
class MessagesTask(activity: ConversationActivity) : AsyncTask<Void, Void, List<NornMessage>>() {
val activity = WeakReference<ConversationActivity>(activity)
override fun doInBackground(vararg params: Void): List<NornMessage> {
val activity = this.activity.get()!!
val db = NornDatabase.getNornDatabase(activity)
return db.messageDao().getConversationMessages(activity.conversation.id).map {
NornMessage.fromDbMessage(it, activity)
}
}
override fun onPostExecute(result: List<NornMessage>) {
super.onPostExecute(result)
try {
// créer la liste de messages
val activity = this.activity.get()!!
val container = activity.findViewById<ListView>(R.id.fragment_container)
activity.adapter = NornMessageAdapter(
result.toMutableList(),
activity,
activity.conversation.id
)
container.adapter = activity.adapter
activity.registerForContextMenu(container)
}catch (e: Exception) {
e.stackTrace.forEach { println(it) }
throw e
}
}
}
class SendTask(activity: ConversationActivity, val text: String) : AsyncTask<Void, Void, NornMessage>() {
val activity = WeakReference<ConversationActivity>(activity)
override fun doInBackground(vararg params: Void): NornMessage {
val activity = this.activity.get()!!
val msg = NornMessage(text, activity.conversation, Date(), activity)
msg.send(activity)
return msg
}
override fun onPostExecute(nMessage: NornMessage) {
// transmettre le message à toutes les activités
NornMessageDispatcher.send(nMessage)
Log.d(this::class.java.simpleName, nMessage.toString())
}
}
}
override val layout = R.layout.activity_conversation
@ -77,63 +130,15 @@ class ConversationActivity: ComposeView.SendActivity() {
}
// lire les messages en arrière plan
val task = @SuppressLint("StaticFieldLeak")
object: AsyncTask<Void, Void, List<NornMessage>>() {
override fun doInBackground(vararg params: Void): List<NornMessage> {
val db = NornDatabase.getNornDatabase(this@ConversationActivity)
return db.messageDao().getConversationMessages(this@ConversationActivity.conversation.id).map {
NornMessage.fromDbMessage(it, this@ConversationActivity)
}
}
override fun onPostExecute(result: List<NornMessage>) {
super.onPostExecute(result)
try {
// créer la liste de messages
val container = fragment_container as ListView
this@ConversationActivity.adapter = NornMessageAdapter(
result.toMutableList(),
this@ConversationActivity,
this@ConversationActivity.conversation.id
)
container.adapter = this@ConversationActivity.adapter
registerForContextMenu(container)
}catch (e: Exception) {
e.stackTrace.forEach { println(it) }
throw e
}
}
}
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
MessagesTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
// ajouter le titre de la conversation
this.title = this.conversation.name
}
@SuppressLint("StaticFieldLeak")
override fun sendMessage(text: String) {
// envoyer le message en arrière plan
object: AsyncTask<Void, Void, NornMessage>() {
override fun doInBackground(vararg params: Void): NornMessage {
val msg = NornMessage(text, this@ConversationActivity.conversation, Date(), this@ConversationActivity)
msg.send(this@ConversationActivity)
return msg
}
override fun onPostExecute(nMessage: NornMessage) {
// transmettre le message à toutes les activités
NornMessageDispatcher.send(nMessage)
Log.d(this::class.java.simpleName, nMessage.toString())
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
SendTask(this, text).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {

View file

@ -1,6 +1,5 @@
package xyz.johnny.norntalk
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
@ -11,40 +10,85 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ListView
import android.widget.Toast
import kotlinx.android.synthetic.main.content_main.*
import kotlinx.android.synthetic.main.qrcode_layout.*
import xyz.johnny.norntalk.database.NornDatabase
import xyz.johnny.norntalk.messages.NornMessage
import xyz.johnny.norntalk.messages.NornMessageDispatcher
import xyz.johnny.norntalk.security.QRCode
import xyz.johnny.norntalk.ui_components.BaseActivity
import xyz.johnny.norntalk.ui_components.NornListAdapter
import java.lang.ref.WeakReference
class MainActivity : BaseActivity() {
override val layout = R.layout.activity_main
companion object {
@SuppressLint("StaticFieldLeak")
override fun initialize() {
// créer la liste de conversations en parallèle
Log.d(this::class.java.simpleName, "creating conversation list")
object: AsyncTask<Context, Void, List<NornMessage>>() {
class MessagesTask(activity: MainActivity) : AsyncTask<Context, Void, List<NornMessage>>() {
val activity = WeakReference<MainActivity>(activity)
override fun doInBackground(vararg params: Context): List<NornMessage> {
val db = NornDatabase.getNornDatabase(this@MainActivity)
return db.messageDao().getLastMessages().map {NornMessage.fromDbMessage(it, this@MainActivity)}
val activity = this.activity.get()!!
val db = NornDatabase.getNornDatabase(activity)
return db.messageDao().getLastMessages().map { NornMessage.fromDbMessage(it, activity) }
}
override fun onPostExecute(result: List<NornMessage>) {
super.onPostExecute(result)
val container = fragment_container as ListView
container.adapter = NornListAdapter(result.toMutableList(), this@MainActivity)
val activity = this.activity.get()!!
val container = activity.findViewById<ListView>(R.id.fragment_container)
container.adapter = NornListAdapter(result.toMutableList(), activity)
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
class QRCodeTask(context: BaseActivity, val toast: Toast) : AsyncTask<Void, Void, Bitmap>() {
val context = WeakReference<BaseActivity>(context)
override fun doInBackground(vararg params: Void?): Bitmap? {
return QRCode.generate(this.context.get()!!)
}
override fun onPostExecute(qrcode: Bitmap?) {
super.onPostExecute(qrcode)
if (qrcode != null) {
this.toast.setText(R.string.qrcode_generated)
this.toast.show()
// création de la pop-up
val alertDialog = AlertDialog.Builder(this.context.get()!!).create()
alertDialog.setTitle(R.string.action_qrcode)
// ajouter le QR code généré
val qrcode_group = this.context.get()!!.findViewById<LinearLayout>(R.id.qrcode_group)
val qrDialog = alertDialog.layoutInflater.inflate(R.layout.qrcode_layout, qrcode_group)
qrDialog.findViewById<ImageView>(R.id.qrcode).setImageBitmap(qrcode)
alertDialog.setView(qrDialog)
// afficher la pop-up
alertDialog.show()
} else {
toast.setText(R.string.qrcode_error)
toast.show()
}
}
}
}
override val layout = R.layout.activity_main
override fun initialize() {
// créer la liste de conversations en parallèle
Log.d(this::class.java.simpleName, "creating conversation list")
MessagesTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
/**
@ -91,38 +135,7 @@ class MainActivity : BaseActivity() {
val toast = Toast.makeText(this, getString(R.string.generating_qrcode), Toast.LENGTH_SHORT)
toast.show()
object : AsyncTask<Void, Void, Bitmap>() {
override fun doInBackground(vararg params: Void?): Bitmap? {
return QRCode.generate(this@MainActivity)
}
override fun onPostExecute(qrcode: Bitmap?) {
super.onPostExecute(qrcode)
if (qrcode != null) {
toast.setText(R.string.qrcode_generated)
toast.show()
// création de la pop-up
val alertDialog = AlertDialog.Builder(this@MainActivity).create()
alertDialog.setTitle(R.string.action_qrcode)
// ajouter le QR code généré
val qrDialog = alertDialog.layoutInflater.inflate(R.layout.qrcode_layout, qrcode_group)
qrDialog.findViewById<ImageView>(R.id.qrcode).setImageBitmap(qrcode)
alertDialog.setView(qrDialog)
// afficher la pop-up
alertDialog.show()
} else {
toast.setText(R.string.qrcode_error)
toast.show()
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
QRCodeTask(this, toast).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
override fun onDestroy() {

View file

@ -12,6 +12,7 @@ import android.support.v7.app.AppCompatActivity
import com.google.zxing.BarcodeFormat
import xyz.johnny.norntalk.database.NornDatabase
import xyz.johnny.norntalk.messages.NornNotification
import xyz.johnny.norntalk.security.LoadHelper
import xyz.johnny.norntalk.security.Security
@ -22,24 +23,7 @@ import xyz.johnny.norntalk.security.Security
*/
class SplashActivity : AppCompatActivity() {
companion object {
/**
* Méthode permettant d'effectuer des opérations en parallèle
*
* @param function Fonction contenant l'opération à effectuer en parallèle
* @return Tâche correspondant à l'opération
*/
@SuppressLint("StaticFieldLeak")
fun load_helper(function: () -> Any?): AsyncTask<Void, Void, Unit> {
return object : AsyncTask<Void, Void, Unit>() {
override fun doInBackground(vararg params: Void?) {
function()
}
}
}
}
private var _tasks: Array<AsyncTask<Void, Void, Unit>>? = null
private var _tasks: Array<LoadHelper>? = null
/**
* Les tâches effectuées en arrière plan
*/
@ -72,13 +56,13 @@ class SplashActivity : AppCompatActivity() {
*
* @return Tâches correspondants aux opérations
*/
fun load(): Array<AsyncTask<Void, Void, Unit>> {
fun load(): Array<LoadHelper> {
val tasks = arrayOf(
load_helper { Security.random },
load_helper { Security.curve },
load_helper { NornNotification.createChannel(this) },
load_helper { NornDatabase.getNornDatabase(this) },
load_helper { Security.barcodeEncoder.encodeBitmap("a", BarcodeFormat.QR_CODE, 512, 512).recycle() }
LoadHelper { Security.random },
LoadHelper { Security.curve },
LoadHelper { NornNotification.createChannel(this) },
LoadHelper { NornDatabase.getNornDatabase(this) },
LoadHelper { Security.barcodeEncoder.encodeBitmap("a", BarcodeFormat.QR_CODE, 512, 512).recycle() }
)
tasks.forEach { it.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) }
@ -86,7 +70,6 @@ class SplashActivity : AppCompatActivity() {
return tasks
}
@SuppressLint("StaticFieldLeak")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -11,6 +11,7 @@ import android.os.AsyncTask
import android.os.AsyncTask.THREAD_POOL_EXECUTOR
import xyz.johnny.norntalk.database.dao.*
import xyz.johnny.norntalk.database.entities.*
import java.lang.ref.WeakReference
/**
* Base de données de Norn Talk
@ -41,23 +42,22 @@ abstract class NornDatabase : RoomDatabase() {
}
class BuildDBTask : AsyncTask<Context, Void, Unit>() {
override fun doInBackground(vararg params: Context) {
val dbBuilder = Room.databaseBuilder(params[0].applicationContext,
NornDatabase::class.java,
DB_NAME).fallbackToDestructiveMigration()
dbBuilder.addMigrations(migration2_3)
instance = dbBuilder.build()
}
}
/**
* Tâche permettant de créer la base de données
*/
private val task by lazy {
@SuppressLint("StaticFieldLeak")
object: AsyncTask<Context, Void, Unit>() {
override fun doInBackground(vararg params: Context) {
val dbBuilder = Room.databaseBuilder(params[0].applicationContext,
NornDatabase::class.java,
DB_NAME).fallbackToDestructiveMigration()
dbBuilder.addMigrations(migration2_3)
instance = dbBuilder.build()
}
}
}
private val task by lazy { BuildDBTask() }
/**
* Fonction permettant d'accéder à l'instance de la base de données
@ -87,6 +87,18 @@ abstract class NornDatabase : RoomDatabase() {
*/
class Helper(val context: Context) {
companion object {
class RunTask<T>(context: Context, val function: (db: NornDatabase) -> T) : AsyncTask<Void, Void, T>() {
val context = WeakReference<Context>(context)
override fun doInBackground(vararg params: Void?): T {
return function(getNornDatabase(this.context.get()!!))
}
}
}
/**
* Execute la fonction donnée dans une tâche effectuée en parallèle
*
@ -94,14 +106,10 @@ abstract class NornDatabase : RoomDatabase() {
* @param function Fonction acceptant un argument et retournant un objet de type T
* @return La tâche correspondante à l'opération, en cours d'execution
*/
@SuppressLint("StaticFieldLeak")
fun <T> run(function: (db: NornDatabase) -> T): AsyncTask<Void, Void, T> {
return object : AsyncTask<Void, Void, T>() {
override fun doInBackground(vararg params: Void?): T {
return function(getNornDatabase(context))
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
return RunTask<T>(context, function).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
}
/**

View file

@ -12,6 +12,7 @@ import android.widget.Toast
import xyz.johnny.norntalk.R
import xyz.johnny.norntalk.security.CheckNumber
import xyz.johnny.norntalk.security.QRCode
import java.lang.ref.WeakReference
import java.util.*
@ -20,6 +21,25 @@ import java.util.*
*/
class NornMessageReceiver : BroadcastReceiver() {
class DispatchTask(context: Context, val message: NornMessage) : AsyncTask<Void, Void, NornMessage>() {
val context = WeakReference<Context>(context)
override fun doInBackground(vararg params: Void): NornMessage {
message.insertMessage(this.context.get()!!).get()
return message
}
override fun onPostExecute(message: NornMessage) {
super.onPostExecute(message)
// envoyer une notification
message.sendNotification()
// Afficher le message dans les activités
NornMessageDispatcher.send(message)
}
}
override fun onReceive(context: Context, intent: Intent) {
// Récupérer les messages
val msgs = getMessagesFromIntent(intent)
@ -100,24 +120,7 @@ class NornMessageReceiver : BroadcastReceiver() {
}
// Transmettre le message au reste de l'application en parallèle
val task = @SuppressLint("StaticFieldLeak")
object : AsyncTask<Context, Void, NornMessage>() {
override fun doInBackground(vararg params: Context?): NornMessage {
message.insertMessage(context).get()
return message
}
override fun onPostExecute(message: NornMessage) {
super.onPostExecute(message)
// envoyer une notification
message.sendNotification()
// Afficher le message dans les activités
NornMessageDispatcher.send(message)
}
}
task.executeOnExecutor(THREAD_POOL_EXECUTOR, context)
DispatchTask(context, message).executeOnExecutor(THREAD_POOL_EXECUTOR)
}

View file

@ -0,0 +1,17 @@
package xyz.johnny.norntalk.security
import android.os.AsyncTask
/**
* Classe permettant d'effectuer des opérations en parallèle
*
* @param function Fonction contenant l'opération à effectuer en parallèle
* @return Tâche correspondant à l'opération
*/
class LoadHelper(val function: () -> Any?) : AsyncTask<Void, Void, Unit>() {
override fun doInBackground(vararg params: Void?) {
this.function()
}
}

View file

@ -23,6 +23,7 @@ import org.bouncycastle.util.encoders.Hex
import xyz.johnny.norntalk.R
import xyz.johnny.norntalk.messages.NornContact
import xyz.johnny.norntalk.messages.NornMessage
import java.lang.ref.WeakReference
import java.util.*
/**
@ -41,6 +42,34 @@ import java.util.*
*/
object QRCode {
class CheckNumberTask(context: Context, val tmpNumber: String, val numberObj: NumberObject, val alertDialog: AlertDialog) : AsyncTask<Void, Void, Boolean>() {
val context = WeakReference<Context>(context)
override fun doInBackground(vararg params: Void?): Boolean {
// vérifier le numéro de téléphone
return CheckNumber.checkNumber(tmpNumber, this.context.get()!!)
}
override fun onPostExecute(result: Boolean) {
super.onPostExecute(result)
val context = this.context.get()!!
if (result) {
// transmettre le numéro de téléphone
synchronized(numberObj) {
numberObj.number = NornContact.format(tmpNumber, context)
alertDialog.dismiss()
numberObj.notifyAll()
}
Toast.makeText(context, R.string.correct_number, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, R.string.wrong_number, Toast.LENGTH_SHORT).show()
}
}
}
/**
* Taille du token présent dans les QR codes
*/
@ -77,7 +106,6 @@ object QRCode {
* @param context Contexte courant
* @param numberObj Stocke le numéro de téléphone
*/
@SuppressLint("StaticFieldLeak")
private fun popUpNumber(context: Context, numberObj: NumberObject) {
// Création de la popup
val alertDialog = AlertDialog.Builder(context).create()
@ -115,29 +143,7 @@ object QRCode {
// récupérer la valeur du champs de texte
val tmpNumber = alertDialog.findViewById<EditText>(R.id.etNum)?.text.toString()
object : AsyncTask<Void, Void, Boolean>() {
override fun doInBackground(vararg params: Void?): Boolean {
// vérifier le numéro de téléphone
return CheckNumber.checkNumber(tmpNumber, context)
}
override fun onPostExecute(result: Boolean) {
super.onPostExecute(result)
if (result) {
// transmettre le numéro de téléphone
synchronized(numberObj) {
numberObj.number = NornContact.format(tmpNumber, context)
alertDialog.dismiss()
numberObj.notifyAll()
}
Toast.makeText(context, R.string.correct_number, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, R.string.wrong_number, Toast.LENGTH_SHORT).show()
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
CheckNumberTask(context, tmpNumber, numberObj, alertDialog).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
}
@ -220,7 +226,6 @@ object QRCode {
* @param context Contexte courant
* @return Bitmap du QR code ou null s'il est impossible de créer le QR code
*/
@SuppressLint("StaticFieldLeak")
fun generate(context: Context, auto: Boolean = false) : Bitmap? {
// trouver le numéro de l'utilisateur

View file

@ -17,6 +17,7 @@ import kotlinx.android.synthetic.main.passalert_layout.*
import org.bouncycastle.util.encoders.Hex
import xyz.johnny.norntalk.R
import xyz.johnny.norntalk.SplashActivity
import xyz.johnny.norntalk.security.LoadHelper
import xyz.johnny.norntalk.security.QRCode
import xyz.johnny.norntalk.security.Security
import java.security.MessageDigest
@ -94,7 +95,7 @@ abstract class BaseActivity: AppCompatActivity() {
currentActivity = this
if (Telephony.Sms.getDefaultSmsPackage(applicationContext) == applicationContext.packageName)
SplashActivity.load_helper { QRCode.getNumber(this, true) }
LoadHelper { QRCode.getNumber(this, true) }
super.onResume()
}