diff --git a/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt b/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt index d3e96f1..9130349 100644 --- a/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt +++ b/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt @@ -28,7 +28,7 @@ class ChatActivity : ServiceBoundActivity() { private lateinit var binding: ActivityChatBinding private var sessionId = -1 - private lateinit var sessionName: String + private var sessionName: String? = null private var avatar: ByteArray? = null private lateinit var chatAdapter: ChatAdapter private var lastLoadedMessageOffset = 0 @@ -47,155 +47,159 @@ class ChatActivity : ServiceBoundActivity() { sessionId = intent.getIntExtra("sessionId", -1) if (sessionId != -1) { - intent.getStringExtra("sessionName")?.let { name -> - sessionName = name - binding.toolbar.avatar.setTextAvatar(name) - binding.toolbar.title.text = name - chatAdapter = ChatAdapter(this@ChatActivity, ::onClickSaveFile) - binding.recyclerChat.apply { - adapter = chatAdapter - layoutManager = LinearLayoutManager(this@ChatActivity, LinearLayoutManager.VERTICAL, false).apply { - stackFromEnd = true - } - addOnScrollListener(object : RecyclerView.OnScrollListener() { - fun loadMsgsIfNeeded(recyclerView: RecyclerView) { - if (!recyclerView.canScrollVertically(-1) && isServiceInitialized()) { - airaService.contacts[sessionId]?.let { contact -> - loadMsgs(contact.uuid) - } + chatAdapter = ChatAdapter(this@ChatActivity, ::onClickSaveFile) + binding.recyclerChat.apply { + adapter = chatAdapter + layoutManager = LinearLayoutManager(this@ChatActivity, LinearLayoutManager.VERTICAL, false).apply { + stackFromEnd = true + } + addOnScrollListener(object : RecyclerView.OnScrollListener() { + fun loadMsgsIfNeeded(recyclerView: RecyclerView) { + if (!recyclerView.canScrollVertically(-1) && isServiceInitialized()) { + airaService.contacts[sessionId]?.let { contact -> + loadMsgs(contact.uuid) } } - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - loadMsgsIfNeeded(recyclerView) - } - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - loadMsgsIfNeeded(recyclerView) - } - }) + } + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + loadMsgsIfNeeded(recyclerView) + } + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + loadMsgsIfNeeded(recyclerView) + } + }) + } + binding.toolbar.toolbar.setOnClickListener { + showSessionInfo() + } + binding.buttonSend.setOnClickListener { + val msg = binding.editMessage.text.toString() + airaService.sendTo(sessionId, Protocol.newMessage(msg)) + binding.editMessage.text.clear() + chatAdapter.newMessage(ChatItem(true, Protocol.newMessage(msg))) + if (airaService.contacts.contains(sessionId)) { + lastLoadedMessageOffset += 1 } - binding.toolbar.toolbar.setOnClickListener { - showSessionInfo() - } - binding.buttonSend.setOnClickListener { - val msg = binding.editMessage.text.toString() - airaService.sendTo(sessionId, Protocol.newMessage(msg)) - binding.editMessage.text.clear() - chatAdapter.newMessage(ChatItem(true, Protocol.newMessage(msg))) - if (airaService.contacts.contains(sessionId)) { - lastLoadedMessageOffset += 1 + binding.recyclerChat.smoothScrollToPosition(chatAdapter.itemCount) + } + binding.buttonAttach.setOnClickListener { + filePicker.launch("*/*") + } + serviceConnection = object : ServiceConnection { + override fun onServiceConnected(componentName: ComponentName?, service: IBinder) { + val binder = service as AIRAService.AIRABinder + airaService = binder.getService() + + chatAdapter.clear() + val contact = airaService.contacts[sessionId] + val avatar = if (contact == null) { + sessionName = airaService.savedNames[sessionId] + airaService.savedAvatars[sessionId] + } else { + sessionName = contact.name + contact.avatar + } + binding.toolbar.title.text = sessionName ?: airaService.sessions[sessionId]!!.ip + if (avatar == null) { + binding.toolbar.avatar.setTextAvatar(sessionName) + } else { + AIRADatabase.loadAvatar(avatar)?.let { image -> + this@ChatActivity.avatar = image + binding.toolbar.avatar.setImageAvatar(image) + } + } + if (contact != null) { + displayIconTrustLevel(true, contact.verified) + loadMsgs(contact.uuid) + } + airaService.savedMsgs[sessionId]?.let { + for (chatItem in it.asReversed()) { + chatAdapter.newLoadedMessage(chatItem) + } + } + airaService.receiveFileTransfers[sessionId]?.let { + if (it.shouldAsk) { + it.ask(this@ChatActivity) + } } binding.recyclerChat.smoothScrollToPosition(chatAdapter.itemCount) - } - binding.buttonAttach.setOnClickListener { - filePicker.launch("*/*") - } - serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, service: IBinder) { - val binder = service as AIRAService.AIRABinder - airaService = binder.getService() - - chatAdapter.clear() - val contact = airaService.contacts[sessionId] - if (contact == null) { - airaService.savedAvatars[sessionId] - } else { - contact.avatar - }?.let { - AIRADatabase.loadAvatar(it)?.let { image -> - avatar = image - binding.toolbar.avatar.setImageAvatar(image) - } - } - if (contact != null) { - displayIconTrustLevel(true, contact.verified) - loadMsgs(contact.uuid) - } - airaService.savedMsgs[sessionId]?.let { - for (chatItem in it.asReversed()) { - chatAdapter.newLoadedMessage(chatItem) - } - } - airaService.receiveFileTransfers[sessionId]?.let { - if (it.shouldAsk) { - it.ask(this@ChatActivity, sessionName) - } - } - binding.recyclerChat.smoothScrollToPosition(chatAdapter.itemCount) - val showBottomPanel = { - binding.bottomPanel.visibility = View.VISIBLE - } - airaService.uiCallbacks = object : AIRAService.UiCallbacks { - override fun onNewSession(sessionId: Int, ip: String) { - if (this@ChatActivity.sessionId == sessionId) { - runOnUiThread { - showBottomPanel() - } - } - } - override fun onSessionDisconnect(sessionId: Int) { - if (this@ChatActivity.sessionId == sessionId) { - runOnUiThread { - val inputManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - inputManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0) - binding.bottomPanel.visibility = View.GONE - invalidateOptionsMenu() - } - } - } - override fun onNameTold(sessionId: Int, name: String) { - if (this@ChatActivity.sessionId == sessionId) { - runOnUiThread { - sessionName = name - binding.toolbar.title.text = name - } - } - } - override fun onAvatarChanged(sessionId: Int, avatar: ByteArray?) { - if (this@ChatActivity.sessionId == sessionId) { - runOnUiThread { - this@ChatActivity.avatar = avatar - if (avatar == null) { - binding.toolbar.avatar.setTextAvatar(sessionName) - } else { - binding.toolbar.avatar.setImageAvatar(avatar) - } - } - } - } - override fun onNewMessage(sessionId: Int, data: ByteArray): Boolean { - return if (this@ChatActivity.sessionId == sessionId) { - runOnUiThread { - chatAdapter.newMessage(ChatItem(false, data)) - binding.recyclerChat.smoothScrollToPosition(chatAdapter.itemCount) - } - if (airaService.contacts.contains(sessionId)) { - lastLoadedMessageOffset += 1 - } - !airaService.isAppInBackground - } else { - false - } - } - override fun onAskLargeFiles(sessionId: Int, name: String, filesReceiver: FilesReceiver): Boolean { - return if (this@ChatActivity.sessionId == sessionId) { - runOnUiThread { - filesReceiver.ask(this@ChatActivity, name) - } - true - } else { - false - } - } - } - airaService.isAppInBackground = false - if (airaService.isOnline(sessionId)) { - showBottomPanel() - binding.recyclerChat.updatePadding(bottom = 0) - } - airaService.setSeen(sessionId, true) + val showBottomPanel = { + binding.bottomPanel.visibility = View.VISIBLE } - override fun onServiceDisconnected(name: ComponentName?) {} + airaService.uiCallbacks = object : AIRAService.UiCallbacks { + override fun onNewSession(sessionId: Int, ip: String) { + if (this@ChatActivity.sessionId == sessionId) { + runOnUiThread { + showBottomPanel() + } + } + } + override fun onSessionDisconnect(sessionId: Int) { + if (this@ChatActivity.sessionId == sessionId) { + runOnUiThread { + val inputManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0) + binding.bottomPanel.visibility = View.GONE + invalidateOptionsMenu() + } + } + } + override fun onNameTold(sessionId: Int, name: String) { + if (this@ChatActivity.sessionId == sessionId) { + runOnUiThread { + sessionName = name + binding.toolbar.title.text = name + if (avatar == null) { + binding.toolbar.avatar.setTextAvatar(name) + } + } + } + } + override fun onAvatarChanged(sessionId: Int, avatar: ByteArray?) { + if (this@ChatActivity.sessionId == sessionId) { + runOnUiThread { + this@ChatActivity.avatar = avatar + if (avatar == null) { + binding.toolbar.avatar.setTextAvatar(sessionName) + } else { + binding.toolbar.avatar.setImageAvatar(avatar) + } + } + } + } + override fun onNewMessage(sessionId: Int, data: ByteArray): Boolean { + return if (this@ChatActivity.sessionId == sessionId) { + runOnUiThread { + chatAdapter.newMessage(ChatItem(false, data)) + binding.recyclerChat.smoothScrollToPosition(chatAdapter.itemCount) + } + if (airaService.contacts.contains(sessionId)) { + lastLoadedMessageOffset += 1 + } + !airaService.isAppInBackground + } else { + false + } + } + override fun onAskLargeFiles(sessionId: Int, filesReceiver: FilesReceiver): Boolean { + return if (this@ChatActivity.sessionId == sessionId) { + runOnUiThread { + filesReceiver.ask(this@ChatActivity) + } + true + } else { + false + } + } + } + airaService.isAppInBackground = false + if (airaService.isOnline(sessionId)) { + showBottomPanel() + binding.recyclerChat.updatePadding(bottom = 0) + } + airaService.setSeen(sessionId, true) } + override fun onServiceDisconnected(name: ComponentName?) {} } } } @@ -251,9 +255,13 @@ class ChatActivity : ServiceBoundActivity() { true } R.id.set_as_contact -> { - if (airaService.setAsContact(sessionId, sessionName)) { - invalidateOptionsMenu() - displayIconTrustLevel(true, false) + if (sessionName == null) { + Toast.makeText(this, R.string.no_name_error, Toast.LENGTH_SHORT).show() + } else { + if (airaService.setAsContact(sessionId, sessionName!!)) { + invalidateOptionsMenu() + displayIconTrustLevel(true, false) + } } true } diff --git a/app/src/main/java/sushi/hardcore/aira/MainActivity.kt b/app/src/main/java/sushi/hardcore/aira/MainActivity.kt index 26c4397..f8e3d38 100644 --- a/app/src/main/java/sushi/hardcore/aira/MainActivity.kt +++ b/app/src/main/java/sushi/hardcore/aira/MainActivity.kt @@ -71,9 +71,9 @@ class MainActivity : ServiceBoundActivity() { return false } - override fun onAskLargeFiles(sessionId: Int, name: String, filesReceiver: FilesReceiver): Boolean { + override fun onAskLargeFiles(sessionId: Int, filesReceiver: FilesReceiver): Boolean { runOnUiThread { - filesReceiver.ask(this@MainActivity, name) + filesReceiver.ask(this@MainActivity) } return true } @@ -322,7 +322,6 @@ class MainActivity : ServiceBoundActivity() { private fun launchChatActivity(session: Session) { startActivity(Intent(this, ChatActivity::class.java).apply { putExtra("sessionId", session.sessionId) - putExtra("sessionName", airaService.getNameOf(session.sessionId)) }) } diff --git a/app/src/main/java/sushi/hardcore/aira/adapters/SessionAdapter.kt b/app/src/main/java/sushi/hardcore/aira/adapters/SessionAdapter.kt index c0744eb..adec787 100644 --- a/app/src/main/java/sushi/hardcore/aira/adapters/SessionAdapter.kt +++ b/app/src/main/java/sushi/hardcore/aira/adapters/SessionAdapter.kt @@ -31,18 +31,16 @@ class SessionAdapter(private val activity: AppCompatActivity): BaseAdapter() { val view: View = convertView ?: inflater.inflate(R.layout.adapter_session, parent, false) val currentSession = getItem(position) view.findViewById(R.id.text_name).apply { - val avatarName = if (currentSession.name == null) { + setTextColor(if (currentSession.name == null) { text = currentSession.ip - setTextColor(Color.RED) - "?" + Color.RED } else { text = currentSession.name - setTextColor(Color.WHITE) - currentSession.name!! - } + Color.WHITE + }) val avatar = view.findViewById(R.id.avatar) if (currentSession.avatar == null) { - avatar.setTextAvatar(avatarName) + avatar.setTextAvatar(currentSession.name) } else { avatar.setImageAvatar(currentSession.avatar!!) } diff --git a/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt b/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt index 8f02046..eb9f1ba 100644 --- a/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt +++ b/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt @@ -108,7 +108,7 @@ class AIRAService : Service() { fun onNameTold(sessionId: Int, name: String) fun onAvatarChanged(sessionId: Int, avatar: ByteArray?) fun onNewMessage(sessionId: Int, data: ByteArray): Boolean - fun onAskLargeFiles(sessionId: Int, name: String, filesReceiver: FilesReceiver): Boolean + fun onAskLargeFiles(sessionId: Int, filesReceiver: FilesReceiver): Boolean } fun connectTo(ip: String) { @@ -188,7 +188,7 @@ class AIRAService : Service() { return sessions.contains(sessionId) } - fun getNameOf(sessionId: Int): String { + private fun getNameOf(sessionId: Int): String { return contacts[sessionId]?.name ?: savedNames[sessionId] ?: sessions[sessionId]!!.ip } @@ -343,11 +343,10 @@ class AIRAService : Service() { } private fun sendNotification(sessionId: Int, msgContent: ByteArray) { - val name = getNameOf(sessionId) val notificationBuilder = NotificationCompat.Builder(this, MESSAGES_NOTIFICATION_CHANNEL_ID) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setSmallIcon(R.drawable.ic_launcher) - .setContentTitle(name) + .setContentTitle(getNameOf(sessionId)) .setContentText( if (msgContent[0] == Protocol.MESSAGE) { msgContent.decodeToString(1) @@ -358,7 +357,6 @@ class AIRAService : Service() { .setContentIntent( PendingIntent.getActivity(this, 0, Intent(this, ChatActivity::class.java).apply { putExtra("sessionId", sessionId) - putExtra("sessionName", name) }, 0) ) .setAutoCancel(true) @@ -706,7 +704,7 @@ class AIRAService : Service() { Protocol.ASK_LARGE_FILES -> { if (!receiveFileTransfers.containsKey(sessionId) && !sendFileTransfers.containsKey(sessionId)) { Protocol.parseAskFiles(buffer)?.let { files -> - val name = getNameOf(sessionId) + val sessionName = getNameOf(sessionId) val filesReceiver = FilesReceiver( files, { filesReceiver -> @@ -723,12 +721,12 @@ class AIRAService : Service() { }, this, notificationManager, - name + sessionName ) receiveFileTransfers[sessionId] = filesReceiver var shouldSendNotification = true if (!isAppInBackground) { - if (uiCallbacks?.onAskLargeFiles(sessionId, name, filesReceiver) == true) { + if (uiCallbacks?.onAskLargeFiles(sessionId, filesReceiver) == true) { shouldSendNotification = false } } @@ -737,12 +735,11 @@ class AIRAService : Service() { .setCategory(NotificationCompat.CATEGORY_EVENT) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle(getString(R.string.download_file_request)) - .setContentText(getString(R.string.want_to_send_files, name)) + .setContentText(getString(R.string.want_to_send_files, sessionName)) .setOngoing(true) //not cancelable .setContentIntent( PendingIntent.getActivity(this, 0, Intent(this, ChatActivity::class.java).apply { putExtra("sessionId", sessionId) - putExtra("sessionName", name) }, 0) ) .setDefaults(Notification.DEFAULT_ALL) diff --git a/app/src/main/java/sushi/hardcore/aira/background_service/FilesReceiver.kt b/app/src/main/java/sushi/hardcore/aira/background_service/FilesReceiver.kt index 6d5beac..b15f6b0 100644 --- a/app/src/main/java/sushi/hardcore/aira/background_service/FilesReceiver.kt +++ b/app/src/main/java/sushi/hardcore/aira/background_service/FilesReceiver.kt @@ -15,14 +15,14 @@ class FilesReceiver( private val onAborted: (FilesReceiver) -> Unit, context: Context, notificationManager: NotificationManagerCompat, - sessionName: String + private val sessionName: String ): FilesTransfer(context, notificationManager, sessionName) { var shouldAsk = true @SuppressLint("SetTextI18n") - fun ask(activity: AppCompatActivity, senderName: String) { + fun ask(activity: AppCompatActivity) { val dialogBinding = DialogAskFileBinding.inflate(activity.layoutInflater) - dialogBinding.textTitle.text = activity.getString(R.string.want_to_send_files, senderName)+':' + dialogBinding.textTitle.text = activity.getString(R.string.want_to_send_files, sessionName)+':' val filesInfo = StringBuilder() for (file in files) { filesInfo.appendLine(file.fileName+" ("+FileUtils.formatSize(file.fileSize)+')') diff --git a/app/src/main/java/sushi/hardcore/aira/widgets/Avatar.kt b/app/src/main/java/sushi/hardcore/aira/widgets/Avatar.kt index 6305a40..00d3f60 100644 --- a/app/src/main/java/sushi/hardcore/aira/widgets/Avatar.kt +++ b/app/src/main/java/sushi/hardcore/aira/widgets/Avatar.kt @@ -33,12 +33,15 @@ class Avatar @JvmOverloads constructor( } } - fun setTextAvatar(name: String) { - if (name.isNotEmpty()) { - binding.textLetter.text = name[0].toString() - binding.imageAvatar.visibility = View.GONE - binding.textAvatar.visibility = View.VISIBLE + fun setTextAvatar(name: String?) { + val char = if (name == null || name.isEmpty()) { + '?' + } else { + name[0] } + binding.textLetter.text = char.toString() + binding.imageAvatar.visibility = View.GONE + binding.textAvatar.visibility = View.VISIBLE } fun setImageAvatar(avatar: ByteArray) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0683d5b..1d8c255 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,6 +93,7 @@ Remove Choose avatar Send a messageā€¦ + This session has no name ! Send file