2020-05-11 08:19:26 +02:00
|
|
|
package org.thoughtcrime.securesms.loki.activities
|
2019-12-18 15:25:23 +01:00
|
|
|
|
2020-01-08 00:50:11 +01:00
|
|
|
import android.animation.Animator
|
|
|
|
import android.animation.AnimatorListenerAdapter
|
2021-04-23 09:49:24 +02:00
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
2021-04-21 09:00:57 +02:00
|
|
|
import android.graphics.BitmapFactory
|
2019-12-18 15:25:23 +01:00
|
|
|
import android.os.Bundle
|
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
2020-01-08 00:50:11 +01:00
|
|
|
import android.view.inputmethod.InputMethodManager
|
|
|
|
import android.widget.Toast
|
2021-04-21 09:00:57 +02:00
|
|
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
|
|
|
import androidx.core.view.isVisible
|
2021-04-20 09:22:36 +02:00
|
|
|
import androidx.fragment.app.*
|
2020-11-20 08:59:13 +01:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-04-21 09:00:57 +02:00
|
|
|
import com.google.android.material.chip.Chip
|
2019-12-18 15:25:23 +01:00
|
|
|
import kotlinx.android.synthetic.main.activity_join_public_chat.*
|
|
|
|
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
|
2020-11-20 08:59:13 +01:00
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.withContext
|
2019-12-18 15:25:23 +01:00
|
|
|
import network.loki.messenger.R
|
2021-04-21 09:00:57 +02:00
|
|
|
import okhttp3.HttpUrl
|
2021-04-28 09:41:30 +02:00
|
|
|
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2.DefaultGroup
|
2021-05-18 08:11:38 +02:00
|
|
|
import org.session.libsession.utilities.Address
|
2021-05-18 08:03:47 +02:00
|
|
|
import org.session.libsession.utilities.recipients.Recipient
|
2021-04-23 09:49:24 +02:00
|
|
|
import org.session.libsession.utilities.GroupUtil
|
2021-05-18 01:12:33 +02:00
|
|
|
import org.session.libsignal.utilities.Log
|
2021-05-24 06:01:20 +02:00
|
|
|
import org.session.libsignal.utilities.PublicKeyValidation
|
2020-01-08 00:50:11 +01:00
|
|
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
2020-01-06 04:26:52 +01:00
|
|
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
2021-06-30 06:57:53 +02:00
|
|
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
2021-07-09 05:18:48 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.DefaultGroupsViewModel
|
2021-04-23 09:49:24 +02:00
|
|
|
import org.thoughtcrime.securesms.groups.GroupManager
|
2021-05-19 03:12:29 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
2020-05-11 08:19:26 +02:00
|
|
|
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
|
|
|
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
2021-07-09 05:01:16 +02:00
|
|
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
2021-07-09 05:18:48 +02:00
|
|
|
import org.thoughtcrime.securesms.util.State
|
2021-06-02 02:51:36 +02:00
|
|
|
import java.util.*
|
2019-12-18 15:25:23 +01:00
|
|
|
|
2020-01-06 04:26:52 +01:00
|
|
|
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
2019-12-18 15:25:23 +01:00
|
|
|
private val adapter = JoinPublicChatActivityAdapter(this)
|
|
|
|
|
|
|
|
// region Lifecycle
|
2020-01-06 04:26:52 +01:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
|
|
|
super.onCreate(savedInstanceState, isReady)
|
2019-12-18 15:25:23 +01:00
|
|
|
// Set content view
|
|
|
|
setContentView(R.layout.activity_join_public_chat)
|
|
|
|
// Set title
|
2020-05-25 08:12:24 +02:00
|
|
|
supportActionBar!!.title = resources.getString(R.string.activity_join_public_chat_title)
|
2019-12-18 15:25:23 +01:00
|
|
|
// Set up view pager
|
|
|
|
viewPager.adapter = adapter
|
|
|
|
tabLayout.setupWithViewPager(viewPager)
|
|
|
|
}
|
|
|
|
// endregion
|
|
|
|
|
2020-01-08 00:50:11 +01:00
|
|
|
// region Updating
|
|
|
|
private fun showLoader() {
|
|
|
|
loader.visibility = View.VISIBLE
|
|
|
|
loader.animate().setDuration(150).alpha(1.0f).start()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun hideLoader() {
|
|
|
|
loader.animate().setDuration(150).alpha(0.0f).setListener(object : AnimatorListenerAdapter() {
|
|
|
|
|
|
|
|
override fun onAnimationEnd(animation: Animator?) {
|
|
|
|
super.onAnimationEnd(animation)
|
|
|
|
loader.visibility = View.GONE
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
// endregion
|
|
|
|
|
2019-12-18 15:25:23 +01:00
|
|
|
// region Interaction
|
|
|
|
override fun handleQRCodeScanned(url: String) {
|
|
|
|
joinPublicChatIfPossible(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun joinPublicChatIfPossible(url: String) {
|
2021-05-14 00:56:25 +02:00
|
|
|
// Add "http" if not entered explicitly
|
|
|
|
val stringWithExplicitScheme = if (!url.startsWith("http")) "http://$url" else url
|
|
|
|
val url = HttpUrl.parse(stringWithExplicitScheme) ?: return Toast.makeText(this,R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
|
|
|
val room = url.pathSegments().firstOrNull()
|
|
|
|
val publicKey = url.queryParameter("public_key")
|
2021-04-21 09:00:57 +02:00
|
|
|
val isV2OpenGroup = !room.isNullOrEmpty()
|
2021-05-24 06:01:20 +02:00
|
|
|
if (isV2OpenGroup && (publicKey == null || !PublicKeyValidation.isValid(publicKey, 64,false))) {
|
|
|
|
return Toast.makeText(this, R.string.invalid_public_key, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
2020-03-13 00:56:25 +01:00
|
|
|
showLoader()
|
2020-11-20 08:59:13 +01:00
|
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|
|
|
try {
|
2021-04-23 09:49:24 +02:00
|
|
|
val (threadID, groupID) = if (isV2OpenGroup) {
|
2021-05-14 00:56:25 +02:00
|
|
|
val server = HttpUrl.Builder().scheme(url.scheme()).host(url.host()).apply {
|
|
|
|
if (url.port() != 80 || url.port() != 443) { this.port(url.port()) } // Non-standard port; add to server
|
2021-05-04 07:52:08 +02:00
|
|
|
}.build()
|
2021-05-19 03:12:29 +02:00
|
|
|
|
|
|
|
val sanitizedServer = server.toString().removeSuffix("/")
|
|
|
|
val openGroupID = "$sanitizedServer.${room!!}"
|
|
|
|
OpenGroupManager.add(sanitizedServer, room, publicKey!!, this@JoinPublicChatActivity)
|
|
|
|
val threadID = GroupManager.getOpenGroupThreadID(openGroupID, this@JoinPublicChatActivity)
|
|
|
|
val groupID = GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())
|
2021-04-23 09:49:24 +02:00
|
|
|
threadID to groupID
|
2021-04-21 09:00:57 +02:00
|
|
|
} else {
|
2021-05-19 03:12:29 +02:00
|
|
|
throw Exception("No longer supported.")
|
2021-04-21 09:00:57 +02:00
|
|
|
}
|
2021-07-09 05:01:16 +02:00
|
|
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@JoinPublicChatActivity)
|
2021-04-23 09:49:24 +02:00
|
|
|
withContext(Dispatchers.Main) {
|
2021-05-14 00:56:25 +02:00
|
|
|
val recipient = Recipient.from(this@JoinPublicChatActivity, Address.fromSerialized(groupID), false)
|
|
|
|
openConversationActivity(this@JoinPublicChatActivity, threadID, recipient)
|
2021-04-23 09:49:24 +02:00
|
|
|
finish()
|
|
|
|
}
|
2020-11-20 08:59:13 +01:00
|
|
|
} catch (e: Exception) {
|
2021-05-14 00:56:25 +02:00
|
|
|
Log.e("Loki", "Couldn't join open group.", e)
|
2020-11-20 08:59:13 +01:00
|
|
|
withContext(Dispatchers.Main) {
|
|
|
|
hideLoader()
|
|
|
|
Toast.makeText(this@JoinPublicChatActivity, R.string.activity_join_public_chat_error, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
return@launch
|
|
|
|
}
|
2020-01-08 00:50:11 +01:00
|
|
|
}
|
2019-12-18 15:25:23 +01:00
|
|
|
}
|
|
|
|
// endregion
|
2021-04-23 09:49:24 +02:00
|
|
|
|
|
|
|
// region Convenience
|
|
|
|
private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) {
|
2021-06-30 06:57:53 +02:00
|
|
|
val intent = Intent(context, ConversationActivityV2::class.java)
|
|
|
|
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
|
2021-07-01 01:31:30 +02:00
|
|
|
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
|
2021-04-23 09:49:24 +02:00
|
|
|
context.startActivity(intent)
|
|
|
|
}
|
|
|
|
// endregion
|
2019-12-18 15:25:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// region Adapter
|
|
|
|
private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity) : FragmentPagerAdapter(activity.supportFragmentManager) {
|
|
|
|
|
|
|
|
override fun getCount(): Int {
|
|
|
|
return 2
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun getItem(index: Int): Fragment {
|
|
|
|
return when (index) {
|
|
|
|
0 -> EnterChatURLFragment()
|
|
|
|
1 -> {
|
|
|
|
val result = ScanQRCodeWrapperFragment()
|
|
|
|
result.delegate = activity
|
2020-05-25 08:12:24 +02:00
|
|
|
result.message = activity.resources.getString(R.string.activity_join_public_chat_scan_qr_code_explanation)
|
2019-12-18 15:25:23 +01:00
|
|
|
result
|
|
|
|
}
|
|
|
|
else -> throw IllegalStateException()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-21 09:00:57 +02:00
|
|
|
override fun getPageTitle(index: Int): CharSequence {
|
2019-12-18 15:25:23 +01:00
|
|
|
return when (index) {
|
2020-05-25 08:12:24 +02:00
|
|
|
0 -> activity.resources.getString(R.string.activity_join_public_chat_enter_group_url_tab_title)
|
|
|
|
1 -> activity.resources.getString(R.string.activity_join_public_chat_scan_qr_code_tab_title)
|
2019-12-18 15:25:23 +01:00
|
|
|
else -> throw IllegalStateException()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// endregion
|
|
|
|
|
2020-01-08 05:16:34 +01:00
|
|
|
// region Enter Chat URL Fragment
|
2019-12-18 15:25:23 +01:00
|
|
|
class EnterChatURLFragment : Fragment() {
|
2021-04-20 09:22:36 +02:00
|
|
|
private val viewModel by activityViewModels<DefaultGroupsViewModel>()
|
|
|
|
|
2019-12-18 15:25:23 +01:00
|
|
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
|
|
return inflater.inflate(R.layout.fragment_enter_chat_url, container, false)
|
|
|
|
}
|
|
|
|
|
2021-05-14 00:56:25 +02:00
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
chatURLEditText.imeOptions = chatURLEditText.imeOptions or 16777216 // Always use incognito keyboard
|
|
|
|
joinPublicChatButton.setOnClickListener { joinPublicChatIfPossible() }
|
|
|
|
viewModel.defaultRooms.observe(viewLifecycleOwner) { state ->
|
|
|
|
defaultRoomsContainer.isVisible = state is State.Success
|
2021-06-02 02:51:36 +02:00
|
|
|
defaultRoomsLoaderContainer.isVisible = state is State.Loading
|
2021-05-14 00:56:25 +02:00
|
|
|
defaultRoomsLoader.isVisible = state is State.Loading
|
|
|
|
when (state) {
|
|
|
|
State.Loading -> {
|
|
|
|
// TODO: Show a loader
|
|
|
|
}
|
|
|
|
is State.Error -> {
|
|
|
|
// TODO: Hide the loader
|
|
|
|
}
|
|
|
|
is State.Success -> {
|
|
|
|
populateDefaultGroups(state.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-20 09:22:36 +02:00
|
|
|
private fun populateDefaultGroups(groups: List<DefaultGroup>) {
|
2021-04-21 09:00:57 +02:00
|
|
|
defaultRoomsGridLayout.removeAllViews()
|
2021-05-14 02:09:12 +02:00
|
|
|
defaultRoomsGridLayout.useDefaultMargins = false
|
2021-04-21 09:00:57 +02:00
|
|
|
groups.forEach { defaultGroup ->
|
2021-05-14 02:09:12 +02:00
|
|
|
val chip = layoutInflater.inflate(R.layout.default_group_chip, defaultRoomsGridLayout, false) as Chip
|
2021-04-21 09:00:57 +02:00
|
|
|
val drawable = defaultGroup.image?.let { bytes ->
|
2021-05-14 00:56:25 +02:00
|
|
|
val bitmap = BitmapFactory.decodeByteArray(bytes,0, bytes.size)
|
2021-04-21 09:00:57 +02:00
|
|
|
RoundedBitmapDrawableFactory.create(resources,bitmap).apply {
|
|
|
|
isCircular = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chip.chipIcon = drawable
|
|
|
|
chip.text = defaultGroup.name
|
2021-04-27 09:29:37 +02:00
|
|
|
chip.setOnClickListener {
|
2021-05-12 06:48:13 +02:00
|
|
|
(requireActivity() as JoinPublicChatActivity).joinPublicChatIfPossible(defaultGroup.joinURL)
|
2021-04-27 09:29:37 +02:00
|
|
|
}
|
2021-04-21 09:00:57 +02:00
|
|
|
defaultRoomsGridLayout.addView(chip)
|
|
|
|
}
|
2021-05-14 02:09:12 +02:00
|
|
|
if ((groups.size and 1) != 0) { // This checks that the number of rooms is even
|
2021-04-21 09:00:57 +02:00
|
|
|
layoutInflater.inflate(R.layout.grid_layout_filler, defaultRoomsGridLayout)
|
|
|
|
}
|
2021-04-20 09:22:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-23 09:49:24 +02:00
|
|
|
// region Convenience
|
2019-12-18 15:25:23 +01:00
|
|
|
private fun joinPublicChatIfPossible() {
|
2020-11-20 08:59:13 +01:00
|
|
|
val inputMethodManager = requireContext().getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
2020-01-08 00:50:11 +01:00
|
|
|
inputMethodManager.hideSoftInputFromWindow(chatURLEditText.windowToken, 0)
|
2021-06-02 02:51:36 +02:00
|
|
|
val chatURL = chatURLEditText.text.trim().toString().toLowerCase(Locale.US)
|
2020-11-20 08:59:13 +01:00
|
|
|
(requireActivity() as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
|
2019-12-18 15:25:23 +01:00
|
|
|
}
|
2021-04-23 09:49:24 +02:00
|
|
|
// endregion
|
2019-12-18 15:25:23 +01:00
|
|
|
}
|
|
|
|
// endregion
|