diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a0f96bc45f..b078083205 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,8 @@
+
+
+ val contactsFragment = ContactsFragment()
+ val transaction = activity?.supportFragmentManager?.beginTransaction();
+ transaction?.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ transaction?.replace(this.id, contactsFragment, "contactsFragment");
+ transaction?.addToBackStack(null)?.commit();
+ }
+ }
}
diff --git a/app/src/main/java/chat/rocket/android/contacts/ContactListFragment.kt b/app/src/main/java/chat/rocket/android/contacts/ContactListFragment.kt
new file mode 100644
index 0000000000..61cf88ff4e
--- /dev/null
+++ b/app/src/main/java/chat/rocket/android/contacts/ContactListFragment.kt
@@ -0,0 +1,97 @@
+package chat.rocket.android.contacts
+
+import android.Manifest
+import android.app.Activity
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.provider.ContactsContract
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import chat.rocket.android.R
+import chat.rocket.android.contacts.models.Contact
+import chat.rocket.android.main.ui.MainActivity
+import timber.log.Timber
+import java.util.ArrayList
+import kotlin.collections.HashMap
+
+
+/**
+ * Load a list of contacts in a recycler view
+ */
+class ContactListFragment : Fragment() {
+ /**
+ * The list of contacts to load in the recycler view
+ */
+ private var contactArrayList: ArrayList? = null
+
+ /**
+ * The mapping of contacts with their registration status
+ */
+ private var contactHashMap: HashMap = HashMap()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val bundle = arguments
+ if (bundle != null) {
+ contactArrayList = bundle.getParcelableArrayList("CONTACT_ARRAY_LIST")
+ contactHashMap= bundle.getSerializable("CONTACT_HASH_MAP") as HashMap
+ }
+
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.fragment_contacts, container, false)
+ val context = view.context
+
+ val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
+ val emptyTextView = view.findViewById(R.id.text_no_data_to_display) as TextView
+
+ if (contactArrayList!!.size == 0) {
+ emptyTextView.visibility = View.VISIBLE
+ recyclerView.visibility = View.GONE
+ } else {
+ emptyTextView.visibility = View.GONE
+ recyclerView.visibility = View.VISIBLE
+
+ recyclerView.setHasFixedSize(true)
+ recyclerView.layoutManager = LinearLayoutManager(context)
+ recyclerView.adapter = ContactRecyclerViewAdapter(this.activity as MainActivity, contactArrayList!!, contactHashMap)
+ }
+
+ return view
+ }
+
+ companion object {
+
+ /**
+ * Create a new ContactList fragment that displays the given list of contacts
+ *
+ * @param contactArrayList the list of contacts to load in the recycler view
+ * @param contactHashMap the mapping of contacts with their registration status
+ * @return the newly created ContactList fragment
+ */
+ fun newInstance(
+ contactArrayList: ArrayList,
+ contactHashMap: HashMap
+ ): ContactListFragment {
+ val contactListFragment = ContactListFragment()
+
+ val arguments = Bundle()
+ arguments.putParcelableArrayList("CONTACT_ARRAY_LIST", contactArrayList)
+ arguments.putSerializable("CONTACT_HASH_MAP", contactHashMap)
+
+ contactListFragment.arguments = arguments
+
+ return contactListFragment
+ }
+ }
+}
diff --git a/app/src/main/java/chat/rocket/android/contacts/ContactRecyclerViewAdapter.kt b/app/src/main/java/chat/rocket/android/contacts/ContactRecyclerViewAdapter.kt
new file mode 100644
index 0000000000..39824c1e56
--- /dev/null
+++ b/app/src/main/java/chat/rocket/android/contacts/ContactRecyclerViewAdapter.kt
@@ -0,0 +1,78 @@
+package chat.rocket.android.contacts
+
+import android.content.Context
+import timber.log.Timber
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.TextView
+import android.widget.Toast
+import androidx.fragment.app.FragmentActivity
+import androidx.recyclerview.widget.RecyclerView
+import chat.rocket.android.R
+import chat.rocket.android.contacts.models.Contact
+import chat.rocket.android.main.ui.MainActivity
+import java.util.*
+import kotlin.collections.HashMap
+import chat.rocket.android.util.extensions.showToast
+
+class ContactRecyclerViewAdapter(
+ private val context: MainActivity,
+ private val contactArrayList: ArrayList,
+ private val contactHashMap: HashMap
+) : RecyclerView.Adapter() {
+
+ override fun getItemCount(): Int {
+ return contactArrayList.size
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater
+ .from(parent.context)
+ .inflate(R.layout.item_contact, parent, false)
+ return ViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ holder.contact = contactArrayList[position]
+ holder.status = contactHashMap.get(holder.contact!!.getPhoneNumber())
+ try {
+ holder.contactName.text = holder.contact!!.getName()
+ if (holder.contact!!.isPhone()) {
+ holder.contactDetail.text = holder.contact!!.getPhoneNumber()
+ } else {
+ holder.contactDetail.text = holder.contact!!.getEmailAddress()
+ }
+ } catch (exception: NullPointerException) {
+ Timber.e("Failed to send resolution. Exception is: $exception")
+ }
+
+ }
+
+ inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
+ var contact: Contact? = null
+ var status: String? = null
+
+ var contactName: TextView
+ var contactDetail: TextView
+ var inviteButton: Button
+
+ init {
+ this.contactName = view.findViewById(R.id.contact_name) as TextView
+ this.contactDetail = view.findViewById(R.id.contact_detail) as TextView
+ this.inviteButton = view.findViewById(R.id.invite_contact) as Button
+
+ this.inviteButton.setOnClickListener { view ->
+ run {
+ // Make API call using context.presenter
+ if(contact!!.isPhone()){
+ context.presenter.inviteViaSMS(contact!!.getPhoneNumber()!!);
+ }else{
+ context.presenter.inviteViaEmail(contact!!.getEmailAddress()!!);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/chat/rocket/android/contacts/ContactsFragment.kt b/app/src/main/java/chat/rocket/android/contacts/ContactsFragment.kt
new file mode 100644
index 0000000000..345f94a93d
--- /dev/null
+++ b/app/src/main/java/chat/rocket/android/contacts/ContactsFragment.kt
@@ -0,0 +1,299 @@
+package chat.rocket.android.contacts
+
+import android.Manifest
+import android.app.Activity
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.provider.ContactsContract
+import android.view.*
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.SearchView
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import chat.rocket.android.R
+import chat.rocket.android.contacts.models.Contact
+import chat.rocket.android.createchannel.ui.CreateChannelFragment
+import chat.rocket.android.main.ui.MainActivity
+import chat.rocket.android.util.extension.onQueryTextListener
+import kotlinx.android.synthetic.main.app_bar.*
+import java.util.ArrayList
+import kotlin.Comparator
+import kotlin.collections.HashMap
+
+/**
+ * Load a list of contacts in a recycler view
+ */
+class ContactsFragment : Fragment() {
+ /**
+ * The list of contacts to load in the recycler view
+ */
+ private var contactArrayList: ArrayList = ArrayList()
+
+ /**
+ * The mapping of contacts with their registration status
+ */
+ private var contactHashMap: HashMap = HashMap()
+
+ private val MY_PERMISSIONS_REQUEST_RW_CONTACTS = 0
+
+ private var createNewChannelLink: View? = null
+ private var searchView: SearchView? = null
+ private var sortView: MenuItem? = null
+
+ private fun getContactList() {
+ val cr = context!!.contentResolver
+
+ val cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
+
+ if ((cur?.count ?: 0) > 0) {
+ while (cur != null && cur.moveToNext()) {
+ val id = cur.getString(
+ cur.getColumnIndex(ContactsContract.Contacts._ID))
+ val name = cur.getString(cur.getColumnIndex(
+ ContactsContract.Contacts.DISPLAY_NAME))
+
+ if (cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
+ // Has phone numbers
+
+ val pCur = cr.query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
+ arrayOf(id), null)
+ while (pCur!!.moveToNext()) {
+ val phoneNo = pCur.getString(pCur.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.NUMBER))
+ val contact = Contact()
+ contact.setName(name)
+ contact.setPhoneNumber(phoneNo)
+ contactArrayList.add(contact)
+ contactHashMap[phoneNo] = "INDETERMINATE"
+ }
+ pCur.close()
+ }
+
+ if (true) {
+ // No check for having email address
+
+ val eCur = cr.query(
+ ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
+ ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
+ arrayOf(id), null)
+ while (eCur!!.moveToNext()) {
+ val emailID = eCur.getString(eCur.getColumnIndex(
+ ContactsContract.CommonDataKinds.Email.DATA))
+ val contact = Contact()
+ contact.setName(name)
+ contact.setEmailAddress(emailID)
+ contactArrayList.add(contact)
+ contactHashMap[emailID] = "INDETERMINATE"
+ }
+ eCur.close()
+ }
+ }
+ }
+ cur?.close()
+ contactArrayList.sortWith(Comparator { o1, o2 ->
+ o1.getName()!!.compareTo(o2.getName()!!)
+ })
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ inflater.inflate(R.menu.chatrooms, menu)
+
+ sortView = menu.findItem(R.id.action_sort)
+ sortView!!.isVisible = false
+
+ val searchItem = menu.findItem(R.id.action_search)
+ searchView = searchItem?.actionView as? SearchView
+ searchView?.setIconifiedByDefault(false)
+ searchView?.maxWidth = Integer.MAX_VALUE
+ searchView?.onQueryTextListener { queryContacts(it) }
+
+ val expandListener = object : MenuItem.OnActionExpandListener {
+ override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
+ // Simply setting sortView to visible won't work, so we invalidate the options
+ // to recreate the entire menu...
+ activity?.invalidateOptionsMenu()
+ queryContacts("")
+ return true
+ }
+
+ override fun onMenuItemActionExpand(item: MenuItem): Boolean {
+ return true
+ }
+ }
+ searchItem?.setOnActionExpandListener(expandListener)
+ }
+
+ fun containsIgnoreCase(src: String, what: String): Boolean {
+ val length = what.length
+ if (length == 0)
+ return true // Empty string is contained
+
+ val firstLo = Character.toLowerCase(what[0])
+ val firstUp = Character.toUpperCase(what[0])
+
+ for (i in src.length - length downTo 0) {
+ // Quick check before calling the more expensive regionMatches() method:
+ val ch = src[i]
+ if (ch != firstLo && ch != firstUp)
+ continue
+
+ if (src.regionMatches(i, what, 0, length, ignoreCase = true))
+ return true
+ }
+
+ return false
+ }
+
+ fun queryContacts(query: String) {
+ if (query.isBlank() or query.isEmpty()) {
+ setupFrameLayout(contactArrayList)
+ } else {
+ var filteredContactArrayList: ArrayList = ArrayList()
+ for (contact in contactArrayList) {
+ if (containsIgnoreCase(contact.getName()!!, query)
+ || (contact.isPhone() && containsIgnoreCase(contact.getPhoneNumber()!!, query))
+ || (!contact.isPhone() && containsIgnoreCase(contact.getEmailAddress()!!, query))
+ ) {
+ filteredContactArrayList.add(contact)
+ }
+ }
+ setupFrameLayout(filteredContactArrayList)
+ }
+ }
+
+ private fun populateContacts(actualContacts: Boolean) {
+ if (actualContacts) {
+ getContactList()
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ when (requestCode) {
+ MY_PERMISSIONS_REQUEST_RW_CONTACTS -> {
+ if (
+ grantResults.isNotEmpty()
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED
+ && grantResults[1] == PackageManager.PERMISSION_GRANTED
+ ) {
+ // Permission granted
+ populateContacts(true)
+ } else {
+ populateContacts(false)
+ }
+ return
+ }
+ else -> {
+ // Ignore all other requests.
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+
+ if (
+ ContextCompat.checkSelfPermission(context!!, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
+ && ContextCompat.checkSelfPermission(context!!, Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED
+ ) {
+ populateContacts(true)
+ } else {
+ ActivityCompat.requestPermissions(
+ this.activity as Activity,
+ arrayOf(
+ Manifest.permission.READ_CONTACTS,
+ Manifest.permission.WRITE_CONTACTS
+ ),
+ MY_PERMISSIONS_REQUEST_RW_CONTACTS
+ )
+ }
+
+ // Filter before sending to FrameLayout
+ setupFrameLayout(contactArrayList)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupToolbar()
+ }
+
+ fun setupToolbar(){
+ (activity as AppCompatActivity).supportActionBar?.title = getString(R.string.title_contacts)
+ (activity as MainActivity).toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
+ (activity as MainActivity).toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
+ }
+
+ override fun setUserVisibleHint(isVisibleToUser: Boolean) {
+ super.setUserVisibleHint(isVisibleToUser)
+ }
+
+ fun setupFrameLayout(filteredContactArrayList: ArrayList) {
+ try {
+ val contactListFragment = ContactListFragment.newInstance(
+ filteredContactArrayList,
+ contactHashMap
+ )
+ val fragmentTransaction = childFragmentManager.beginTransaction()
+ fragmentTransaction.replace(
+ R.id.contacts_area,
+ contactListFragment,
+ "CONTACT_LIST_FRAGMENT"
+ )
+ fragmentTransaction.commit()
+ } catch (exception: IllegalStateException) {
+ //This is one bad user who clicks too fast
+ } catch (exception: NullPointerException) {
+ }
+
+
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.fragment_contact_parent, container, false)
+
+ createNewChannelLink = view.findViewById(R.id.create_new_channel_button)
+ createNewChannelLink!!.setOnClickListener {
+ val createChannelFragment = CreateChannelFragment()
+ val transaction = activity?.supportFragmentManager?.beginTransaction();
+ transaction?.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ transaction?.replace(this.id, createChannelFragment, "createChannelFragment");
+ transaction?.addToBackStack(null)?.commit();
+ }
+
+ return view
+ }
+
+ companion object {
+
+ /**
+ * Create a new ContactList fragment that displays the given list of contacts
+ *
+ * @param contactArrayList the list of contacts to load in the recycler view
+ * @param contactHashMap the mapping of contacts with their registration status
+ * @return the newly created ContactList fragment
+ */
+ fun newInstance(
+ contactArrayList: ArrayList,
+ contactHashMap: HashMap
+ ): ContactsFragment {
+ val contactsFragment = ContactsFragment()
+
+ val arguments = Bundle()
+ arguments.putParcelableArrayList("CONTACT_ARRAY_LIST", contactArrayList)
+ arguments.putSerializable("CONTACT_HASH_MAP", contactHashMap)
+
+ contactsFragment.arguments = arguments
+
+ return contactsFragment
+ }
+ }
+
+}
diff --git a/app/src/main/java/chat/rocket/android/contacts/models/Contact.kt b/app/src/main/java/chat/rocket/android/contacts/models/Contact.kt
new file mode 100644
index 0000000000..d567183c9e
--- /dev/null
+++ b/app/src/main/java/chat/rocket/android/contacts/models/Contact.kt
@@ -0,0 +1,70 @@
+package chat.rocket.android.contacts.models
+
+import android.os.Parcel
+import android.os.Parcelable
+
+
+class Contact() : Parcelable {
+ private var id: Int = 0
+ private var name: String? = null
+ private var phoneNumber: String? = null
+ private var emailAddress: String? = null
+ private var isPhone: Boolean = true
+
+ fun getName(): String? {
+ return name
+ }
+
+ fun setName(name: String) {
+ this.name = name
+ }
+
+ fun getPhoneNumber(): String? {
+ return phoneNumber
+ }
+
+ fun setPhoneNumber(phoneNumber: String) {
+ this.phoneNumber = phoneNumber
+ }
+
+ fun getEmailAddress(): String? {
+ return emailAddress
+ }
+
+ fun setEmailAddress(emailAddress: String) {
+ this.emailAddress = emailAddress
+ this.isPhone = false
+ }
+
+ fun isPhone(): Boolean {
+ return this.isPhone
+ }
+
+ constructor(parcel: Parcel) : this() {
+ this.id = parcel.readInt()
+ this.name = parcel.readString()
+ this.phoneNumber = parcel.readString()
+ }
+
+
+ override fun writeToParcel(dest: Parcel?, flags: Int) {
+ dest?.writeInt(id)
+ dest?.writeString(name)
+ dest?.writeString(phoneNumber)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): Contact {
+ return Contact(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
index e299409ed9..b50e376eb1 100644
--- a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
+++ b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
@@ -36,10 +36,15 @@ import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getCustomEmojis
import chat.rocket.core.internal.rest.me
+import chat.rocket.core.internal.rest.unregisterPushToken
+import chat.rocket.core.internal.rest.inviteViaEmail
+import chat.rocket.core.internal.rest.inviteViaSMS
import chat.rocket.core.model.Myself
import kotlinx.coroutines.experimental.channels.Channel
import timber.log.Timber
import javax.inject.Inject
+import chat.rocket.android.util.extensions.showToast
+
class MainPresenter @Inject constructor(
private val view: MainView,
@@ -209,6 +214,60 @@ class MainPresenter @Inject constructor(
}
}
+ fun inviteViaEmail(email:String) {
+ launchUI(strategy) {
+ try {
+ val result:Boolean = retryIO("inviteViaEmail") { client.inviteViaEmail(email) }
+ if (result) {
+ view.showMessage("Invitation Email Sent")
+ } else{
+ view.showMessage("Failed to send Invitation Email")
+ }
+ } catch (ex: Exception) {
+ when (ex) {
+ is RocketChatAuthException -> {
+ logout()
+ }
+ else -> {
+ Timber.d(ex, "Error while inviting via email")
+ ex.message?.let {
+ view.showMessage(it)
+ }.ifNull {
+ view.showGenericErrorMessage()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun inviteViaSMS(phone:String) {
+ launchUI(strategy) {
+ try {
+ val result:Boolean = retryIO("inviteViaSMS") { client.inviteViaSMS(phone) }
+ if (result) {
+ view.showMessage("Invitation SMS Sent")
+ } else{
+ view.showMessage("Failed to send Invitation SMS")
+ }
+ } catch (ex: Exception) {
+ when (ex) {
+ is RocketChatAuthException -> {
+ logout()
+ }
+ else -> {
+ Timber.d(ex, "Error while inviting via SMS")
+ ex.message?.let {
+ view.showMessage(it)
+ }.ifNull {
+ view.showGenericErrorMessage()
+ }
+ }
+ }
+ }
+ }
+ }
+
private suspend fun saveAccount(uiModel: NavHeaderUiModel) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
diff --git a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
index 935a918825..2daa5e58c8 100644
--- a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+++ b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
@@ -5,6 +5,7 @@ import android.app.Activity
import android.app.AlertDialog
import android.app.ProgressDialog
import android.os.Bundle
+import android.text.Layout
import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GravityCompat
diff --git a/app/src/main/res/drawable/ic_add_white_24dp.xml b/app/src/main/res/drawable/ic_add_white_24dp.xml
new file mode 100644
index 0000000000..b9b8eca8b9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_white_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_new_chat.xml b/app/src/main/res/layout/activity_new_chat.xml
new file mode 100644
index 0000000000..88d85e04f8
--- /dev/null
+++ b/app/src/main/res/layout/activity_new_chat.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_chat_rooms.xml b/app/src/main/res/layout/fragment_chat_rooms.xml
index 1a82b796a5..8045f97aa9 100644
--- a/app/src/main/res/layout/fragment_chat_rooms.xml
+++ b/app/src/main/res/layout/fragment_chat_rooms.xml
@@ -56,4 +56,15 @@
android:textSize="20sp"
android:visibility="gone"
tools:visibility="visible" />
+
+
diff --git a/app/src/main/res/layout/fragment_contact_parent.xml b/app/src/main/res/layout/fragment_contact_parent.xml
new file mode 100644
index 0000000000..ca48795778
--- /dev/null
+++ b/app/src/main/res/layout/fragment_contact_parent.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_contacts.xml b/app/src/main/res/layout/fragment_contacts.xml
new file mode 100644
index 0000000000..01e217940e
--- /dev/null
+++ b/app/src/main/res/layout/fragment_contacts.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_contact.xml b/app/src/main/res/layout/item_contact.xml
new file mode 100644
index 0000000000..b786030e11
--- /dev/null
+++ b/app/src/main/res/layout/item_contact.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 2465cd428d..eb2608e38f 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -10,6 +10,7 @@
Login Daten prüfen
Legal Terms
Chats
+ New Chat
Profil
Benutzer
Benutzer (%d)
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 9641a163f5..79e84c867e 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -9,6 +9,7 @@
Autenticación
Términos legales
Chats
+ New Chat
Perfil
Miembros
Miembros (%d)
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 048caf6b9a..2ec58ddaf3 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -10,6 +10,7 @@
Authentification
Termes juridiques
Chats
+ New Chat
Profil
Membres
Membres (%d)
diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml
index c6b80cd726..c67efffa37 100644
--- a/app/src/main/res/values-hi-rIN/strings.xml
+++ b/app/src/main/res/values-hi-rIN/strings.xml
@@ -10,6 +10,7 @@
प्रमाणीकरण
कानूनी शर्तें
चैट
+ नया चैट
प्रोफाइल
सदस्य
सदस्य (%d)
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index ff2aad2126..ddb8e8170f 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -12,6 +12,7 @@
認証
Legal Terms
チャット
+ New チャット
プロフィール
メンバー
メンバー (%d)
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index b03833c72a..c0cbe51e09 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -10,6 +10,7 @@
Autenticação
Termos Legais
Chats
+ New Chat
Perfil
Membros
Membros (%d)
diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml
index 3bb4d5eb2d..1db9f4bbe9 100644
--- a/app/src/main/res/values-ru-rRU/strings.xml
+++ b/app/src/main/res/values-ru-rRU/strings.xml
@@ -10,6 +10,7 @@
Аутентификация
Юридические условия
Чаты
+ New Chat
Профиль
Пользователи
Пользователи (%d)
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 70b0e3275b..6d7ac4db7e 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -10,6 +10,7 @@
Doğrulama
Yasal Şartlar
Sohbetler
+ New Sohbetler
Profil
Üyeler
Üyeler (%d)
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c2a0a239d6..131e5e639a 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -10,6 +10,7 @@
Аутентифікація
Юридичні умови
Чати
+ New Chat
Профіль
Користувачі
Користувачі (%d)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 57aeeb31ee..62b000dc46 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,6 +22,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
Authentication
Legal Terms
Chats
+ New Chat
Profile
Members
Members (%d)
@@ -184,7 +185,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
view more
view less
You are muted on this channel
-
+
Analytics tracking
Send anonymous statics to help improving this app
@@ -292,6 +293,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
Mute someone in the room
Unmute someone in the room
Invite one user to join this channel
+ Invite
Unarchive
Join the given channel
Generates a gif based upon the provided text
diff --git a/build.gradle b/build.gradle
index 24dd93492c..8869843f99 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.android.tools.build:gradle:3.3.0-alpha13'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:4.1.0'