mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
feat: handling default group requests and open group api updates for proper image endpoint handling
This commit is contained in:
parent
c601098065
commit
f9939aae92
@ -57,48 +57,49 @@ import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.session.libsession.messaging.messages.visible.Quote;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI;
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPIV2;
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupV2;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.SimpleTask;
|
||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.MessageDetailsActivity;
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import org.thoughtcrime.securesms.ShareActivity;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.components.ConversationTypingView;
|
||||
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
||||
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.SimpleTask;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -396,7 +397,8 @@ public class ConversationFragment extends Fragment
|
||||
|
||||
if (isGroupChat) {
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
boolean isPublicChat = (publicChat != null);
|
||||
OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
|
||||
boolean isPublicChat = (publicChat != null || openGroupChat != null);
|
||||
int selectedMessageCount = messageRecords.size();
|
||||
boolean areAllSentByUser = true;
|
||||
Set<String> uniqueUserSet = new HashSet<>();
|
||||
@ -407,7 +409,11 @@ public class ConversationFragment extends Fragment
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1);
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||
boolean userCanModerate = isPublicChat && OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer());
|
||||
boolean userCanModerate =
|
||||
(isPublicChat &&
|
||||
(OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer())
|
||||
|| OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer()))
|
||||
);
|
||||
boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate);
|
||||
// allow banning if moderating a public chat and only one user's messages are selected
|
||||
boolean isBanOptionVisible = isPublicChat && userCanModerate && !areAllSentByUser && uniqueUserSet.size() == 1;
|
||||
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.activities
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import android.util.Patterns
|
||||
import android.view.LayoutInflater
|
||||
@ -9,14 +10,20 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.*
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.chip.Chip
|
||||
import kotlinx.android.synthetic.main.activity_join_public_chat.*
|
||||
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
import okhttp3.HttpUrl
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPIV2.DefaultGroup
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
@ -24,11 +31,13 @@ import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||
import org.thoughtcrime.securesms.loki.viewmodel.DefaultGroup
|
||||
import org.thoughtcrime.securesms.loki.viewmodel.DefaultGroupsViewModel
|
||||
import org.thoughtcrime.securesms.loki.viewmodel.State
|
||||
|
||||
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||
|
||||
private val viewModel by viewModels<DefaultGroupsViewModel>()
|
||||
|
||||
private val adapter = JoinPublicChatActivityAdapter(this)
|
||||
|
||||
// region Lifecycle
|
||||
@ -70,12 +79,24 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
||||
if (!Patterns.WEB_URL.matcher(url).matches() || !url.startsWith("https://")) {
|
||||
return Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
val properString = if (!url.startsWith("http")) "http://$url" else url
|
||||
val httpUrl = HttpUrl.parse(url) ?: return Toast.makeText(this,R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||
|
||||
val room = httpUrl.pathSegments().firstOrNull()
|
||||
val publicKey = httpUrl.queryParameter("public_key")
|
||||
val isV2OpenGroup = !room.isNullOrEmpty()
|
||||
showLoader()
|
||||
val channel: Long = 1
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, url, channel)
|
||||
if (isV2OpenGroup) {
|
||||
val server = httpUrl.newBuilder().removeAllQueryParameters("public_key").removePathSegment(0).build().toString()
|
||||
OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, server, room, publicKey)
|
||||
} else {
|
||||
OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, url, channel)
|
||||
}
|
||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@JoinPublicChatActivity)
|
||||
} catch (e: Exception) {
|
||||
Log.e("JoinPublicChatActivity", "Fialed to join open group.", e)
|
||||
@ -111,7 +132,7 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPageTitle(index: Int): CharSequence? {
|
||||
override fun getPageTitle(index: Int): CharSequence {
|
||||
return when (index) {
|
||||
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)
|
||||
@ -124,7 +145,6 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
|
||||
// region Enter Chat URL Fragment
|
||||
class EnterChatURLFragment : Fragment() {
|
||||
|
||||
// factory producer is app scoped because default groups will want to stick around for app lifetime
|
||||
private val viewModel by activityViewModels<DefaultGroupsViewModel>()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
@ -132,7 +152,23 @@ class EnterChatURLFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun populateDefaultGroups(groups: List<DefaultGroup>) {
|
||||
Log.d("Loki", "Got some default groups $groups")
|
||||
defaultRoomsGridLayout.removeAllViews()
|
||||
groups.forEach { defaultGroup ->
|
||||
val chip = layoutInflater.inflate(R.layout.default_group_chip,defaultRoomsGridLayout, false) as Chip
|
||||
val drawable = defaultGroup.image?.let { bytes ->
|
||||
val bitmap = BitmapFactory.decodeByteArray(bytes,0,bytes.size)
|
||||
RoundedBitmapDrawableFactory.create(resources,bitmap).apply {
|
||||
isCircular = true
|
||||
}
|
||||
}
|
||||
chip.chipIcon = drawable
|
||||
chip.text = defaultGroup.name
|
||||
defaultRoomsGridLayout.addView(chip)
|
||||
}
|
||||
if (groups.size and 1 != 0) {
|
||||
// add a filler weight 1 view
|
||||
layoutInflater.inflate(R.layout.grid_layout_filler, defaultRoomsGridLayout)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -140,6 +176,8 @@ class EnterChatURLFragment : Fragment() {
|
||||
chatURLEditText.imeOptions = chatURLEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||
joinPublicChatButton.setOnClickListener { joinPublicChatIfPossible() }
|
||||
viewModel.defaultRooms.observe(viewLifecycleOwner) { state ->
|
||||
defaultRoomsParent.isVisible = state is State.Success
|
||||
defaultRoomsLoader.isVisible = state is State.Loading
|
||||
when (state) {
|
||||
State.Loading -> {
|
||||
// show a loader here probs
|
||||
|
@ -123,7 +123,7 @@ class PublicChatManager(private val context: Context) {
|
||||
if (threadID < 0) {
|
||||
val imageID = info.imageID
|
||||
if (!imageID.isNullOrEmpty()) {
|
||||
val profilePictureAsByteArray = OpenGroupAPIV2.downloadOpenGroupProfilePicture(imageID)
|
||||
val profilePictureAsByteArray = OpenGroupAPIV2.downloadOpenGroupProfilePicture(info.id,server)
|
||||
profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray)
|
||||
}
|
||||
val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, info.name)
|
||||
|
@ -25,8 +25,10 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
private val friendRequestStatus = "friend_request_status"
|
||||
private val sessionResetStatus = "session_reset_status"
|
||||
val publicChat = "public_chat"
|
||||
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||
@JvmStatic
|
||||
val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic
|
||||
val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||
}
|
||||
|
||||
override fun getThreadID(hexEncodedPublicKey: String): Long {
|
||||
@ -45,11 +47,13 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
val threadID = cursor.getLong(threadID)
|
||||
val string = cursor.getString(publicChat)
|
||||
val publicChat = PublicChat.fromJSON(string)
|
||||
if (publicChat != null) { result[threadID] = publicChat }
|
||||
if (publicChat != null) {
|
||||
result[threadID] = publicChat
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Do nothing
|
||||
} finally {
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return result
|
||||
@ -79,25 +83,40 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) }
|
||||
}
|
||||
|
||||
override fun getPublicChat(threadID: Long): PublicChat? {
|
||||
if (threadID < 0) { return null }
|
||||
fun getOpenGroupChat(threadID: Long): OpenGroupV2? {
|
||||
if (threadID < 0) {
|
||||
return null
|
||||
}
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
||||
return database.get(publicChat, "${Companion.threadID} = ?", arrayOf(threadID.toString())) { cursor ->
|
||||
val json = cursor.getString(publicChat)
|
||||
OpenGroupV2.fromJson(json)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPublicChat(threadID: Long): PublicChat? {
|
||||
if (threadID < 0) {
|
||||
return null
|
||||
}
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString())) { cursor ->
|
||||
val publicChatAsJSON = cursor.getString(publicChat)
|
||||
PublicChat.fromJSON(publicChatAsJSON)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setPublicChat(publicChat: PublicChat, threadID: Long) {
|
||||
if (threadID < 0) { return }
|
||||
if (threadID < 0) {
|
||||
return
|
||||
}
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.threadID, threadID)
|
||||
contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON()))
|
||||
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
||||
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf(threadID.toString()))
|
||||
}
|
||||
|
||||
override fun removePublicChat(threadID: Long) {
|
||||
databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
||||
databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString()))
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import androidx.annotation.WorkerThread
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupV2
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||
@ -18,6 +19,18 @@ object OpenGroupUtilities {
|
||||
|
||||
private const val TAG = "OpenGroupUtilities"
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
fun addGroup(context: Context, server: String, room: String, publicKey: String?): OpenGroupV2 {
|
||||
val groupId = "$server.$room"
|
||||
val threadID = GroupManager.getOpenGroupThreadID(groupId, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
||||
if (openGroup != null) return openGroup
|
||||
|
||||
val application = ApplicationContext.getInstance(context)
|
||||
val group = application.publicChatManager.addChat(server, room, publicKey)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
|
@ -1,10 +1,13 @@
|
||||
package org.thoughtcrime.securesms.loki.viewmodel
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPIV2
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
|
||||
typealias DefaultGroups = List<OpenGroupAPIV2.DefaultGroup>
|
||||
typealias GroupState = State<DefaultGroups>
|
||||
|
||||
class DefaultGroupsViewModel : ViewModel() {
|
||||
|
||||
@ -12,28 +15,10 @@ class DefaultGroupsViewModel : ViewModel() {
|
||||
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
|
||||
}
|
||||
|
||||
val defaultRooms = OpenGroupAPIV2.defaultRooms.asLiveData().distinctUntilChanged().switchMap { groups ->
|
||||
liveData {
|
||||
// load images etc
|
||||
emit(State.Loading)
|
||||
val images = groups.filterNot { it.imageID.isNullOrEmpty() }.map { group ->
|
||||
val image = viewModelScope.async(Dispatchers.IO) {
|
||||
try {
|
||||
OpenGroupAPIV2.downloadOpenGroupProfilePicture(group.imageID!!)
|
||||
} catch (e: Exception) {
|
||||
Log.e("Loki", "Error getting group profile picture", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
group.id to image
|
||||
}.toMap()
|
||||
val defaultGroups = groups.map { group ->
|
||||
DefaultGroup(group.id, group.name, images[group.id]?.await())
|
||||
}
|
||||
emit(State.Success(defaultGroups))
|
||||
}
|
||||
}
|
||||
val defaultRooms = OpenGroupAPIV2.defaultRooms.map<DefaultGroups, GroupState> {
|
||||
State.Success(it)
|
||||
}.onStart {
|
||||
emit(State.Loading)
|
||||
}.asLiveData()
|
||||
|
||||
}
|
||||
|
||||
data class DefaultGroup(val id: String, val name: String, val image: ByteArray?)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package org.thoughtcrime.securesms.loki.viewmodel
|
||||
|
||||
sealed class State<T> {
|
||||
sealed class State<out T> {
|
||||
object Loading : State<Nothing>()
|
||||
data class Success<T>(val value: T): State<T>()
|
||||
data class Error(val error: Exception): State<Nothing>()
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/contentView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -24,47 +23,39 @@
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:hint="@string/fragment_enter_chat_url_edit_text_hint" />
|
||||
|
||||
<com.github.ybq.android.spinkit.SpinKitView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/defaultRoomsLoader"
|
||||
style="@style/SpinKitView.Small.WanderingCubes"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:id="@+id/defaultRoomsParent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginVertical="16dp"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_join_public_chat_join_rooms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<GridLayout
|
||||
android:id="@+id/defaultRoomsGridLayout"
|
||||
android:columnCount="2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
<androidx.gridlayout.widget.GridLayout
|
||||
app:columnCount="2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.chip.Chip
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
style="?attr/chipStyle"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:textStartPadding="10dp"
|
||||
android:text="Main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
style="?attr/chipStyle"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:textStartPadding="10dp"
|
||||
android:text="Main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
style="?attr/chipStyle"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:textStartPadding="10dp"
|
||||
android:text="Main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
style="?attr/chipStyle"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:textStartPadding="10dp"
|
||||
android:text="Main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</androidx.gridlayout.widget.GridLayout>
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Common.ProminentOutline"
|
||||
|
15
app/src/main/res/layout/default_group_chip.xml
Normal file
15
app/src/main/res/layout/default_group_chip.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
style="?attr/chipStyle"
|
||||
app:textStartPadding="10dp"
|
||||
app:textEndPadding="10dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
tools:text="Main Group"
|
||||
android:ellipsize="end"
|
||||
tools:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="52dp" />
|
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/contentView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -24,19 +23,40 @@
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:hint="@string/fragment_enter_chat_url_edit_text_hint" />
|
||||
|
||||
<com.github.ybq.android.spinkit.SpinKitView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/defaultRoomsLoader"
|
||||
style="@style/SpinKitView.Small.WanderingCubes"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:id="@+id/defaultRoomsParent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginVertical="16dp"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_join_public_chat_join_rooms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<GridLayout
|
||||
android:id="@+id/defaultRoomsGridLayout"
|
||||
android:columnCount="2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
app:closeIconEnabled="true"
|
||||
style="?attr/chipStyle"
|
||||
android:text="Main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Common.ProminentOutline"
|
||||
android:id="@+id/joinPublicChatButton"
|
||||
|
5
app/src/main/res/layout/grid_layout_filler.xml
Normal file
5
app/src/main/res/layout/grid_layout_filler.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_height="0dp"/>
|
@ -1884,5 +1884,6 @@
|
||||
<string name="activity_backup_restore_passphrase">30-digit passphrase</string>
|
||||
<!-- LinkDeviceActivity -->
|
||||
<string name="activity_link_device_skip_prompt">This is taking a while, would you like to skip?</string>
|
||||
<string name="activity_join_public_chat_join_rooms">Or join one of these...</string>
|
||||
|
||||
</resources>
|
||||
|
@ -30,10 +30,10 @@ import java.util.*
|
||||
object OpenGroupAPIV2 {
|
||||
|
||||
private val moderators: HashMap<String, Set<String>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
|
||||
private const val DEFAULT_SERVER = "https://sog.ibolpap.finance"
|
||||
const val DEFAULT_SERVER = "https://sog.ibolpap.finance"
|
||||
private const val DEFAULT_SERVER_PUBLIC_KEY = "b464aa186530c97d6bcf663a3a3b7465a5f782beaa67c83bee99468824b4aa10"
|
||||
|
||||
val defaultRooms = MutableSharedFlow<List<Info>>(replay = 1)
|
||||
val defaultRooms = MutableSharedFlow<List<DefaultGroup>>(replay = 1)
|
||||
|
||||
private val sharedContext = Kovenant.createContext()
|
||||
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
||||
@ -47,6 +47,10 @@ object OpenGroupAPIV2 {
|
||||
object NO_PUBLIC_KEY : Error()
|
||||
}
|
||||
|
||||
data class DefaultGroup(val id: String,
|
||||
val name: String,
|
||||
val image: ByteArray?)
|
||||
|
||||
data class Info(
|
||||
val id: String,
|
||||
val name: String,
|
||||
@ -77,7 +81,7 @@ object OpenGroupAPIV2 {
|
||||
val urlBuilder = HttpUrl.Builder()
|
||||
.scheme(parsed.scheme())
|
||||
.host(parsed.host())
|
||||
.addPathSegment(request.endpoint)
|
||||
.addPathSegments(request.endpoint)
|
||||
|
||||
if (request.verb == GET) {
|
||||
for ((key, value) in request.queryParameters) {
|
||||
@ -139,6 +143,14 @@ object OpenGroupAPIV2 {
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadOpenGroupProfilePicture(roomID: String, server: String): Promise<ByteArray, Exception> {
|
||||
val request = Request(verb = GET, room = roomID, server = server, endpoint = "rooms/$roomID/image", isAuthRequired = false)
|
||||
return send(request).map(sharedContext) { json ->
|
||||
val result = json["result"] as? String ?: throw Error.PARSING_FAILED
|
||||
decode(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun getAuthToken(room: String, server: String): Promise<String, Exception> {
|
||||
val storage = MessagingConfiguration.shared.storage
|
||||
return storage.getAuthToken(room, server)?.let {
|
||||
@ -312,15 +324,30 @@ object OpenGroupAPIV2 {
|
||||
}
|
||||
}
|
||||
|
||||
fun isUserModerator(publicKey: String, room: String, server: String): Boolean = moderators["$server.$room"]?.contains(publicKey)
|
||||
?: false
|
||||
@JvmStatic
|
||||
fun isUserModerator(publicKey: String, room: String, server: String): Boolean =
|
||||
moderators["$server.$room"]?.contains(publicKey) ?: false
|
||||
// endregion
|
||||
|
||||
// region General
|
||||
fun getDefaultRoomsIfNeeded(): Promise<List<Info>, Exception> {
|
||||
fun getDefaultRoomsIfNeeded(): Promise<List<DefaultGroup>, Exception> {
|
||||
val storage = MessagingConfiguration.shared.storage
|
||||
storage.setOpenGroupPublicKey(DEFAULT_SERVER, DEFAULT_SERVER_PUBLIC_KEY)
|
||||
return getAllRooms(DEFAULT_SERVER).success { new ->
|
||||
return getAllRooms(DEFAULT_SERVER).map { groups ->
|
||||
val images = groups.map { group ->
|
||||
group.id to downloadOpenGroupProfilePicture(group.id, DEFAULT_SERVER)
|
||||
}.toMap()
|
||||
|
||||
groups.map { group ->
|
||||
val image = try {
|
||||
images[group.id]!!.get()
|
||||
} catch (e: Exception) {
|
||||
// no image or image failed to download
|
||||
null
|
||||
}
|
||||
DefaultGroup(group.id, group.name, image)
|
||||
}
|
||||
}.success { new ->
|
||||
defaultRooms.tryEmit(new)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user