AIRA-android/app/src/main/java/sushi/hardcore/aira/MainActivity.kt

361 lines
15 KiB
Kotlin
Raw Normal View History

2021-01-26 19:45:18 +01:00
package sushi.hardcore.aira
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
import android.provider.OpenableColumns
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.AbsListView
2021-01-26 19:45:18 +01:00
import android.widget.AdapterView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import sushi.hardcore.aira.adapters.Session
import sushi.hardcore.aira.adapters.SessionAdapter
import sushi.hardcore.aira.background_service.AIRAService
import sushi.hardcore.aira.background_service.ReceiveFileTransfer
import sushi.hardcore.aira.databinding.ActivityMainBinding
import sushi.hardcore.aira.databinding.DialogIpAddressesBinding
2021-01-26 19:45:18 +01:00
import sushi.hardcore.aira.utils.FileUtils
import sushi.hardcore.aira.utils.StringUtils
import java.lang.StringBuilder
import java.net.NetworkInterface
2021-01-26 19:45:18 +01:00
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var airaService: AIRAService
private lateinit var onlineSessionAdapter: SessionAdapter
private var offlineSessionAdapter: SessionAdapter? = null
private val onSessionsItemClickSendFile = AdapterView.OnItemClickListener { adapter, _, position, _ ->
askShareFileTo(adapter.getItemAtPosition(position) as Session)
}
private val onSessionsScrollListener = object : AbsListView.OnScrollListener {
override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) {}
override fun onScroll(listView: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
if (listView.getChildAt(0) != null) {
binding.refresher.isEnabled = listView.firstVisiblePosition == 0 && listView.getChildAt(0).top == 0
}
}
}
2021-01-26 19:45:18 +01:00
private val uiCallbacks = object : AIRAService.UiCallbacks {
override fun onNewSession(sessionId: Int, ip: String) {
runOnUiThread {
handleNewSession(sessionId, ip)
}
}
override fun onSessionDisconnect(sessionId: Int) {
runOnUiThread {
onlineSessionAdapter.remove(sessionId)?.let { session ->
if (session.isContact) {
offlineSessionAdapter?.add(session)
2021-01-26 19:45:18 +01:00
}
}
}
}
override fun onNameTold(sessionId: Int, name: String) {
runOnUiThread {
onlineSessionAdapter.setName(sessionId, name)
}
}
override fun onNewMessage(sessionId: Int, data: ByteArray): Boolean {
runOnUiThread {
onlineSessionAdapter.setSeen(sessionId, false)
}
return false
}
override fun onAskLargeFile(sessionId: Int, name: String, receiveFileTransfer: ReceiveFileTransfer): Boolean {
runOnUiThread {
receiveFileTransfer.ask(this@MainActivity, name)
}
return true
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val identityName = intent.getStringExtra("identityName")
identityName?.let { title = it }
val openedToShareFile = intent.action == Intent.ACTION_SEND
onlineSessionAdapter = SessionAdapter(this)
binding.onlineSessions.apply {
adapter = onlineSessionAdapter
onItemClickListener = if (openedToShareFile) {
onSessionsItemClickSendFile
2021-01-26 19:45:18 +01:00
} else {
2021-05-05 20:54:25 +02:00
AdapterView.OnItemClickListener { _, _, position, _ ->
if (isSelecting()) {
changeSelection(onlineSessionAdapter, position)
} else {
launchChatActivity(onlineSessionAdapter.getItem(position))
}
}
2021-01-26 19:45:18 +01:00
}
2021-05-05 20:54:25 +02:00
onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, position, _ ->
changeSelection(onlineSessionAdapter, position)
true
}
setOnScrollListener(onSessionsScrollListener)
2021-01-26 19:45:18 +01:00
}
if (openedToShareFile) {
binding.offlineSessions.visibility = View.GONE
binding.textOfflineSessions.visibility = View.GONE
} else {
offlineSessionAdapter = SessionAdapter(this)
binding.offlineSessions.apply {
adapter = offlineSessionAdapter
onItemClickListener = if (openedToShareFile) {
onSessionsItemClickSendFile
} else {
2021-05-05 20:54:25 +02:00
AdapterView.OnItemClickListener { _, _, position, _ ->
if (isSelecting()) {
changeSelection(offlineSessionAdapter!!, position)
} else {
launchChatActivity(offlineSessionAdapter!!.getItem(position))
}
}
}
onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, position, _ ->
changeSelection(offlineSessionAdapter!!, position)
true
2021-01-26 19:45:18 +01:00
}
setOnScrollListener(onSessionsScrollListener)
}
2021-01-26 19:45:18 +01:00
}
Intent(this, AIRAService::class.java).also { serviceIntent ->
bindService(serviceIntent, object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder) {
val binder = service as AIRAService.AIRABinder
airaService = binder.getService()
airaService.uiCallbacks = uiCallbacks
airaService.isAppInBackground = false
loadContacts()
if (AIRAService.isServiceRunning) {
title = airaService.identityName
loadSessions()
} else {
airaService.identityName = identityName
startService(serviceIntent)
}
binding.refresher.setOnRefreshListener {
airaService.restartDiscovery()
binding.refresher.isRefreshing = false
}
}
override fun onServiceDisconnected(name: ComponentName?) {}
}, Context.BIND_AUTO_CREATE)
}
binding.buttonShowIp.setOnClickListener {
val ipAddresses = StringBuilder()
for (iface in NetworkInterface.getNetworkInterfaces()) {
for (addr in iface.inetAddresses) {
if (!addr.isLoopbackAddress) {
ipAddresses.appendLine(StringUtils.getIpFromInetAddress(addr)+" ("+iface.displayName+')')
}
}
}
val dialogBinding = DialogIpAddressesBinding.inflate(layoutInflater)
dialogBinding.textIpAddresses.text = ipAddresses.substring(0, ipAddresses.length-1) //remove last LF
AlertDialog.Builder(this)
.setTitle(R.string.your_addresses)
.setView(dialogBinding.root)
.setPositiveButton(R.string.ok, null)
.show()
}
2021-01-26 19:45:18 +01:00
binding.editPeerIp.setOnEditorActionListener { _, _, _ ->
if (::airaService.isInitialized){
airaService.connectTo(binding.editPeerIp.text.toString())
}
binding.editPeerIp.text.clear()
true
}
}
2021-05-05 20:54:25 +02:00
override fun onCreateOptionsMenu(menu: Menu): Boolean {
2021-01-26 19:45:18 +01:00
menuInflater.inflate(R.menu.main_activity, menu)
2021-05-05 20:54:25 +02:00
menu.findItem(R.id.remove_contact).isVisible = isSelecting()
2021-01-26 19:45:18 +01:00
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.settings -> {
startActivity(Intent(this, SettingsActivity::class.java))
true
}
R.id.close -> {
if (::airaService.isInitialized) {
AlertDialog.Builder(this)
.setTitle(R.string.warning)
.setMessage(R.string.ask_log_out)
.setPositiveButton(R.string.yes) { _, _ ->
airaService.logOut()
if (AIRADatabase.isIdentityProtected(Constants.getDatabaseFolder(this))) {
startActivity(Intent(this, LoginActivity::class.java))
}
finish()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
true
}
2021-05-05 20:54:25 +02:00
R.id.remove_contact -> {
AlertDialog.Builder(this)
.setTitle(R.string.warning)
.setMessage(R.string.ask_remove_contacts)
.setPositiveButton(R.string.delete) { _, _ ->
Thread {
for (sessionId in onlineSessionAdapter.getSelectedSessionIds()) {
airaService.removeContact(sessionId)
}
offlineSessionAdapter?.let {
for (sessionId in it.getSelectedSessionIds()) {
airaService.removeContact(sessionId)
}
}
runOnUiThread {
unSelectAll()
refreshSessions()
}
}.start()
}
.setNegativeButton(R.string.cancel, null)
.show()
true
}
2021-01-26 19:45:18 +01:00
else -> super.onOptionsItemSelected(item)
}
}
override fun onPause() {
super.onPause()
if (::airaService.isInitialized) {
airaService.isAppInBackground = true
}
}
override fun onResume() {
super.onResume()
if (::airaService.isInitialized) {
if (AIRAService.isServiceRunning) {
airaService.isAppInBackground = false
airaService.uiCallbacks = uiCallbacks //restoring callbacks
2021-05-05 20:54:25 +02:00
refreshSessions()
2021-01-26 19:45:18 +01:00
title = airaService.identityName
} else {
finish()
}
}
}
2021-05-05 20:54:25 +02:00
override fun onBackPressed() {
if (isSelecting()) {
unSelectAll()
} else {
super.onBackPressed()
}
}
private fun refreshSessions() {
onlineSessionAdapter.reset()
offlineSessionAdapter?.reset()
loadContacts()
loadSessions()
}
private fun unSelectAll() {
onlineSessionAdapter.unSelectAll()
offlineSessionAdapter?.unSelectAll()
invalidateOptionsMenu()
}
private fun changeSelection(adapter: SessionAdapter, position: Int) {
val wasSelecting = adapter.selectedItems.isNotEmpty()
adapter.onSelectionChanged(position)
val isSelecting = adapter.selectedItems.isNotEmpty()
if (wasSelecting != isSelecting) {
invalidateOptionsMenu()
}
}
private fun isSelecting(): Boolean {
return onlineSessionAdapter.selectedItems.isNotEmpty() || offlineSessionAdapter?.selectedItems?.isNotEmpty() == true
}
2021-01-26 19:45:18 +01:00
private fun loadContacts() {
if (offlineSessionAdapter != null) {
for ((sessionId, contact) in airaService.contacts) {
offlineSessionAdapter!!.add(Session(sessionId, true, contact.verified, contact.seen, null, contact.name))
}
2021-01-26 19:45:18 +01:00
}
}
private fun loadSessions() {
for ((sessionId, session) in airaService.sessions) {
handleNewSession(sessionId, session.ip)
}
}
private fun handleNewSession(sessionId: Int, ip: String) {
val seen = !airaService.notSeen.contains(sessionId)
val contact = airaService.contacts[sessionId]
if (contact == null) {
onlineSessionAdapter.add(Session(sessionId, false, false, seen, ip, airaService.savedNames[sessionId]))
} else {
onlineSessionAdapter.add(Session(sessionId, true, contact.verified, seen, ip, contact.name))
offlineSessionAdapter?.remove(sessionId)
2021-01-26 19:45:18 +01:00
}
}
private fun launchChatActivity(session: Session) {
startActivity(Intent(this, ChatActivity::class.java).apply {
putExtra("sessionId", session.sessionId)
putExtra("sessionName", session.name)
})
}
private fun askShareFileTo(session: Session) {
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
if (uri == null) {
Toast.makeText(this, R.string.share_uri_null, Toast.LENGTH_SHORT).show()
} else {
val cursor = contentResolver.query(uri, null, null, null, null)
if (cursor == null) {
Toast.makeText(this, R.string.file_open_failed, Toast.LENGTH_SHORT).show()
} else {
if (cursor.moveToFirst()) {
val fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
val fileSize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE))
val inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) {
Toast.makeText(this, R.string.file_open_failed, Toast.LENGTH_SHORT).show()
} else {
AlertDialog.Builder(this)
.setTitle(R.string.warning)
.setMessage(getString(R.string.ask_send_file, fileName, FileUtils.formatSize(fileSize), session.name ?: session.ip))
.setPositiveButton(R.string.yes) { _, _ ->
airaService.sendFileTo(session.sessionId, fileName, fileSize, inputStream)
finish()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}
cursor.close()
}
}
}
}