diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index c9ab7ca..a3378ad 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/build.gradle b/app/build.gradle index 83e0f6a..9835795 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,5 +46,6 @@ dependencies { kapt "android.arch.lifecycle:compiler:1.1.1" // Test helpers for LiveData testImplementation "android.arch.core:core-testing:1.1.1" + // For Bubbles that contain messages in MessageListActivity; remove when I create my own bubble implementation 'com.github.lguipeng:BubbleView:1.0.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e4e2a8..1106d15 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,7 +43,6 @@ - \ No newline at end of file diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/Conversation.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/Conversation.kt index c54383f..9393e67 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/Conversation.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/Conversation.kt @@ -1,5 +1,6 @@ package com.example.markmoussa.meshchatapplication +import com.hypelabs.hype.Instance import java.util.* /** @@ -8,4 +9,4 @@ import java.util.* // This version with a constructor used simply so we can generate dummy data to populate conversations -class Conversation(val user: User?, val timeStamp: Date?, val messageList: Store?, val currentlyOnline: Boolean) \ No newline at end of file +class Conversation(val user: User?, val timeStamp: Date?, val messageList: Store?, val instance: Instance?, val currentlyOnline: Boolean) \ No newline at end of file diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListActivity.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListActivity.kt index a1aac6a..abf75cc 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListActivity.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListActivity.kt @@ -4,11 +4,11 @@ package com.example.markmoussa.meshchatapplication * Created by markmoussa on 2/24/18. */ -import android.arch.lifecycle.Lifecycle import android.arch.lifecycle.LifecycleObserver -import android.arch.lifecycle.OnLifecycleEvent import android.arch.lifecycle.ProcessLifecycleOwner +import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.support.v4.widget.SwipeRefreshLayout @@ -25,7 +25,7 @@ import com.hypelabs.hype.Message class ConversationListActivity : AppCompatActivity(), Store.Delegate, LifecycleObserver { var mConversationList: MutableList = mutableListOf() - lateinit var lifeCycleObserver: LifecycleObserverActivity + private lateinit var lifeCycleObserver: LifecycleObserverActivity override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,13 +33,14 @@ class ConversationListActivity : AppCompatActivity(), Store.Delegate, LifecycleO val hypeFramework = applicationContext as HypeLifeCycle + // This tells us ProcessLifecycleOwner.get().lifecycle.addObserver(LifecycleObserverActivity(this).also { lifeCycleObserver = it}) populateConversationList() var mConversationListRecycler: RecyclerView? = null var mConversationListAdapter: ConversationListAdapter? = null - mConversationListRecycler = findViewById(R.id.reyclerview_conversation_list) + mConversationListRecycler = findViewById(R.id.reyclerview_conversation_list) mConversationListRecycler!!.layoutManager = LinearLayoutManager(this) mConversationListAdapter = ConversationListAdapter(this, mConversationList) mConversationListRecycler.adapter = mConversationListAdapter @@ -49,13 +50,13 @@ class ConversationListActivity : AppCompatActivity(), Store.Delegate, LifecycleO ItemClickSupport.addTo(mConversationListRecycler).setOnItemClickListener(object : ItemClickSupport.OnItemClickListener { override fun onItemClicked(recyclerView: RecyclerView, position: Int, v: View) { // when item (conversation) is clicked, go to that conversation's message list - Log.i("TEST: ", "This is a test, item number " + position.toString() + " picked.") +// Log.i("TEST: ", "This is a test, item number " + position.toString() + " picked.") val userIdentifier = mConversationList[position].user?.userIdentifier val contactStore = hypeFramework.getAllMessages()[userIdentifier] contactStore?.delegate = this@ConversationListActivity val intent = Intent(this@ConversationListActivity, MessageListActivity::class.java) intent.putExtra("userIdentifier", userIdentifier) - if(userIdentifier in hypeFramework.getAllOnlinePeers()) { + if(userIdentifier in hypeFramework.getOnlinePeers()) { intent.putExtra("online", true) } else { intent.putExtra("online", false) @@ -96,43 +97,60 @@ class ConversationListActivity : AppCompatActivity(), Store.Delegate, LifecycleO } } - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_conversation_list, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when(item!!.itemId) { - R.id.newMessageMenuButton -> { - val intent = Intent(this, NewMessageActivity::class.java) - startActivity(intent) - return true - } - else -> return super.onOptionsItemSelected(item) +// override fun onCreateOptionsMenu(menu: Menu?): Boolean { +// menuInflater.inflate(R.menu.menu_conversation_list, menu) +// return true +// } +// +// override fun onOptionsItemSelected(item: MenuItem?): Boolean { +// when(item!!.itemId) { +// R.id.newMessageMenuButton -> { +// val intent = Intent(this, NewMessageActivity::class.java) +// startActivity(intent) +// return true +// } +// else -> return super.onOptionsItemSelected(item) +// } +// } + + // This function initializes conversation list for the first time. + // Making separate function because this one returns the list, populateConversationList + // modifies it (so that the changes can be detected by the adapter) + private fun initializeConversationList(): MutableList { + val hypeFramework = applicationContext as HypeLifeCycle + val messageList = hypeFramework.getAllMessages() + val contactsList = hypeFramework.getAllContacts() + val onlinePeers = hypeFramework.getOnlinePeers() + val conversationList = mutableListOf() + for(x in messageList) { + conversationList.add(Conversation(contactsList[x.key], null, x.value, if(contactsList.containsKey(x.key)) onlinePeers[x.key] else null, onlinePeers.containsKey(x.key))) } + return conversationList } - private fun populateConversationList() { // creating conversations list mConversationList.clear() val hypeFramework = applicationContext as HypeLifeCycle var currentlyOnline: Boolean + val contactsList = hypeFramework.getAllContacts() + val onlinePeers = hypeFramework.getOnlinePeers() + Log.d("ConversationListActivit", "Length of hypeFramework.getAllMessages(): ${hypeFramework.getAllMessages().size}") for(x in hypeFramework.getAllMessages()) { - currentlyOnline = x.key in hypeFramework.getAllOnlinePeers() - val nickname: String? - if(x.key in hypeFramework.getAllContacts()) { - // I get the actual user here. Consider just passing that into the conversation instead of - // creating a brand new one - nickname = hypeFramework.getAllContacts()[x.key]!!.nickname - Log.d("ConversationListActivit", "Nickname is: $nickname") - } else { - nickname = null - } - mConversationList.add(Conversation(User(nickname, null, x.key), null, x.value, currentlyOnline)) + currentlyOnline = onlinePeers.containsKey(x.key) +// val nickname: String? +// if(contactsList.containsKey(x.key)) { +// // I get the actual user here. Consider just passing that into the conversation instead of +// // creating a brand new one +// nickname = contactsList[x.key]!!.nickname +//// Log.d("ConversationListActivit", "Nickname is: $nickname") +// } else { +// nickname = null +// } + mConversationList.add(Conversation(contactsList[x.key], null, x.value, if(currentlyOnline) onlinePeers[x.key] else null, currentlyOnline)) } // Debugging - Log.d("ConversationListActivit", "populateConversationList() returned: ") +// Log.d("ConversationListActivit", "populateConversationList() returned: ") for(x in mConversationList) { Log.d("ConversationListActivit", "User nickname: ${x.user?.nickname}, User Identifier: ${x.user?.userIdentifier}, user online: ${x.currentlyOnline}") } @@ -142,7 +160,7 @@ class ConversationListActivity : AppCompatActivity(), Store.Delegate, LifecycleO // TODO: Add option to delete conversation - override fun onMessageAdded(store: Store, message: Message) { + override fun onMessageAdded(store: Store, message: Pair) { updateInterface() } @@ -160,6 +178,19 @@ class ConversationListActivity : AppCompatActivity(), Store.Delegate, LifecycleO } } + override fun onBackPressed() { + val sharedPreferences: SharedPreferences = applicationContext.getSharedPreferences("sp", Context.MODE_PRIVATE) + if(sharedPreferences.contains("USERNAME") && sharedPreferences.getString("USERNAME", null).isNotEmpty()) { + val intent = Intent(this, MainActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + intent.putExtra("Exit me", true) + startActivity(intent) + finish() + } else { + super.onBackPressed() + } + } + } diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListAdapter.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListAdapter.kt index 9a7c8c2..2a83c27 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListAdapter.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/ConversationListAdapter.kt @@ -2,6 +2,7 @@ package com.example.markmoussa.meshchatapplication import android.content.Context import android.support.v7.widget.RecyclerView +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -57,8 +58,13 @@ class ConversationListAdapter(private val mContext: Context, private val mConver } else { usernameText.text = conversation.user.userIdentifier.toString() } - if(conversation.messageList != null) { - messagePreviewText.text = conversation.messageList.getMessageStringAtIndex(conversation.messageList.lastReadIndex) + if(!(conversation.messageList?.getMessages()!!.isEmpty())) { + + var messagePreview = conversation.messageList.getMessageStringAtIndex(conversation.messageList.getMessages().size - 1) + if(!(messagePreview.isNullOrEmpty()) && messagePreview!!.length > 40) { + messagePreview = messagePreview.substring(0, 33) + "..." + } + messagePreviewText.text = messagePreview } else { messagePreviewText.text = "" } @@ -66,11 +72,11 @@ class ConversationListAdapter(private val mContext: Context, private val mConver //timeText.text = DateUtils.formatDateTime(message.getCreatedAt(), HOUR_IN_MILLIS, FORMAT_SHOW_TIME) timestampText.text = Date().toString() // TODO: Figure out how to pull profile pic, or if not, then what profile pic should be (if there should be one at all) - if(conversation.user.profileUrl != null) { + if(conversation.user.profileUri != null) { // do nothing } val hypeFramework = mContext.applicationContext as HypeLifeCycle - if(conversation.user.userIdentifier in hypeFramework.getAllOnlinePeers()) { + if(conversation.user.userIdentifier in hypeFramework.getOnlinePeers()) { onlineStatusIcon.setImageResource(android.R.drawable.presence_online) } else { onlineStatusIcon.setImageResource(android.R.drawable.presence_offline) diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/HypeLifeCycle.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/HypeLifeCycle.kt index f3ffef2..8693d36 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/HypeLifeCycle.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/HypeLifeCycle.kt @@ -7,10 +7,6 @@ package com.example.markmoussa.meshchatapplication import android.app.Application import android.content.Context import android.content.SharedPreferences -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.net.Uri -import android.util.Base64 import android.util.Log import com.hypelabs.hype.Error import com.hypelabs.hype.Hype @@ -20,10 +16,7 @@ import com.hypelabs.hype.MessageInfo import com.hypelabs.hype.MessageObserver import com.hypelabs.hype.NetworkObserver import com.hypelabs.hype.StateObserver -import org.json.JSONObject import java.io.* -import java.nio.charset.Charset -import java.util.* import kotlin.collections.HashMap import kotlin.properties.Delegates @@ -31,15 +24,16 @@ import kotlin.properties.Delegates class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Application() { // TODO: DO THIS SECOND - Figure out why these observables aren't working sometimes (such as messageDatabase in setMessageDatabase() - // The onlinePeers object keeps track of message storage associated with each instance (peer) - private var onlinePeers: MutableList by Delegates.observable(mutableListOf()) { - _, _, _ -> updateOnlinePeersFile() - } + // TODO: Consider merging messageDatabase and contactsDatabase (can do HashMap>) + // The onlinePeers object keeps track of message storage associated with each instance (peer) via their userIdentifiers + private var onlinePeers: HashMap = hashMapOf() + // The messageDatabase keeps track of the users and their previous messages via saving their userIdentifiers and a Store private var messageDatabase: HashMap by Delegates.observable(hashMapOf()) { _, _, _ -> updateMessageDatabase() } + // contactsDatabase saves previously before seen users to save sending data back and forth via their userIdentifiers and User objects private var contactsDatabase: HashMap by Delegates.observable(hashMapOf()) { _, _, _ -> updateContactsDatabase() } @@ -56,7 +50,15 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati // Generate an app identifier in the HypeLabs dashboard (https://hypelabs.io/apps/), // by creating a new app. Copy the given identifier here. + + // ChatApplication Hype.setAppIdentifier("f370ac17") + + // MeshNetworkApplication + // Hype.setAppIdentifier("9a96baaa") + + // MeshNetworkApp2 + Hype.setAppIdentifier("b056a7af") val sharedPreferences: SharedPreferences = applicationContext.getSharedPreferences("sp", Context.MODE_PRIVATE) val userIdentifier = sharedPreferences.getInt("USER_IDENTIFIER", Hype.DefaultUserIdentifier) Hype.setUserIdentifier(userIdentifier) @@ -70,10 +72,14 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati } override fun onHypeStart() { + + // TODO: When you stop and start Hype again, for some reason the other users can't find you again + // it looks like this is due to Hype not having time to "lose" the instance before refinding it + Log.i(TAG, "Hype started!") Log.i(TAG, "Loading store from file") - readOnlinePeers() - readMessageDatabase() + messageDatabase = readMessageDatabase() + contactsDatabase = readContactsDatabase() } override fun onHypeStop(error: Error?) { @@ -85,7 +91,7 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati } Log.i(TAG, String.format("Hype stopped [%s]", description)) - readOnlinePeers() + onlinePeers.clear() } override fun onHypeFailedStarting(error: Error) { @@ -144,10 +150,10 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati var store = getAllMessages()[instance.userIdentifier] if(store == null) { - store = Store(instance) + store = Store(instance.userIdentifier) } // Storing the message triggers a reload update in the MessageList activity - store.add(message, this) + store.add(Pair(message.data.toString(charset("UTF-8")), false), this) setMessageDatabase(instance.userIdentifier, store) } @@ -175,7 +181,14 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati override fun onHypeRequestAccessToken(i: Int): String { // Access the app settings (https://hypelabs.io/apps/) to find an access token to use here. - return "903cbdd53f59e2f771cbf2a9429c91" + // This one is for chatApplication +// return "903cbdd53f59e2f771cbf2a9429c91" + + // MeshNetworkApplication +// return "4e5936b294a88cf2" + + // MeshNetworkApp2 + return "4e5936b294a88cf2" } override fun onCreate() { @@ -189,14 +202,27 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati // setting onlinePeers to read file here instead of at the top because readOnlinePeers() needs // dirPath in order to proceed, and dirPath is still null when onlinePeers is instantiated - onlinePeers = readOnlinePeers() - messageDatabase = readMessageDatabase() +// onlinePeers = readOnlinePeers() +// messageDatabase = readMessageDatabase() +// contactsDatabase = readContactsDatabase() } - fun getAllOnlinePeers(): MutableList { + fun getOnlinePeers(): HashMap { return onlinePeers } + // adds online peer to onlinePeers and triggers updating online peers file in memory + private fun setOnlinePeers(userIdentifier: Long, instance: Instance, addOrRemove: Boolean) { + // true = add, false = remove + if(addOrRemove) { + if(!(onlinePeers.contains(userIdentifier))) { + onlinePeers[userIdentifier] = instance + } + } else { + onlinePeers.remove(userIdentifier) + } + } + fun getAllMessages(): HashMap { return messageDatabase } @@ -209,17 +235,7 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati this.messageDatabase = messageDatabase } - // adds online peer to onlinePeers and triggers updating online peers file in memory - private fun setAllOnlinePeers(userIdentifier: Long, addOrRemove: Boolean) { - // true = add, false = remove - if(addOrRemove) { - if(!(onlinePeers.contains(userIdentifier))) { - onlinePeers.add(userIdentifier) - } - } else { - onlinePeers.remove(userIdentifier) - } - } + // adds message to store and triggers updating store file in memory fun setMessageDatabase(userIdentifier: Long, store: Store) { @@ -228,58 +244,35 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati // TODO: Figure out why and delete manual call once fixed updateMessageDatabase() // debugging - Log.d("HypeLifeCycle ", "new messageDatabase (from file) is: ${messageDatabase.entries.toString()}") + Log.d("HypeLifeCycle ", "new messageDatabase (from variable) is: ${messageDatabase.entries.toString()}") for(x in messageDatabase.values) { for(y in x.getMessages()) { - Log.d("HypeLifeCycle", "Store contents (from new messageDatabase (from file)): ${y.data.toString(charset("UTF-8"))}") + Log.d("HypeLifeCycle", "Store contents (from new messageDatabase (from variable)): ${y.first}") } } } // adds contact to contactsDatabase and triggers updating the contacts file in memory fun setContactsDatabase(userIdentifier: Long, user: User) { - contactsDatabase[userIdentifier] = user - } + // TODO: Hackish fix since I can't send over userIdentifier without the bytes going over its limit + // Fix once Hype's SDK allows for greater than 255 bytes sent on announcement + val newUser = User(user.nickname, user.profileUri, userIdentifier) + contactsDatabase[userIdentifier] = newUser - private fun readOnlinePeers(): MutableList { - val storeFile = File(dirPath, "storeFile") - if(!(storeFile.exists()) || storeFile.length() == 0.toLong()) { - storeFile.createNewFile() - return mutableListOf() - } else { - try { - val fis = FileInputStream(storeFile) - val ois = ObjectInputStream(fis) - val result: MutableList = ois.readObject() as MutableList - ois.close() - Log.d("HypeLifeCycle: ", "readOnlinePeers() returned: " + result.toString()) - return result - } catch (e: Exception) { - e.printStackTrace() - } - } - return mutableListOf() - } - - fun updateOnlinePeersFile() { - try { - val storeFile = File(dirPath, "storeFile") - if(!(storeFile.exists())) { - storeFile.createNewFile() - } - val fos = FileOutputStream(storeFile) - val oos = ObjectOutputStream(fos) - oos.writeObject(onlinePeers) - oos.close() - } catch(e: Exception) { - e.printStackTrace() - } + // TODO: For some reason, observable delegate not calling updateContactsDatabase() when message database is changed + // TODO: Figure out why and delete manual call once fixed + updateContactsDatabase() } private fun readMessageDatabase(): HashMap { val messageDatabaseFile = File(dirPath, "messageDatabase") if(!(messageDatabaseFile.exists()) || messageDatabaseFile.length() == 0.toLong()) { messageDatabaseFile.createNewFile() + if(!(messageDatabaseFile.exists())) { + Log.d("HypeLifeCycle", "File for message database does not exist; creating new one (from read)") + } else { + Log.d("HypeLifeCycle", "messageDatabaseFile.length() == 0") + } return hashMapOf() } else { try { @@ -287,11 +280,13 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati val ois = ObjectInputStream(fis) val result = ois.readObject() as HashMap ois.close() + // debugging Log.d("HypeLifeCycle ", "reading messageDatabase (from file) is: ${result.entries.toString()}") + for(x in result.values) { for(y in x.getMessages()) { - Log.d("HypeLifeCycle", "Store contents (from reading messageDatabase (from file)): ${y.data.toString(charset("UTF-8"))}") + Log.d("HypeLifeCycle", "Store contents (from reading messageDatabase (from file)): ${y.first}") } } return result @@ -310,16 +305,23 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati val messageDatabaseFile = File(dirPath, "messageDatabase") if(!(messageDatabaseFile.exists())) { messageDatabaseFile.createNewFile() + Log.d("HypeLifeCycle", "File for message database does not exist; creating new one (from update)") } - // TODO: DO THIS FIRST - figure out how to serialize messageDatabase - // TODO: since Instance from Hype SDK not serializable, it won't let me serialize all of messageDatabase - // by that logic, I should probably check up on contactsDatabase as well + // since Instance from Hype SDK not serializable, it won't let me serialize all of messageDatabase val fos = FileOutputStream(messageDatabaseFile) val oos = ObjectOutputStream(fos) oos.writeObject(messageDatabase) // debugging Log.d("HypeLifeCycle", "Right after writing the file, the new file is: ${readMessageDatabase()}") oos.close() + + // Checking if the file is empty, meaning the messageDatabase didn't write properly + // debugging + if(messageDatabaseFile.length() == 0.toLong()) { + Log.d("HypeLifeCycle", "The messageDatabaseFile is still empty after writing; this means there's a problem in updateMessageDatabase()") + } + oos.close() + } catch(e: Exception) { e.printStackTrace() } @@ -329,6 +331,7 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati val contactsFile = File(dirPath, "contactsFile") if(!(contactsFile.exists()) || contactsFile.length() == 0.toLong()) { contactsFile.createNewFile() + Log.d("HypeLifeCycle", "File for contacts database does not exist; creating new one (from read)") return hashMapOf() } else { try { @@ -350,10 +353,12 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati val contactsFile = File(dirPath, "contactsFile") if(!(contactsFile.exists())) { contactsFile.createNewFile() + Log.d("HypeLifeCycle", "File for contacts database does not exist; creating new one (from update)") } val fos = FileOutputStream(contactsFile) val oos = ObjectOutputStream(fos) oos.writeObject(contactsDatabase) + Log.d("HypeLifeCycle", "Right after writing the contacts file, the new file is: ${readContactsDatabase()}") oos.close() } catch(e: Exception) { @@ -364,12 +369,13 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati private fun addToResolvedInstancesMap(instance: Instance) { // Instances should be strongly kept by some data structure. Their identifiers // are useful for keeping track of which instances are ready to communicate. - getAllOnlinePeers().add(instance.userIdentifier) - setAllOnlinePeers(instance.userIdentifier, true) - Log.d("HypeLifeCycle ", "New onlinePeers: " + getAllOnlinePeers().toString()) + getOnlinePeers().put(instance.userIdentifier, instance) + setOnlinePeers(instance.userIdentifier, instance, true) + Log.d("HypeLifeCycle ", "New onlinePeers: " + getOnlinePeers().toString()) if(getAllMessages()[instance.userIdentifier] == null) { - setMessageDatabase(instance.userIdentifier, Store(instance)) - Log.d("HypeLifeCycle ", "New messageDatabase: " + getAllMessages().toString()) + Log.d("HypeLifeCycle", "Could not find userIdentifier in getAllMessages() (aka messageDatabase), therefore starting a brand new Store") + setMessageDatabase(instance.userIdentifier, Store(instance.userIdentifier)) + Log.d("HypeLifeCycle ", "New messageDatabase (from variable): " + getAllMessages().toString()) } if(!(readContactsDatabase().containsKey(instance.userIdentifier))) { // restoring User object from serialized byteArray @@ -380,21 +386,20 @@ class HypeLifeCycle : StateObserver, NetworkObserver, MessageObserver, Applicati // and adding the userIdentifier exceeds the limit) Log.d("HypeLifeCycle ", "NEWUSER OBJECT: ${newUser.toString()}") setContactsDatabase(instance.userIdentifier, newUser) + } else { + Log.d("HypeLifeCycle", "User recognized in ContactsDatabase, no need to set anything new") } -// // Notify the conversationList activity to refresh the UI -// val conversationListActivity = ConversationListActivity() -// conversationListActivity.notifyOnlinePeersChanged() } fun removeFromResolvedInstancesMap(instance: Instance) { // Cleaning up is always a good idea. It's not possible to communicate with instances // that were previously lost. -// getAllOnlinePeers().remove(instance.userIdentifier) +// getOnlinePeers().remove(instance.userIdentifier) // debugging Log.d("HypeLifeCycle", "Lost instance") - setAllOnlinePeers(instance.userIdentifier, false) + setOnlinePeers(instance.userIdentifier, instance,false) // Notify the conversationList activity to refresh the UI // val conversationListActivity = ConversationListActivity() diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/LifecycleObserverActivity.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/LifecycleObserverActivity.kt index a16a242..cd51b1e 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/LifecycleObserverActivity.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/LifecycleObserverActivity.kt @@ -27,9 +27,18 @@ class LifecycleObserverActivity(context: Context) : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_STOP) fun onAppBackgrounded() { Log.i("DEBUG ", "ON APP BACKGROUNDED CALLED") - val hypeFramework = mContext as HypeLifeCycle - hypeFramework.requestHypeToStop() + // Commenting this out because if Hype stops when app backgrounded, messages would only get sent + // when the user has the app open + val hypeFramework = mContext as HypeLifeCycle + hypeFramework.requestHypeToStop() + } + + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun onAppDestroyed() { + // Commenting this part out for demo tomorrow +// val hypeFramework = mContext as HypeLifeCycle +// hypeFramework.requestHypeToStop() } diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/MainActivity.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/MainActivity.kt index e2878b7..c85b593 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/MainActivity.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/MainActivity.kt @@ -5,7 +5,9 @@ package com.example.markmoussa.meshchatapplication */ import android.Manifest +import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.content.pm.PackageManager import android.support.v7.app.AppCompatActivity import android.os.Bundle @@ -33,5 +35,16 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this, SignUpActivity::class.java) startActivity(intent) } + + val sharedPreferences: SharedPreferences = applicationContext.getSharedPreferences("sp", Context.MODE_PRIVATE) + if(sharedPreferences.contains("USERNAME") && sharedPreferences.getString("USERNAME", null).isNotEmpty()) { + val intent = Intent(this, ConversationListActivity::class.java) + startActivity(intent) + } + + // Companion code to onBackPressed for having the back button exit app instead of going back to SignUpActivity + if(intent.getBooleanExtra("Exit me", false)){ + finish() + } } } diff --git a/app/src/main/java/com/example/markmoussa/meshchatapplication/MessageListActivity.kt b/app/src/main/java/com/example/markmoussa/meshchatapplication/MessageListActivity.kt index d57542d..493351e 100644 --- a/app/src/main/java/com/example/markmoussa/meshchatapplication/MessageListActivity.kt +++ b/app/src/main/java/com/example/markmoussa/meshchatapplication/MessageListActivity.kt @@ -7,6 +7,7 @@ package com.example.markmoussa.meshchatapplication import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.support.v4.widget.SwipeRefreshLayout +import android.support.v7.app.AlertDialog import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.util.Log @@ -18,29 +19,36 @@ import java.io.UnsupportedEncodingException class MessageListActivity : AppCompatActivity(), Store.Delegate { - private var mMessageList: MutableList = mutableListOf() + private var mMessageList: MutableList> = mutableListOf() private lateinit var mMessageAdapter: MessageListAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_message_list) - mMessageAdapter = MessageListAdapter(this, mMessageList) - var mMessageRecycler: RecyclerView? = null - mMessageRecycler = findViewById(R.id.recyclerview_message_list) as RecyclerView - mMessageRecycler.layoutManager = LinearLayoutManager(this) - mMessageRecycler.adapter = mMessageAdapter - // getting the messages - notifyMessageListChanged() val hypeFramework = applicationContext as HypeLifeCycle val userIdentifier = intent.getLongExtra("userIdentifier", 0) - // Setting actionbar with name of user + val contactsList = hypeFramework.getAllContacts() + // Setting profileUri to default user image in case it doesn't exist + // TODO: This way of setting the default profile pic might not be the best but it works; look into making more efficient later + var profileUri: String? = "/Users/markmoussa/AndroidStudioProjects/MeshChatApplication/app/src/main/res/drawable/default_user_image.png" + // Setting actionbar with name of user and getting profile pic path if(userIdentifier != 0.toLong()) { - if(userIdentifier in hypeFramework.getAllContacts()) { + if(userIdentifier in contactsList) { val actionBar = supportActionBar - actionBar!!.title = hypeFramework.getAllContacts()[userIdentifier]!!.nickname + actionBar!!.title = contactsList[userIdentifier]!!.nickname + + profileUri = contactsList[userIdentifier]?.profileUri } } + mMessageAdapter = MessageListAdapter(this, mMessageList, profileUri) + var mMessageRecycler: RecyclerView? = null + mMessageRecycler = findViewById(R.id.recyclerview_message_list) + mMessageRecycler.layoutManager = LinearLayoutManager(this) + mMessageRecycler.adapter = mMessageAdapter + // getting the messages + notifyMessageListChanged() + // hackish fix at the messages not appearing when notifyDataSetChanged() called. Need to fix later // also need to fix same thing for ConversationListActivity @@ -54,7 +62,6 @@ class MessageListActivity : AppCompatActivity(), Store.Delegate { override fun onStart() { super.onStart() // checking to see which user chat they want, or if it's a new message - // TODO: made a new separate class for new messages for now, condense that into this class when I have everything set up val hypeFramework = applicationContext as HypeLifeCycle val userIdentifier = intent.getLongExtra("userIdentifier", 0) @@ -63,13 +70,13 @@ class MessageListActivity : AppCompatActivity(), Store.Delegate { // debugging Log.d("MessageListActivity ", "Store is this: ") for(x in store.getMessages()) { - Log.i("DBEUG", x.data.toString()) + Log.i("DEBUG", x.first) } store.delegate = this store.lastReadIndex = store.getMessages().size - val chatBox = findViewById(R.id.edittext_chatbox) as EditText - val sendButton = findViewById