mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 12:09:45 +01:00
Compiles again
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
4b38eb1d19
commit
ca4998614c
@ -2,11 +2,11 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "8b9e5dddd027e51eb17ffd53d365e6d4",
|
||||
"identityHash": "c81b4edb8abdd29b77836d16a7d991c2",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "conversations",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user` INTEGER, `conversation_id` TEXT, `token` TEXT, `name` TEXT, `display_name` TEXT, `type` INTEGER, `count` INTEGER NOT NULL, `number_of_guests` INTEGER NOT NULL, `participants_count` INTEGER NOT NULL, `participant_type` INTEGER, `has_password` INTEGER NOT NULL, `session_id` TEXT, `favorite` INTEGER NOT NULL, `last_activity` INTEGER NOT NULL, `unread_messages` INTEGER NOT NULL, `unread_mention` INTEGER NOT NULL, `last_message` TEXT, `object_type` TEXT, `notification_level` INTEGER, `read_only_state` INTEGER, `lobby_state` INTEGER, `lobby_timer` INTEGER, `last_read_message_id` INTEGER NOT NULL, `modified_at` INTEGER, `changing` INTEGER NOT NULL, FOREIGN KEY(`user`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user` INTEGER, `conversation_id` TEXT, `token` TEXT, `name` TEXT, `display_name` TEXT, `type` INTEGER, `count` INTEGER NOT NULL, `number_of_guests` INTEGER NOT NULL, `participants_count` INTEGER NOT NULL, `participant_type` INTEGER, `has_password` INTEGER NOT NULL, `session_id` TEXT, `favorite` INTEGER NOT NULL, `last_activity` INTEGER NOT NULL, `unread_messages` INTEGER NOT NULL, `unread_mention` INTEGER NOT NULL, `last_message` TEXT, `object_type` TEXT, `notification_level` INTEGER, `read_only_state` INTEGER, `lobby_state` INTEGER, `lobby_timer` INTEGER, `last_read_message` INTEGER NOT NULL, `modified_at` INTEGER, `changing` INTEGER NOT NULL, FOREIGN KEY(`user`) REFERENCES `users`(`id`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@ -148,7 +148,7 @@
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastReadMessageId",
|
||||
"columnName": "last_read_message_id",
|
||||
"columnName": "last_read_message",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
@ -173,19 +173,20 @@
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_conversations_user",
|
||||
"unique": false,
|
||||
"name": "index_conversations_user_conversation_id",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"user"
|
||||
"user",
|
||||
"conversation_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_conversations_user` ON `${TABLE_NAME}` (`user`)"
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_conversations_user_conversation_id` ON `${TABLE_NAME}` (`user`, `conversation_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "users",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"user"
|
||||
],
|
||||
@ -197,7 +198,7 @@
|
||||
},
|
||||
{
|
||||
"tableName": "messages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user` INTEGER, `conversation` INTEGER, `message_id` INTEGER NOT NULL, `actor_id` TEXT, `actor_type` TEXT, `actor_display_name` TEXT, `timestamp` INTEGER NOT NULL, `message` TEXT, `system_message_type` TEXT, FOREIGN KEY(`conversation`) REFERENCES `conversations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `conversation` INTEGER, `message_id` INTEGER NOT NULL, `actor_id` TEXT, `actor_type` TEXT, `actor_display_name` TEXT, `timestamp` INTEGER NOT NULL, `message` TEXT, `system_message_type` TEXT, FOREIGN KEY(`conversation`) REFERENCES `conversations`(`id`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@ -205,12 +206,6 @@
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "user",
|
||||
"columnName": "user",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "conversation",
|
||||
"columnName": "conversation",
|
||||
@ -274,22 +269,13 @@
|
||||
"conversation"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_messages_conversation` ON `${TABLE_NAME}` (`conversation`)"
|
||||
},
|
||||
{
|
||||
"name": "index_messages_user_conversation",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"user",
|
||||
"conversation"
|
||||
],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_messages_user_conversation` ON `${TABLE_NAME}` (`user`, `conversation`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "conversations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"conversation"
|
||||
],
|
||||
@ -301,25 +287,31 @@
|
||||
},
|
||||
{
|
||||
"tableName": "users",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `user_id` TEXT, `username` TEXT, `token` TEXT, `display_name` TEXT, `push_configuration` TEXT, `capabilities` TEXT, `client_auth_cert` TEXT, `external_signaling` TEXT, `status` INTEGER)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `user_id` TEXT NOT NULL, `username` TEXT NOT NULL, `base_url` TEXT NOT NULL, `token` TEXT, `display_name` TEXT, `push_configuration` TEXT, `capabilities` TEXT, `client_auth_cert` TEXT, `external_signaling` TEXT, `status` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "user_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "baseUrl",
|
||||
"columnName": "base_url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
@ -377,7 +369,7 @@
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8b9e5dddd027e51eb17ffd53d365e6d4')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c81b4edb8abdd29b77836d16a7d991c2')"
|
||||
]
|
||||
}
|
||||
}
|
@ -76,7 +76,7 @@ class MagicCallActivity : BaseActivity() {
|
||||
)
|
||||
} else {
|
||||
router!!.setRoot(
|
||||
RouterTransaction.with(CallController(intent.extras))
|
||||
RouterTransaction.with(CallController(intent.extras!!))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
|
@ -34,6 +34,7 @@ import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -54,7 +55,7 @@ import java.util.regex.Pattern
|
||||
|
||||
class ConversationItem(
|
||||
val model: Conversation,
|
||||
private val userEntity: UserEntity,
|
||||
private val user: UserNgEntity,
|
||||
private val context: Context
|
||||
) : AbstractFlexibleItem<ConversationItem.ConversationItemViewHolder>(), IFilterable<String> {
|
||||
|
||||
@ -74,7 +75,7 @@ class ConversationItem(
|
||||
&& model.unreadMention == comparedConversation.unreadMention
|
||||
&& model.objectType == comparedConversation.objectType
|
||||
&& model.changing == comparedConversation.changing
|
||||
&& userEntity.id == inItem.userEntity.id)
|
||||
&& user.id == inItem.user.id)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -82,7 +83,7 @@ class ConversationItem(
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(
|
||||
model.conversationId, model.token,
|
||||
userEntity.id
|
||||
user.id
|
||||
)
|
||||
}
|
||||
|
||||
@ -168,13 +169,13 @@ class ConversationItem(
|
||||
holder.itemView.dialogLastMessage!!.text = model.lastMessage!!.text
|
||||
} else {
|
||||
var authorDisplayName = ""
|
||||
model.lastMessage!!.activeUser = userEntity
|
||||
model.lastMessage!!.activeUser = user
|
||||
val text: String
|
||||
if (model.lastMessage!!
|
||||
.messageType == ChatMessage.MessageType.REGULAR_TEXT_MESSAGE && (!(ONE_TO_ONE_CONVERSATION).equals(
|
||||
model.type) || model.lastMessage!!.actorId == userEntity.userId)
|
||||
model.type) || model.lastMessage!!.actorId == user.userId)
|
||||
) {
|
||||
if (model.lastMessage!!.actorId == userEntity.userId) {
|
||||
if (model.lastMessage!!.actorId == user.userId) {
|
||||
text = String.format(
|
||||
appContext.getString(R.string.nc_formatted_message_you),
|
||||
model.lastMessage!!.lastMessageDisplayText
|
||||
@ -248,7 +249,7 @@ class ConversationItem(
|
||||
) {
|
||||
holder.itemView.dialogAvatar.load(
|
||||
ApiUtils.getUrlForAvatarWithName(
|
||||
userEntity.baseUrl,
|
||||
user.baseUrl,
|
||||
model.name, R.dimen.avatar_size
|
||||
)
|
||||
) {
|
||||
|
@ -35,6 +35,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -51,7 +52,7 @@ class UserItem(
|
||||
*/
|
||||
|
||||
val model: Participant,
|
||||
val entity: UserEntity,
|
||||
val entity: UserNgEntity,
|
||||
private var header: GenericTextHeaderItem?,
|
||||
private val activityContext: Context
|
||||
) : AbstractFlexibleItem<UserItem.UserItemViewHolder>(),
|
||||
|
@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.adapters.messages;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.net.Uri;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.emoji.widget.EmojiTextView;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import com.amulyakhare.textdrawable.TextDrawable;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.google.android.flexbox.FlexboxLayout;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.TextMatchers;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
import com.stfalcon.chatkit.messages.MessageHolders;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class MagicIncomingTextMessageViewHolder
|
||||
extends MessageHolders.IncomingTextMessageViewHolder<ChatMessage> {
|
||||
|
||||
@BindView(R.id.messageAuthor)
|
||||
EmojiTextView messageAuthor;
|
||||
|
||||
@BindView(R.id.messageText)
|
||||
EmojiTextView messageText;
|
||||
|
||||
@BindView(R.id.messageUserAvatar)
|
||||
SimpleDraweeView messageUserAvatarView;
|
||||
|
||||
@BindView(R.id.messageTime)
|
||||
TextView messageTimeView;
|
||||
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
|
||||
@Inject
|
||||
Context context;
|
||||
|
||||
@Inject
|
||||
AppPreferences appPreferences;
|
||||
|
||||
private View itemView;
|
||||
|
||||
public MagicIncomingTextMessageViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getComponentApplication()
|
||||
.inject(this);
|
||||
|
||||
this.itemView = itemView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(ChatMessage message) {
|
||||
super.onBind(message);
|
||||
String author;
|
||||
|
||||
if (!TextUtils.isEmpty(author = message.getActorDisplayName())) {
|
||||
messageAuthor.setText(author);
|
||||
} else {
|
||||
messageAuthor.setText(R.string.nc_nick_guest);
|
||||
}
|
||||
|
||||
if (!message.isGrouped() && !message.isOneToOneConversation()) {
|
||||
messageUserAvatarView.setVisibility(View.VISIBLE);
|
||||
if (message.getActorType().equals("guests")) {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.getActorType().equals("bots") && message.getActorId()
|
||||
.equals("changelog")) {
|
||||
messageUserAvatarView.setController(null);
|
||||
Drawable[] layers = new Drawable[2];
|
||||
layers[0] = context.getDrawable(R.drawable.ic_launcher_background);
|
||||
layers[1] = context.getDrawable(R.drawable.ic_launcher_foreground);
|
||||
LayerDrawable layerDrawable = new LayerDrawable(layers);
|
||||
|
||||
messageUserAvatarView.getHierarchy()
|
||||
.setPlaceholderImage(DisplayUtils.INSTANCE.getRoundedDrawable(layerDrawable));
|
||||
} else if (message.getActorType().equals("bots")) {
|
||||
messageUserAvatarView.setController(null);
|
||||
TextDrawable drawable =
|
||||
TextDrawable.builder().beginConfig().bold().endConfig().buildRound(">",
|
||||
context.getResources().getColor(R.color.black));
|
||||
messageUserAvatarView.setVisibility(View.VISIBLE);
|
||||
messageUserAvatarView.getHierarchy().setPlaceholderImage(drawable);
|
||||
}
|
||||
} else {
|
||||
if (message.isOneToOneConversation()) {
|
||||
messageUserAvatarView.setVisibility(View.GONE);
|
||||
} else {
|
||||
messageUserAvatarView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
messageAuthor.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
Resources resources = itemView.getResources();
|
||||
|
||||
int bg_bubble_color = resources.getColor(R.color.bg_message_list_incoming_bubble);
|
||||
|
||||
int bubbleResource = R.drawable.shape_incoming_message;
|
||||
|
||||
if (message.isGrouped) {
|
||||
bubbleResource = R.drawable.shape_grouped_incoming_message;
|
||||
}
|
||||
|
||||
Drawable bubbleDrawable = DisplayUtils.INSTANCE.getMessageSelector(bg_bubble_color,
|
||||
resources.getColor(R.color.transparent),
|
||||
bg_bubble_color, bubbleResource);
|
||||
ViewCompat.setBackground(bubble, bubbleDrawable);
|
||||
|
||||
HashMap<String, HashMap<String, String>> messageParameters = message.getMessageParameters();
|
||||
|
||||
itemView.setSelected(false);
|
||||
messageTimeView.setTextColor(context.getResources().getColor(R.color.warm_grey_four));
|
||||
|
||||
FlexboxLayout.LayoutParams layoutParams =
|
||||
(FlexboxLayout.LayoutParams) messageTimeView.getLayoutParams();
|
||||
layoutParams.setWrapBefore(false);
|
||||
|
||||
Spannable messageString = new SpannableString(message.getText());
|
||||
|
||||
float textSize = context.getResources().getDimension(R.dimen.chat_text_size);
|
||||
|
||||
if (messageParameters != null && messageParameters.size() > 0) {
|
||||
for (String key : messageParameters.keySet()) {
|
||||
Map<String, String> individualHashMap = message.getMessageParameters().get(key);
|
||||
if (individualHashMap != null) {
|
||||
if (individualHashMap.get("type").equals("user") || individualHashMap.get("type")
|
||||
.equals("guest") || individualHashMap.get("type").equals("call")) {
|
||||
if (individualHashMap.get("id").equals(message.getActiveUser().getUserId())) {
|
||||
messageString =
|
||||
DisplayUtils.INSTANCE.searchAndReplaceWithMentionSpan(messageText.getContext(),
|
||||
messageString,
|
||||
individualHashMap.get("id"),
|
||||
individualHashMap.get("name"),
|
||||
individualHashMap.get("type"),
|
||||
userUtils.getUserById(message.getActiveUser().getUserId()),
|
||||
R.xml.chip_you);
|
||||
} else {
|
||||
messageString =
|
||||
DisplayUtils.INSTANCE.searchAndReplaceWithMentionSpan(messageText.getContext(),
|
||||
messageString,
|
||||
individualHashMap.get("id"),
|
||||
individualHashMap.get("name"),
|
||||
individualHashMap.get("type"),
|
||||
userUtils.getUserById(message.getActiveUser().getUserId()),
|
||||
R.xml.chip_others);
|
||||
}
|
||||
} else if (individualHashMap.get("type").equals("file")) {
|
||||
itemView.setOnClickListener(v -> {
|
||||
Intent browserIntent =
|
||||
new Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap.get("link")));
|
||||
context.startActivity(browserIntent);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.getText())) {
|
||||
textSize = (float) (textSize * 2.5);
|
||||
layoutParams.setWrapBefore(true);
|
||||
itemView.setSelected(true);
|
||||
messageAuthor.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
|
||||
messageTimeView.setLayoutParams(layoutParams);
|
||||
messageText.setText(messageString);
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.adapters.messages
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.net.Uri
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.emoji.widget.EmojiTextView
|
||||
import autodagger.AutoInjector
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.TextMatchers
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.stfalcon.chatkit.messages.MessageHolders
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
||||
.IncomingTextMessageViewHolder<ChatMessage>(incomingView), KoinComponent {
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.messageAuthor)
|
||||
var messageAuthor: EmojiTextView? = null
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.messageText)
|
||||
var messageText: EmojiTextView? = null
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.messageUserAvatar)
|
||||
var messageUserAvatarView: SimpleDraweeView? = null
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.messageTime)
|
||||
var messageTimeView: TextView? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var context: Context? = null
|
||||
|
||||
val appPreferences: AppPreferences by inject()
|
||||
|
||||
init {
|
||||
ButterKnife.bind(
|
||||
this,
|
||||
itemView
|
||||
)
|
||||
NextcloudTalkApplication.sharedApplication!!
|
||||
.componentApplication
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
override fun onBind(message: ChatMessage) {
|
||||
super.onBind(message)
|
||||
val author: String = message.actorDisplayName
|
||||
if (!TextUtils.isEmpty(author)) {
|
||||
messageAuthor!!.text = author
|
||||
} else {
|
||||
messageAuthor!!.setText(R.string.nc_nick_guest)
|
||||
}
|
||||
|
||||
if (!message.grouped && !message.oneToOneConversation) {
|
||||
messageUserAvatarView!!.visibility = View.VISIBLE
|
||||
if (message.actorType == "guests") {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.actorType == "bots" && message.actorType == "changelog") {
|
||||
messageUserAvatarView!!.controller = null
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = context!!.getDrawable(R.drawable.ic_launcher_background)
|
||||
layers[1] = context!!.getDrawable(R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
|
||||
messageUserAvatarView!!.hierarchy
|
||||
.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
} else if (message.actorType == "bots") {
|
||||
messageUserAvatarView!!.controller = null
|
||||
val drawable = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(
|
||||
">",
|
||||
context!!.resources.getColor(R.color.black)
|
||||
)
|
||||
messageUserAvatarView!!.visibility = View.VISIBLE
|
||||
messageUserAvatarView!!.hierarchy.setPlaceholderImage(drawable)
|
||||
}
|
||||
} else {
|
||||
if (message.oneToOneConversation) {
|
||||
messageUserAvatarView!!.visibility = View.GONE
|
||||
} else {
|
||||
messageUserAvatarView!!.visibility = View.INVISIBLE
|
||||
}
|
||||
messageAuthor!!.visibility = View.GONE
|
||||
}
|
||||
|
||||
val resources = itemView.getResources()
|
||||
|
||||
val bg_bubble_color = resources.getColor(R.color.bg_message_list_incoming_bubble)
|
||||
|
||||
var bubbleResource = R.drawable.shape_incoming_message
|
||||
|
||||
if (message.grouped) {
|
||||
bubbleResource = R.drawable.shape_grouped_incoming_message
|
||||
}
|
||||
|
||||
val bubbleDrawable = DisplayUtils.getMessageSelector(
|
||||
bg_bubble_color,
|
||||
resources.getColor(R.color.transparent),
|
||||
bg_bubble_color, bubbleResource
|
||||
)
|
||||
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||
|
||||
val messageParameters = message.messageParameters
|
||||
|
||||
itemView.setSelected(false)
|
||||
messageTimeView!!.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
|
||||
|
||||
val layoutParams = messageTimeView!!.layoutParams as FlexboxLayout.LayoutParams
|
||||
layoutParams.isWrapBefore = false
|
||||
|
||||
var messageString: Spannable = SpannableString(message.text)
|
||||
|
||||
var textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
|
||||
|
||||
if (messageParameters != null && messageParameters.size > 0) {
|
||||
for (key in messageParameters.keys) {
|
||||
val individualHashMap = message.messageParameters[key]
|
||||
if (individualHashMap != null) {
|
||||
if (individualHashMap["type"] == "user" || individualHashMap["type"] == "guest" || individualHashMap["type"] == "call") {
|
||||
if (individualHashMap["id"] == message.activeUser.userId) {
|
||||
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
|
||||
messageText!!.context,
|
||||
messageString,
|
||||
individualHashMap["id"]!!,
|
||||
individualHashMap["name"]!!,
|
||||
individualHashMap["type"]!!,
|
||||
message.activeUser,
|
||||
R.xml.chip_you
|
||||
)
|
||||
} else {
|
||||
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
|
||||
messageText!!.context,
|
||||
messageString,
|
||||
individualHashMap["id"]!!,
|
||||
individualHashMap["name"]!!,
|
||||
individualHashMap["type"]!!,
|
||||
message.activeUser,
|
||||
R.xml.chip_others
|
||||
)
|
||||
}
|
||||
} else if (individualHashMap["type"] == "file") {
|
||||
itemView.setOnClickListener({ v ->
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
|
||||
context!!.startActivity(browserIntent)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
|
||||
textSize = (textSize * 2.5).toFloat()
|
||||
layoutParams.isWrapBefore = true
|
||||
itemView.setSelected(true)
|
||||
messageAuthor!!.visibility = View.GONE
|
||||
}
|
||||
|
||||
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||
messageTimeView!!.layoutParams = layoutParams
|
||||
messageText!!.text = messageString
|
||||
}
|
||||
}
|
@ -103,7 +103,7 @@ public class MagicOutcomingTextMessageViewHolder
|
||||
individualHashMap.get("id"),
|
||||
individualHashMap.get("name"),
|
||||
individualHashMap.get("type"),
|
||||
userUtils.getUserById(message.getActiveUser().getUserId()),
|
||||
message.activeUser,
|
||||
R.xml.chip_others);
|
||||
} else if (individualHashMap.get("type").equals("file")) {
|
||||
itemView.setOnClickListener(v -> {
|
||||
@ -122,7 +122,7 @@ public class MagicOutcomingTextMessageViewHolder
|
||||
}
|
||||
|
||||
Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources();
|
||||
if (message.isGrouped) {
|
||||
if (message.grouped) {
|
||||
Drawable bubbleDrawable =
|
||||
DisplayUtils.INSTANCE.getMessageSelector(
|
||||
resources.getColor(R.color.bg_message_list_outcoming_bubble),
|
||||
|
@ -43,12 +43,12 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
|
||||
import com.nextcloud.talk.components.filebrowser.models.BrowserFile
|
||||
import com.nextcloud.talk.components.filebrowser.models.DavResponse
|
||||
import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.AccountUtils.canWeOpenFilesApp
|
||||
import com.nextcloud.talk.utils.DisplayUtils.setClickableString
|
||||
import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType
|
||||
@ -80,8 +80,8 @@ class MagicPreviewMessageViewHolder(itemView: View?) : IncomingImageMessageViewH
|
||||
override fun onBind(message: ChatMessage) {
|
||||
super.onBind(message)
|
||||
if (userAvatar != null) {
|
||||
if (message.isGrouped || message.isOneToOneConversation) {
|
||||
if (message.isOneToOneConversation) {
|
||||
if (message.grouped || message.oneToOneConversation) {
|
||||
if (message.oneToOneConversation) {
|
||||
userAvatar.visibility = View.GONE
|
||||
} else {
|
||||
userAvatar.visibility = View.INVISIBLE
|
||||
@ -183,7 +183,7 @@ class MagicPreviewMessageViewHolder(itemView: View?) : IncomingImageMessageViewH
|
||||
|
||||
private fun fetchFileInformation(
|
||||
url: String,
|
||||
activeUser: UserEntity?
|
||||
activeUser: UserNgEntity?
|
||||
) {
|
||||
Single.fromCallable {
|
||||
ReadFilesystemOperation(
|
||||
|
@ -246,15 +246,25 @@ class NextcloudTalkApplication : Application(), LifecycleObserver {
|
||||
var userNg: UserNgEntity
|
||||
val newUsers = mutableListOf<UserNgEntity>()
|
||||
for (user in users) {
|
||||
userNg = UserNgEntity()
|
||||
userNg.userId = user.userId
|
||||
userNg.username = user.username
|
||||
userNg = UserNgEntity(user.id, user.userId, user.username, user.baseUrl)
|
||||
userNg.token = user.token
|
||||
userNg.displayName = user.displayName
|
||||
userNg.pushConfiguration = LoganSquare.parse(user.pushConfigurationState, PushConfigurationState::class.java)
|
||||
userNg.capabilities = LoganSquare.parse(user.capabilities, Capabilities::class.java)
|
||||
try {
|
||||
userNg.pushConfiguration =
|
||||
LoganSquare.parse(user.pushConfigurationState, PushConfigurationState::class.java)
|
||||
} catch (e: Exception) {
|
||||
// no push
|
||||
}
|
||||
if (user.capabilities != null) {
|
||||
userNg.capabilities = LoganSquare.parse(user.capabilities, Capabilities::class.java)
|
||||
}
|
||||
userNg.clientCertificate = user.clientCertificate
|
||||
userNg.externalSignaling = LoganSquare.parse(user.externalSignalingServer, ExternalSignalingServer::class.java)
|
||||
try {
|
||||
userNg.externalSignaling =
|
||||
LoganSquare.parse(user.externalSignalingServer, ExternalSignalingServer::class.java)
|
||||
} catch (e: Exception) {
|
||||
// no external signaling
|
||||
}
|
||||
if (user.current) {
|
||||
userNg.status = ACTIVE
|
||||
} else {
|
||||
|
@ -28,6 +28,7 @@ import com.facebook.widget.text.span.BetterImageSpan;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.mention.Mention;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.MagicCharPolicy;
|
||||
import com.nextcloud.talk.utils.text.Spans;
|
||||
@ -37,10 +38,10 @@ import com.vanniktech.emoji.EmojiUtils;
|
||||
|
||||
public class MentionAutocompleteCallback implements AutocompleteCallback<Mention> {
|
||||
private Context context;
|
||||
private UserEntity conversationUser;
|
||||
private UserNgEntity conversationUser;
|
||||
private EditText editText;
|
||||
|
||||
public MentionAutocompleteCallback(Context context, UserEntity conversationUser,
|
||||
public MentionAutocompleteCallback(Context context, UserNgEntity conversationUser,
|
||||
EditText editText) {
|
||||
this.context = context;
|
||||
this.conversationUser = conversationUser;
|
||||
|
@ -35,6 +35,8 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.components.filebrowser.models.BrowserFile
|
||||
import com.nextcloud.talk.interfaces.SelectionInterface
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
import com.nextcloud.talk.newarch.utils.getCredentials
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
@ -49,7 +51,7 @@ import javax.inject.Inject
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class BrowserFileItem(
|
||||
val model: BrowserFile,
|
||||
private val activeUser: UserEntity,
|
||||
private val activeUser: UserNgEntity,
|
||||
private val selectionInterface: SelectionInterface
|
||||
) : AbstractFlexibleItem<BrowserFileItem.ViewHolder>(), IFilterable<String> {
|
||||
@JvmField
|
||||
@ -63,9 +65,9 @@ class BrowserFileItem(
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
override fun equals(o: Any?): Boolean {
|
||||
if (o is BrowserFileItem) {
|
||||
val inItem = o as BrowserFileItem?
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is BrowserFileItem) {
|
||||
val inItem = other as BrowserFileItem?
|
||||
return model.path == inItem!!.model.path
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ import com.nextcloud.talk.controllers.base.BaseController;
|
||||
import com.nextcloud.talk.interfaces.SelectionInterface;
|
||||
import com.nextcloud.talk.jobs.ShareOperationWorker;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
import eu.davidea.fastscroller.FastScroller;
|
||||
@ -73,8 +74,6 @@ import org.parceler.Parcels;
|
||||
public class BrowserController extends BaseController implements ListingInterface,
|
||||
FlexibleAdapter.OnItemClickListener, SelectionInterface {
|
||||
private final Set<String> selectedPaths;
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
@BindView(R.id.recyclerView)
|
||||
RecyclerView recyclerView;
|
||||
@BindView(R.id.fast_scroller)
|
||||
@ -97,7 +96,7 @@ public class BrowserController extends BaseController implements ListingInterfac
|
||||
private ListingAbstractClass listingAbstractClass;
|
||||
private BrowserType browserType;
|
||||
private String currentPath;
|
||||
private UserEntity activeUser;
|
||||
private UserNgEntity activeUser;
|
||||
private String roomToken;
|
||||
|
||||
public BrowserController(Bundle args) {
|
||||
|
@ -25,6 +25,7 @@ import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface;
|
||||
import com.nextcloud.talk.components.filebrowser.models.DavResponse;
|
||||
import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
@ -40,7 +41,7 @@ public class DavListing extends ListingAbstractClass {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getFiles(String path, UserEntity currentUser, @Nullable OkHttpClient okHttpClient) {
|
||||
public void getFiles(String path, UserNgEntity currentUser, @Nullable OkHttpClient okHttpClient) {
|
||||
Single.fromCallable(new Callable<ReadFilesystemOperation>() {
|
||||
@Override
|
||||
public ReadFilesystemOperation call() {
|
||||
|
@ -24,6 +24,7 @@ import android.os.Handler;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public abstract class ListingAbstractClass {
|
||||
@ -35,7 +36,7 @@ public abstract class ListingAbstractClass {
|
||||
this.listingInterface = listingInterface;
|
||||
}
|
||||
|
||||
public abstract void getFiles(String path, UserEntity currentUser,
|
||||
public abstract void getFiles(String path, UserNgEntity currentUser,
|
||||
@Nullable OkHttpClient okHttpClient);
|
||||
|
||||
public void cancelAllJobs() {
|
||||
|
@ -27,6 +27,7 @@ import com.nextcloud.talk.components.filebrowser.models.BrowserFile;
|
||||
import com.nextcloud.talk.components.filebrowser.models.DavResponse;
|
||||
import com.nextcloud.talk.dagger.modules.RestModule;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -42,7 +43,7 @@ public class ReadFilesystemOperation {
|
||||
private final int depth;
|
||||
private final String basePath;
|
||||
|
||||
public ReadFilesystemOperation(OkHttpClient okHttpClient, UserEntity currentUser, String path,
|
||||
public ReadFilesystemOperation(OkHttpClient okHttpClient, UserNgEntity currentUser, String path,
|
||||
int depth) {
|
||||
OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder();
|
||||
okHttpClientBuilder.followRedirects(false);
|
||||
|
File diff suppressed because it is too large
Load Diff
2478
app/src/main/java/com/nextcloud/talk/controllers/CallController.kt
Normal file
2478
app/src/main/java/com/nextcloud/talk/controllers/CallController.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -73,7 +73,6 @@ import com.nextcloud.talk.components.filebrowser.controllers.BrowserController
|
||||
import com.nextcloud.talk.controllers.base.BaseController
|
||||
import com.nextcloud.talk.events.UserMentionClickEvent
|
||||
import com.nextcloud.talk.events.WebSocketCommunicationEvent
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverall
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
@ -81,8 +80,10 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.mention.Mention
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
import com.nextcloud.talk.newarch.local.models.maxMessageLength
|
||||
import com.nextcloud.talk.newarch.utils.Images
|
||||
import com.nextcloud.talk.newarch.utils.getCredentials
|
||||
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ConductorRemapping
|
||||
@ -94,7 +95,6 @@ import com.nextcloud.talk.utils.MagicCharPolicy
|
||||
import com.nextcloud.talk.utils.NotificationUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
||||
import com.nextcloud.talk.utils.text.Spans
|
||||
import com.nextcloud.talk.webrtc.MagicWebSocketInstance
|
||||
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper
|
||||
@ -161,7 +161,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
@JvmField
|
||||
var conversationLobbyText: TextView? = null
|
||||
var roomToken: String? = null
|
||||
val conversationUser: UserEntity?
|
||||
val conversationUser: UserNgEntity?
|
||||
val roomPassword: String
|
||||
var credentials: String? = null
|
||||
var currentConversation: Conversation? = null
|
||||
@ -459,7 +459,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
})
|
||||
|
||||
val filters = arrayOfNulls<InputFilter>(1)
|
||||
val lengthFilter = conversationUser?.messageMaxLength ?: 1000
|
||||
val lengthFilter = conversationUser?.maxMessageLength() ?: 1000
|
||||
|
||||
|
||||
filters[0] = InputFilter.LengthFilter(lengthFilter)
|
||||
@ -627,7 +627,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)
|
||||
)
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser))
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserNgEntity>(conversationUser))
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
router.pushController(
|
||||
RouterTransaction.with(BrowserController(bundle))
|
||||
@ -682,14 +682,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
}
|
||||
|
||||
isLeavingForConversation = false
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.currentRoomId = roomId
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.currentRoomToken = roomId
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.isInCall = false
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.userInRoom = conversationUser
|
||||
|
||||
isLinkPreviewAllowed = appPreferences.areLinkPreviewsAllowed
|
||||
|
||||
@ -745,8 +737,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
|
||||
override fun onDetach(view: View) {
|
||||
eventBus.unregister(this)
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.clear()
|
||||
|
||||
if (activity != null) {
|
||||
activity?.findViewById<View>(R.id.toolbar)
|
||||
@ -850,10 +840,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
inConversation = true
|
||||
currentConversation?.sessionId = roomOverall.ocs.data.sessionId
|
||||
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.session =
|
||||
currentConversation?.sessionId
|
||||
startPing()
|
||||
|
||||
setupWebsocket()
|
||||
@ -886,8 +872,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
})
|
||||
} else {
|
||||
inConversation = true
|
||||
ApplicationWideCurrentRoomHolder.getInstance()
|
||||
.session = currentConversation?.sessionId
|
||||
if (magicWebSocketInstance != null) {
|
||||
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
|
||||
roomToken,
|
||||
@ -1198,7 +1182,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
chatMessageList[i + 1].createdAt
|
||||
)
|
||||
) {
|
||||
chatMessageList[i].isGrouped = true
|
||||
chatMessageList[i].grouped = true
|
||||
countGroupedMessages++
|
||||
} else {
|
||||
countGroupedMessages = 0
|
||||
@ -1206,7 +1190,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
}
|
||||
|
||||
val chatMessage = chatMessageList[i]
|
||||
chatMessage.isOneToOneConversation =
|
||||
chatMessage.oneToOneConversation =
|
||||
currentConversation?.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION
|
||||
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
|
||||
chatMessage.activeUser = conversationUser
|
||||
@ -1272,11 +1256,11 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
||||
}
|
||||
|
||||
if (adapter != null) {
|
||||
chatMessage.isGrouped = (adapter!!.isPreviousSameAuthor(
|
||||
chatMessage.grouped = (adapter!!.isPreviousSameAuthor(
|
||||
chatMessage
|
||||
.actorId, -1
|
||||
) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0)
|
||||
chatMessage.isOneToOneConversation =
|
||||
chatMessage.oneToOneConversation =
|
||||
(currentConversation?.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION)
|
||||
adapter?.addToStart(chatMessage, shouldScroll)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -73,6 +73,9 @@ import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
import com.nextcloud.talk.newarch.local.models.hasSpreedFeatureCapability
|
||||
import com.nextcloud.talk.newarch.utils.getCredentials
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
@ -174,7 +177,7 @@ class ConversationInfoController(args: Bundle) : BaseController(),
|
||||
lateinit var userUtils: UserUtils
|
||||
|
||||
private val conversationToken: String?
|
||||
private val conversationUser: UserEntity?
|
||||
private val conversationUser: UserNgEntity?
|
||||
private val credentials: String?
|
||||
private var roomDisposable: Disposable? = null
|
||||
private var participantsDisposable: Disposable? = null
|
||||
@ -245,7 +248,7 @@ class ConversationInfoController(args: Bundle) : BaseController(),
|
||||
|
||||
if (databaseStorageModule == null) {
|
||||
databaseStorageModule = DatabaseStorageModule(
|
||||
conversationUser, conversationToken, this)
|
||||
conversationUser!!, conversationToken!!, this)
|
||||
}
|
||||
|
||||
notificationsPreferenceScreen.setStorageModule(databaseStorageModule)
|
||||
|
@ -24,11 +24,11 @@ import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BottomSheetLockEvent {
|
||||
private final boolean cancelable;
|
||||
private final int delay;
|
||||
private final boolean shouldRefreshData;
|
||||
private final boolean cancel;
|
||||
private boolean dismissView;
|
||||
public final boolean cancelable;
|
||||
public final int delay;
|
||||
public final boolean shouldRefreshData;
|
||||
public final boolean cancel;
|
||||
public boolean dismissView;
|
||||
|
||||
public BottomSheetLockEvent(boolean cancelable, int delay, boolean shouldRefreshData,
|
||||
boolean cancel) {
|
||||
|
@ -26,9 +26,9 @@ import org.webrtc.MediaStream;
|
||||
|
||||
@Data
|
||||
public class MediaStreamEvent {
|
||||
private final MediaStream mediaStream;
|
||||
private final String session;
|
||||
private final String videoStreamType;
|
||||
public final MediaStream mediaStream;
|
||||
public final String session;
|
||||
public final String videoStreamType;
|
||||
|
||||
public MediaStreamEvent(@Nullable MediaStream mediaStream, String session,
|
||||
String videoStreamType) {
|
||||
|
@ -24,7 +24,7 @@ import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class NetworkEvent {
|
||||
private final NetworkConnectionEvent networkConnectionEvent;
|
||||
public final NetworkConnectionEvent networkConnectionEvent;
|
||||
|
||||
public NetworkEvent(NetworkConnectionEvent networkConnectionEvent) {
|
||||
this.networkConnectionEvent = networkConnectionEvent;
|
||||
|
@ -25,11 +25,11 @@ import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PeerConnectionEvent {
|
||||
private final PeerConnectionEventType peerConnectionEventType;
|
||||
private final String sessionId;
|
||||
private final String nick;
|
||||
private final Boolean changeValue;
|
||||
private final String videoStreamType;
|
||||
public final PeerConnectionEventType peerConnectionEventType;
|
||||
public final String sessionId;
|
||||
public final String nick;
|
||||
public final Boolean changeValue;
|
||||
public final String videoStreamType;
|
||||
|
||||
public PeerConnectionEvent(PeerConnectionEventType peerConnectionEventType,
|
||||
@Nullable String sessionId,
|
||||
|
@ -28,12 +28,12 @@ import org.webrtc.SessionDescription;
|
||||
@Data
|
||||
public class SessionDescriptionSendEvent {
|
||||
@Nullable
|
||||
private final SessionDescription sessionDescription;
|
||||
private final String peerId;
|
||||
private final String type;
|
||||
public final SessionDescription sessionDescription;
|
||||
public final String peerId;
|
||||
public final String type;
|
||||
@Nullable
|
||||
private final NCIceCandidate ncIceCandidate;
|
||||
private final String videoStreamType;
|
||||
public final NCIceCandidate ncIceCandidate;
|
||||
public final String videoStreamType;
|
||||
|
||||
public SessionDescriptionSendEvent(@Nullable SessionDescription sessionDescription, String peerId,
|
||||
String type,
|
||||
|
@ -76,6 +76,10 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.notifications.NotificationOverall
|
||||
import com.nextcloud.talk.models.json.push.DecryptedPushMessage
|
||||
import com.nextcloud.talk.models.json.push.NotificationUser
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
import com.nextcloud.talk.newarch.local.models.hasSpreedFeatureCapability
|
||||
import com.nextcloud.talk.newarch.utils.Images
|
||||
import com.nextcloud.talk.newarch.utils.getCredentials
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
@ -101,11 +105,12 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.disposables.Disposable
|
||||
import okhttp3.JavaNetCookieJar
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import org.parceler.Parcels
|
||||
import retrofit2.Retrofit
|
||||
import java.io.IOException
|
||||
@ -124,19 +129,16 @@ import javax.inject.Inject
|
||||
class NotificationWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(context, workerParams) {
|
||||
@JvmField
|
||||
@Inject
|
||||
var appPreferences: AppPreferences? = null
|
||||
) : Worker(context, workerParams), KoinComponent {
|
||||
|
||||
val appPreferences: AppPreferences by inject()
|
||||
val retrofit: Retrofit by inject()
|
||||
val okHttpClient: OkHttpClient by inject()
|
||||
val usersRepository: UsersRepository by inject()
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var arbitraryStorageUtils: ArbitraryStorageUtils? = null
|
||||
@JvmField
|
||||
@Inject
|
||||
var retrofit: Retrofit? = null
|
||||
@JvmField
|
||||
@Inject
|
||||
var okHttpClient: OkHttpClient? = null
|
||||
private var ncApi: NcApi? = null
|
||||
private var decryptedPushMessage: DecryptedPushMessage? = null
|
||||
private var context: Context? = null
|
||||
@ -148,7 +150,7 @@ class NotificationWorker(
|
||||
|
||||
|
||||
private fun showNotificationForCallWithNoPing(intent: Intent) {
|
||||
val userEntity: UserEntity =
|
||||
val userEntity: UserNgEntity =
|
||||
signatureVerification!!.userEntity
|
||||
var arbitraryStorageEntity: ArbitraryStorageEntity?
|
||||
|
||||
@ -224,7 +226,7 @@ class NotificationWorker(
|
||||
}
|
||||
|
||||
private fun showNotificationWithObjectData(intent: Intent) {
|
||||
val userEntity: UserEntity =
|
||||
val userEntity: UserNgEntity =
|
||||
signatureVerification!!.userEntity
|
||||
ncApi!!.getNotification(
|
||||
credentials, ApiUtils.getUrlForNotificationWithId(
|
||||
@ -575,8 +577,7 @@ class NotificationWorker(
|
||||
}
|
||||
}
|
||||
|
||||
if (soundUri != null && !ApplicationWideCurrentRoomHolder.getInstance().isInCall &&
|
||||
(shouldPlaySound(importantConversation))
|
||||
if (soundUri != null && (shouldPlaySound(importantConversation))
|
||||
) {
|
||||
val audioAttributesBuilder: AudioAttributes.Builder =
|
||||
AudioAttributes.Builder()
|
||||
@ -630,7 +631,7 @@ class NotificationWorker(
|
||||
data.getString(KEY_NOTIFICATION_SIGNATURE)
|
||||
val base64DecodedSubject: ByteArray = Base64.decode(subject, Base64.DEFAULT)
|
||||
val base64DecodedSignature: ByteArray = Base64.decode(signature, Base64.DEFAULT)
|
||||
val pushUtils = PushUtils()
|
||||
val pushUtils = PushUtils(usersRepository)
|
||||
val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey
|
||||
try {
|
||||
signatureVerification = pushUtils.verifySignature(
|
||||
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import com.nextcloud.talk.utils.PushUtils;
|
||||
|
||||
public class PushRegistrationWorker extends Worker {
|
||||
public static final String TAG = "PushRegistrationWorker";
|
||||
|
||||
public PushRegistrationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
PushUtils pushUtils = new PushUtils();
|
||||
pushUtils.generateRsa2048KeyPair();
|
||||
pushUtils.pushRegistrationToServer();
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.jobs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.ListenableWorker.Result
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.utils.PushUtils
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
|
||||
class PushRegistrationWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(context, workerParams), KoinComponent {
|
||||
|
||||
val usersRepository: UsersRepository by inject()
|
||||
|
||||
override fun doWork(): Result {
|
||||
val pushUtils = PushUtils(usersRepository)
|
||||
pushUtils.generateRsa2048KeyPair()
|
||||
pushUtils.pushRegistrationToServer()
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "PushRegistrationWorker"
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.jobs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import autodagger.AutoInjector;
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.models.ExternalSignalingServer;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class WebsocketConnectionsWorker extends Worker {
|
||||
|
||||
private static final String TAG = "WebsocketConnectionsWorker";
|
||||
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
|
||||
public WebsocketConnectionsWorker(@NonNull Context context,
|
||||
@NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
}
|
||||
|
||||
@SuppressLint("LongLogTag")
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getComponentApplication()
|
||||
.inject(this);
|
||||
|
||||
List<UserEntity> userEntityList = userUtils.getUsers();
|
||||
UserEntity userEntity;
|
||||
ExternalSignalingServer externalSignalingServer;
|
||||
WebSocketConnectionHelper webSocketConnectionHelper = new WebSocketConnectionHelper();
|
||||
for (int i = 0; i < userEntityList.size(); i++) {
|
||||
userEntity = userEntityList.get(i);
|
||||
if (!TextUtils.isEmpty(userEntity.getExternalSignalingServer())) {
|
||||
try {
|
||||
externalSignalingServer = LoganSquare.parse(userEntity.getExternalSignalingServer(),
|
||||
ExternalSignalingServer.class);
|
||||
if (!TextUtils.isEmpty(externalSignalingServer.getExternalSignalingServer()) &&
|
||||
!TextUtils.isEmpty(externalSignalingServer.getExternalSignalingTicket())) {
|
||||
WebSocketConnectionHelper.getExternalSignalingInstanceForServer(
|
||||
externalSignalingServer.getExternalSignalingServer(),
|
||||
userEntity, externalSignalingServer.getExternalSignalingTicket(),
|
||||
false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to parse external signaling server");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.jobs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.work.ListenableWorker
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.models.ExternalSignalingServer
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class WebsocketConnectionsWorker(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(context, workerParams), KoinComponent {
|
||||
|
||||
val usersRepository: UsersRepository by inject()
|
||||
|
||||
override fun doWork(): Result {
|
||||
NextcloudTalkApplication.sharedApplication!!
|
||||
.componentApplication
|
||||
.inject(this)
|
||||
|
||||
val userEntityList = usersRepository.getUsers()
|
||||
var userEntity: UserNgEntity
|
||||
for (i in userEntityList.indices) {
|
||||
userEntity = userEntityList[i]
|
||||
if (userEntity.externalSignaling != null) {
|
||||
if (!userEntity.externalSignaling!!.externalSignalingServer.isNullOrEmpty() &&
|
||||
!userEntity.externalSignaling!!.externalSignalingTicket.isNullOrEmpty()) {
|
||||
WebSocketConnectionHelper.getExternalSignalingInstanceForServer(
|
||||
userEntity.externalSignaling!!.externalSignalingServer,
|
||||
userEntity, userEntity.externalSignaling!!.externalSignalingTicket,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WebsocketConnectionsWorker"
|
||||
}
|
||||
}
|
@ -18,19 +18,22 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.models;
|
||||
package com.nextcloud.talk.models
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import lombok.Data;
|
||||
import org.parceler.Parcel;
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import lombok.Data
|
||||
import org.parceler.Parcel
|
||||
|
||||
@Data
|
||||
@Parcel
|
||||
@JsonObject
|
||||
public class ExternalSignalingServer {
|
||||
@JsonField(name = "externalSignalingServer")
|
||||
public String externalSignalingServer;
|
||||
@JsonField(name = "externalSignalingTicket")
|
||||
public String externalSignalingTicket;
|
||||
}
|
||||
@Parcelize
|
||||
data class ExternalSignalingServer(
|
||||
@JsonField(name = ["externalSignalingServer"])
|
||||
var externalSignalingServer: String? = null,
|
||||
@JsonField(name = ["externalSignalingTicket"])
|
||||
var externalSignalingTicket: String? = null
|
||||
): Parcelable
|
@ -21,6 +21,7 @@
|
||||
package com.nextcloud.talk.models;
|
||||
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import lombok.Data;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@ -28,5 +29,5 @@ import org.parceler.Parcel;
|
||||
@Parcel
|
||||
public class SignatureVerification {
|
||||
public boolean signatureValid;
|
||||
public UserEntity userEntity;
|
||||
public UserNgEntity userEntity;
|
||||
}
|
||||
|
@ -32,5 +32,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class AutocompleteOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
List<AutocompleteUser> data;
|
||||
public List<AutocompleteUser> data;
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class AutocompleteOverall {
|
||||
@JsonField(name = "ocs")
|
||||
AutocompleteOCS ocs;
|
||||
public AutocompleteOCS ocs;
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class AutocompleteUser {
|
||||
@JsonField(name = "id")
|
||||
String id;
|
||||
public String id;
|
||||
|
||||
@JsonField(name = "label")
|
||||
String label;
|
||||
public String label;
|
||||
|
||||
@JsonField(name = "source")
|
||||
String source;
|
||||
public String source;
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class CapabilitiesList {
|
||||
@JsonField(name = "capabilities")
|
||||
Capabilities capabilities;
|
||||
public Capabilities capabilities;
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class CapabilitiesOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
CapabilitiesList data;
|
||||
public CapabilitiesList data;
|
||||
}
|
||||
|
@ -29,5 +29,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class CapabilitiesOverall {
|
||||
@JsonField(name = "ocs")
|
||||
CapabilitiesOCS ocs;
|
||||
public CapabilitiesOCS ocs;
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class SpreedCapability {
|
||||
@JsonField(name = "features")
|
||||
List<String> features;
|
||||
public List<String> features;
|
||||
|
||||
@JsonField(name = "config")
|
||||
HashMap<String, HashMap<String, String>> config;
|
||||
public HashMap<String, HashMap<String, String>> config;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.TextMatchers;
|
||||
import com.stfalcon.chatkit.commons.models.IMessage;
|
||||
@ -48,13 +49,13 @@ import org.parceler.Parcel;
|
||||
public class ChatMessage implements IMessage, MessageContentType, MessageContentType.Image {
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public boolean isGrouped;
|
||||
public boolean grouped;
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public boolean isOneToOneConversation;
|
||||
public boolean oneToOneConversation;
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public UserEntity activeUser;
|
||||
public UserNgEntity activeUser;
|
||||
@JsonIgnore
|
||||
@Ignore
|
||||
public Map<String, String> selectedIndividualHashMap;
|
||||
|
@ -34,6 +34,8 @@ import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter
|
||||
import com.nextcloud.talk.models.json.converters.EnumReadOnlyConversationConverter
|
||||
import com.nextcloud.talk.models.json.converters.EnumRoomTypeConverter
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.hasSpreedFeatureCapability
|
||||
import lombok.Data
|
||||
import org.parceler.Parcel
|
||||
import org.parceler.ParcelConstructor
|
||||
@ -120,36 +122,36 @@ class Conversation {
|
||||
return resources.getString(R.string.nc_delete_conversation_default)
|
||||
}
|
||||
|
||||
private fun isLockedOneToOne(conversationUser: UserEntity): Boolean {
|
||||
private fun isLockedOneToOne(conversationUser: UserNgEntity): Boolean {
|
||||
return type == ConversationType.ONE_TO_ONE_CONVERSATION && conversationUser
|
||||
.hasSpreedFeatureCapability(
|
||||
"locked-one-to-one-rooms"
|
||||
)
|
||||
}
|
||||
|
||||
fun canModerate(conversationUser: UserEntity): Boolean {
|
||||
fun canModerate(conversationUser: UserNgEntity): Boolean {
|
||||
return (Participant.ParticipantType.OWNER == participantType || Participant.ParticipantType.MODERATOR == participantType) && !isLockedOneToOne(
|
||||
conversationUser
|
||||
)
|
||||
}
|
||||
|
||||
fun shouldShowLobby(conversationUser: UserEntity): Boolean {
|
||||
fun shouldShowLobby(conversationUser: UserNgEntity): Boolean {
|
||||
return LobbyState.LOBBY_STATE_MODERATORS_ONLY == lobbyState && !canModerate(
|
||||
conversationUser
|
||||
)
|
||||
}
|
||||
|
||||
fun isLobbyViewApplicable(conversationUser: UserEntity): Boolean {
|
||||
fun isLobbyViewApplicable(conversationUser: UserNgEntity): Boolean {
|
||||
return !canModerate(
|
||||
conversationUser
|
||||
) && (type == ConversationType.GROUP_CONVERSATION || type == ConversationType.PUBLIC_CONVERSATION)
|
||||
}
|
||||
|
||||
fun isNameEditable(conversationUser: UserEntity): Boolean {
|
||||
fun isNameEditable(conversationUser: UserNgEntity): Boolean {
|
||||
return canModerate(conversationUser) && ConversationType.ONE_TO_ONE_CONVERSATION != type
|
||||
}
|
||||
|
||||
fun canLeave(conversationUser: UserEntity): Boolean {
|
||||
fun canLeave(conversationUser: UserNgEntity): Boolean {
|
||||
return !canModerate(
|
||||
conversationUser
|
||||
) || type != ConversationType.ONE_TO_ONE_CONVERSATION && participants!!.size > 1
|
||||
|
@ -18,29 +18,28 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.models.json.push;
|
||||
package com.nextcloud.talk.models.json.push
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import lombok.Data;
|
||||
import org.parceler.Parcel;
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import lombok.Data
|
||||
import org.parceler.Parcel
|
||||
|
||||
@Parcel
|
||||
@Data
|
||||
@JsonObject
|
||||
public class PushConfigurationState {
|
||||
@JsonField(name = "pushToken")
|
||||
public String pushToken;
|
||||
|
||||
@JsonField(name = "deviceIdentifier")
|
||||
public String deviceIdentifier;
|
||||
|
||||
@JsonField(name = "deviceIdentifierSignature")
|
||||
public String deviceIdentifierSignature;
|
||||
|
||||
@JsonField(name = "userPublicKey")
|
||||
public String userPublicKey;
|
||||
|
||||
@JsonField(name = "usesRegularPass")
|
||||
public boolean usesRegularPass;
|
||||
}
|
||||
@Parcelize
|
||||
class PushConfigurationState(
|
||||
@JsonField(name = ["pushToken"])
|
||||
var pushToken: String? = null,
|
||||
@JsonField(name = ["deviceIdentifier"])
|
||||
var deviceIdentifier: String? = null,
|
||||
@JsonField(name = ["deviceIdentifierSignature"])
|
||||
var deviceIdentifierSignature: String? = null,
|
||||
@JsonField(name = ["userPublicKey"])
|
||||
var userPublicKey: String? = null,
|
||||
@JsonField(name = ["usesRegularPass"])
|
||||
var usesRegularPass: Boolean = false
|
||||
): Parcelable
|
@ -30,12 +30,12 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class PushRegistration {
|
||||
@JsonField(name = "publicKey")
|
||||
String publicKey;
|
||||
public String publicKey;
|
||||
|
||||
@JsonField(name = "deviceIdentifier")
|
||||
String deviceIdentifier;
|
||||
public String deviceIdentifier;
|
||||
|
||||
@JsonField(name = "signature")
|
||||
String signature;
|
||||
public String signature;
|
||||
}
|
||||
|
||||
|
@ -31,5 +31,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class PushRegistrationOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
PushRegistration data;
|
||||
public PushRegistration data;
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ import org.parceler.Parcel;
|
||||
@JsonObject
|
||||
public class PushRegistrationOverall {
|
||||
@JsonField(name = "ocs")
|
||||
PushRegistrationOCS ocs;
|
||||
public PushRegistrationOCS ocs;
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ import org.parceler.ParcelPropertyConverter;
|
||||
@JsonObject
|
||||
public class DataChannelMessageNick {
|
||||
@JsonField(name = "type")
|
||||
String type;
|
||||
public String type;
|
||||
|
||||
@ParcelPropertyConverter(ObjectParcelConverter.class)
|
||||
@JsonField(name = "payload")
|
||||
HashMap<String, String> payload;
|
||||
public HashMap<String, String> payload;
|
||||
|
||||
public DataChannelMessageNick(String type) {
|
||||
this.type = type;
|
||||
|
@ -30,11 +30,11 @@ import org.parceler.Parcel;
|
||||
@Parcel
|
||||
public class NCIceCandidate {
|
||||
@JsonField(name = "sdpMLineIndex")
|
||||
int sdpMLineIndex;
|
||||
public int sdpMLineIndex;
|
||||
|
||||
@JsonField(name = "sdpMid")
|
||||
String sdpMid;
|
||||
public String sdpMid;
|
||||
|
||||
@JsonField(name = "candidate")
|
||||
String candidate;
|
||||
public String candidate;
|
||||
}
|
||||
|
@ -30,17 +30,17 @@ import org.parceler.Parcel;
|
||||
@Parcel
|
||||
public class NCMessagePayload {
|
||||
@JsonField(name = "type")
|
||||
String type;
|
||||
public String type;
|
||||
|
||||
@JsonField(name = "sdp")
|
||||
String sdp;
|
||||
public String sdp;
|
||||
|
||||
@JsonField(name = "nick")
|
||||
String nick;
|
||||
public String nick;
|
||||
|
||||
@JsonField(name = "candidate")
|
||||
NCIceCandidate iceCandidate;
|
||||
public NCIceCandidate iceCandidate;
|
||||
|
||||
@JsonField(name = "name")
|
||||
String name;
|
||||
public String name;
|
||||
}
|
||||
|
@ -30,12 +30,12 @@ import org.parceler.Parcel;
|
||||
@Parcel
|
||||
public class NCMessageWrapper {
|
||||
@JsonField(name = "fn")
|
||||
NCSignalingMessage signalingMessage;
|
||||
public NCSignalingMessage signalingMessage;
|
||||
|
||||
// always a "message"
|
||||
@JsonField(name = "ev")
|
||||
String ev;
|
||||
public String ev;
|
||||
|
||||
@JsonField(name = "sessionId")
|
||||
String sessionId;
|
||||
public String sessionId;
|
||||
}
|
||||
|
@ -30,17 +30,17 @@ import org.parceler.Parcel;
|
||||
@Parcel
|
||||
public class NCSignalingMessage {
|
||||
@JsonField(name = "from")
|
||||
String from;
|
||||
public String from;
|
||||
@JsonField(name = "to")
|
||||
String to;
|
||||
public String to;
|
||||
@JsonField(name = "type")
|
||||
String type;
|
||||
public String type;
|
||||
@JsonField(name = "payload")
|
||||
NCMessagePayload payload;
|
||||
public NCMessagePayload payload;
|
||||
@JsonField(name = "roomType")
|
||||
String roomType;
|
||||
public String roomType;
|
||||
@JsonField(name = "sid")
|
||||
String sid;
|
||||
public String sid;
|
||||
@JsonField(name = "prefix")
|
||||
String prefix;
|
||||
public String prefix;
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class Signaling {
|
||||
@JsonField(name = "type")
|
||||
String type;
|
||||
public String type;
|
||||
//can be NCMessageWrapper or List<HashMap<String,String>>
|
||||
@JsonField(name = "data")
|
||||
Object messageWrapper;
|
||||
public Object messageWrapper;
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class SignalingOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
List<Signaling> signalings;
|
||||
public List<Signaling> signalings;
|
||||
}
|
||||
|
@ -28,5 +28,5 @@ import lombok.Data;
|
||||
@Data
|
||||
public class SignalingOverall {
|
||||
@JsonField(name = "ocs")
|
||||
SignalingOCS ocs;
|
||||
public SignalingOCS ocs;
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class IceServer {
|
||||
@JsonField(name = "url")
|
||||
String url;
|
||||
public String url;
|
||||
|
||||
@JsonField(name = "urls")
|
||||
List<String> urls;
|
||||
public List<String> urls;
|
||||
|
||||
@JsonField(name = "username")
|
||||
String username;
|
||||
public String username;
|
||||
|
||||
@JsonField(name = "credential")
|
||||
String credential;
|
||||
public String credential;
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class Settings {
|
||||
@JsonField(name = "stunservers")
|
||||
List<IceServer> stunServers;
|
||||
public List<IceServer> stunServers;
|
||||
|
||||
@JsonField(name = "turnservers")
|
||||
List<IceServer> turnServers;
|
||||
public List<IceServer> turnServers;
|
||||
|
||||
@JsonField(name = "server")
|
||||
String externalSignalingServer;
|
||||
public String externalSignalingServer;
|
||||
|
||||
@JsonField(name = "ticket")
|
||||
String externalSignalingTicket;
|
||||
public String externalSignalingTicket;
|
||||
}
|
||||
|
@ -29,5 +29,5 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class SignalingSettingsOcs extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
Settings settings;
|
||||
public Settings settings;
|
||||
}
|
||||
|
@ -28,5 +28,5 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class SignalingSettingsOverall {
|
||||
@JsonField(name = "ocs")
|
||||
SignalingSettingsOcs ocs;
|
||||
public SignalingSettingsOcs ocs;
|
||||
}
|
||||
|
@ -20,9 +20,21 @@
|
||||
|
||||
package com.nextcloud.talk.newarch.data.repository.offline
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.local.dao.UsersDao
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
|
||||
class UsersRepositoryImpl(val usersDao: UsersDao): UsersRepository {
|
||||
override fun getActiveUserLiveData(): LiveData<UserNgEntity> {
|
||||
return usersDao.getActiveUserLiveData()
|
||||
}
|
||||
|
||||
override fun getActiveUser(): UserNgEntity {
|
||||
return usersDao.getActiveUser()
|
||||
}
|
||||
|
||||
override fun getUsers(): List<UserNgEntity> {
|
||||
return usersDao.getUsers()
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,14 @@ import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.newarch.data.source.remote.ApiService
|
||||
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
import com.nextcloud.talk.newarch.utils.getCredentials
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
|
||||
class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : NextcloudTalkRepository {
|
||||
override suspend fun deleteConversationForUser(
|
||||
user: UserEntity,
|
||||
user: UserNgEntity,
|
||||
conversation: Conversation
|
||||
): GenericOverall {
|
||||
return apiService.deleteConversation(
|
||||
@ -39,7 +41,7 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
|
||||
}
|
||||
|
||||
override suspend fun leaveConversationForUser(
|
||||
user: UserEntity,
|
||||
user: UserNgEntity,
|
||||
conversation: Conversation
|
||||
): GenericOverall {
|
||||
return apiService.leaveConversation(
|
||||
@ -51,7 +53,7 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
|
||||
}
|
||||
|
||||
override suspend fun setFavoriteValueForConversation(
|
||||
user: UserEntity,
|
||||
user: UserNgEntity,
|
||||
conversation: Conversation,
|
||||
favorite: Boolean
|
||||
): GenericOverall {
|
||||
@ -68,7 +70,7 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getConversationsForUser(user: UserEntity): List<Conversation> {
|
||||
override suspend fun getConversationsForUser(user: UserNgEntity): List<Conversation> {
|
||||
return apiService.getConversations(
|
||||
user.getCredentials(),
|
||||
ApiUtils.getUrlForGetRooms(user.baseUrl)
|
||||
|
@ -20,6 +20,11 @@
|
||||
|
||||
package com.nextcloud.talk.newarch.domain.repository.offline
|
||||
|
||||
interface UsersRepository {
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
|
||||
interface UsersRepository {
|
||||
fun getActiveUserLiveData(): LiveData<UserNgEntity>
|
||||
fun getActiveUser(): UserNgEntity
|
||||
fun getUsers(): List<UserNgEntity>
|
||||
}
|
@ -23,22 +23,23 @@ package com.nextcloud.talk.newarch.domain.repository.online
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
|
||||
interface NextcloudTalkRepository {
|
||||
suspend fun getConversationsForUser(user: UserEntity): List<Conversation>
|
||||
suspend fun getConversationsForUser(user: UserNgEntity): List<Conversation>
|
||||
suspend fun setFavoriteValueForConversation(
|
||||
user: UserEntity,
|
||||
user: UserNgEntity,
|
||||
conversation: Conversation,
|
||||
favorite: Boolean
|
||||
): GenericOverall
|
||||
|
||||
suspend fun deleteConversationForUser(
|
||||
user: UserEntity,
|
||||
user: UserNgEntity,
|
||||
conversation: Conversation
|
||||
): GenericOverall
|
||||
|
||||
suspend fun leaveConversationForUser(
|
||||
userEntity: UserEntity,
|
||||
userEntity: UserNgEntity,
|
||||
conversation: Conversation
|
||||
): GenericOverall
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import android.app.Application
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase
|
||||
@ -37,14 +38,15 @@ class ConversationListViewModelFactory constructor(
|
||||
private val leaveConversationUseCase: LeaveConversationUseCase,
|
||||
private val deleteConversationUseCase: DeleteConversationUseCase,
|
||||
private val userUtils: UserUtils,
|
||||
private val offlineRepository: ConversationsRepository
|
||||
private val conversationsRepository: ConversationsRepository,
|
||||
private val usersRepository: UsersRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return ConversationsListViewModel(
|
||||
application, conversationsUseCase,
|
||||
setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase,
|
||||
userUtils, offlineRepository
|
||||
userUtils, conversationsRepository, usersRepository
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
@ -310,11 +310,6 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
||||
recyclerViewAdapter.setFilter(it)
|
||||
recyclerViewAdapter.filterItems(500)
|
||||
})
|
||||
|
||||
|
||||
currentUserAvatar.observe(this@ConversationsListView, Observer {
|
||||
settingsItem?.icon = it
|
||||
})
|
||||
}
|
||||
|
||||
return super.onCreateView(inflater, container)
|
||||
@ -452,12 +447,12 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
||||
val conversation = (clickedItem as ConversationItem).model
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, viewModel.currentUserLiveData.value)
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(viewModel.currentUserLiveData.value))
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.token)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId)
|
||||
bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation))
|
||||
ConductorRemapping.remapChatController(
|
||||
router, viewModel.currentUserLiveData.value!!.id, conversation!!.token!!,
|
||||
router, viewModel.currentUserLiveData.value!!.id!!, conversation.token!!,
|
||||
bundle, false
|
||||
)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ package com.nextcloud.talk.newarch.features.conversationsList
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@ -30,17 +30,18 @@ import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.R.drawable
|
||||
import com.nextcloud.talk.R.string
|
||||
import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseViewModel
|
||||
import com.nextcloud.talk.newarch.data.model.ErrorModel
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.utils.ViewState.LOADING
|
||||
import com.nextcloud.talk.utils.ShareUtils
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||
@ -54,26 +55,18 @@ class ConversationsListViewModel constructor(
|
||||
private val leaveConversationUseCase: LeaveConversationUseCase,
|
||||
private val deleteConversationUseCase: DeleteConversationUseCase,
|
||||
private val userUtils: UserUtils,
|
||||
private val offlineRepository: ConversationsRepository
|
||||
private val conversationsRepository: ConversationsRepository,
|
||||
usersRepository: UsersRepository
|
||||
) : BaseViewModel<ConversationsListView>(application) {
|
||||
|
||||
val viewState = MutableLiveData(LOADING)
|
||||
var messageData: String? = null
|
||||
val searchQuery = MutableLiveData<String>()
|
||||
val currentUserLiveData: MutableLiveData<UserEntity> = MutableLiveData()
|
||||
val currentUserLiveData = usersRepository.getActiveUserLiveData()
|
||||
val conversationsLiveData = Transformations.switchMap(currentUserLiveData) {
|
||||
offlineRepository.getConversationsForUser(it.id)
|
||||
conversationsRepository.getConversationsForUser(it.id)
|
||||
}
|
||||
|
||||
var currentUserAvatar: MutableLiveData<Drawable> = MutableLiveData()
|
||||
get() {
|
||||
if (field.value == null) {
|
||||
field.value = context.resources.getDrawable(drawable.ic_settings_white_24dp)
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
fun leaveConversation(conversation: Conversation) {
|
||||
viewModelScope.launch {
|
||||
setConversationUpdateStatus(conversation, true)
|
||||
@ -85,7 +78,7 @@ class ConversationsListViewModel constructor(
|
||||
),
|
||||
object : UseCaseResponse<GenericOverall> {
|
||||
override suspend fun onSuccess(result: GenericOverall) {
|
||||
offlineRepository.deleteConversation(
|
||||
conversationsRepository.deleteConversation(
|
||||
currentUserLiveData.value!!.id, conversation
|
||||
.conversationId!!
|
||||
)
|
||||
@ -114,7 +107,7 @@ class ConversationsListViewModel constructor(
|
||||
),
|
||||
object : UseCaseResponse<GenericOverall> {
|
||||
override suspend fun onSuccess(result: GenericOverall) {
|
||||
offlineRepository.deleteConversation(
|
||||
conversationsRepository.deleteConversation(
|
||||
currentUserLiveData.value!!.id, conversation
|
||||
.conversationId!!
|
||||
)
|
||||
@ -145,7 +138,7 @@ class ConversationsListViewModel constructor(
|
||||
),
|
||||
object : UseCaseResponse<GenericOverall> {
|
||||
override suspend fun onSuccess(result: GenericOverall) {
|
||||
offlineRepository.setFavoriteValueForConversation(
|
||||
conversationsRepository.setFavoriteValueForConversation(
|
||||
currentUserLiveData.value!!.id,
|
||||
conversation.conversationId!!, favorite
|
||||
)
|
||||
@ -161,22 +154,18 @@ class ConversationsListViewModel constructor(
|
||||
}
|
||||
|
||||
fun loadConversations() {
|
||||
val userChanged = !(currentUserLiveData.value?.equals(userUtils.currentUser) ?: false)
|
||||
|
||||
if (userChanged) {
|
||||
currentUserLiveData.value = userUtils.currentUser
|
||||
viewState.value = LOADING
|
||||
}
|
||||
|
||||
getConversationsUseCase.invoke(viewModelScope, parametersOf(currentUserLiveData.value), object :
|
||||
UseCaseResponse<List<Conversation>> {
|
||||
override suspend fun onSuccess(result: List<Conversation>) {
|
||||
val mutableList = result.toMutableList()
|
||||
val internalUserId = currentUserLiveData.value!!.id
|
||||
mutableList.forEach {
|
||||
it.internalUserId = currentUserLiveData.value!!.id
|
||||
it.internalUserId = internalUserId
|
||||
}
|
||||
|
||||
offlineRepository.saveConversationsForUser(currentUserLiveData.value!!.id, mutableList)
|
||||
conversationsRepository.saveConversationsForUser(
|
||||
internalUserId,
|
||||
mutableList)
|
||||
messageData = ""
|
||||
}
|
||||
|
||||
@ -266,7 +255,7 @@ class ConversationsListViewModel constructor(
|
||||
conversation: Conversation,
|
||||
value: Boolean
|
||||
) {
|
||||
offlineRepository.setChangingValueForConversation(
|
||||
conversationsRepository.setChangingValueForConversation(
|
||||
currentUserLiveData.value!!.id, conversation
|
||||
.conversationId!!, value
|
||||
)
|
||||
|
@ -23,6 +23,7 @@ package com.nextcloud.talk.newarch.features.conversationsList.di.module
|
||||
import android.app.Application
|
||||
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
|
||||
import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase
|
||||
import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase
|
||||
@ -42,7 +43,7 @@ val ConversationsListModule = module {
|
||||
factory {
|
||||
createConversationListViewModelFactory(
|
||||
androidApplication(), get(), get(), get(), get
|
||||
(), get(), get()
|
||||
(), get(), get(), get()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -83,11 +84,12 @@ fun createConversationListViewModelFactory(
|
||||
leaveConversationUseCase: LeaveConversationUseCase,
|
||||
deleteConversationUseCase: DeleteConversationUseCase,
|
||||
userUtils: UserUtils,
|
||||
offlineRepository: ConversationsRepository
|
||||
conversationsRepository: ConversationsRepository,
|
||||
usersRepository: UsersRepository
|
||||
): ConversationListViewModelFactory {
|
||||
return ConversationListViewModelFactory(
|
||||
application, getConversationsUseCase,
|
||||
setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase,
|
||||
userUtils, offlineRepository
|
||||
userUtils, conversationsRepository, usersRepository
|
||||
)
|
||||
}
|
@ -20,14 +20,24 @@
|
||||
|
||||
package com.nextcloud.talk.newarch.local.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.local.models.ConversationEntity
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
|
||||
@Dao
|
||||
abstract class UsersDao {
|
||||
// get active user
|
||||
@Query("SELECT * FROM users where status = 1")
|
||||
abstract fun getActiveUser(): UserNgEntity
|
||||
|
||||
@Query("SELECT * FROM users WHERE status = 1")
|
||||
abstract fun getActiveUserLiveData(): LiveData<UserNgEntity>
|
||||
|
||||
@Query("DELETE FROM users WHERE id = :userId")
|
||||
abstract fun deleteUserForId(userId: Long)
|
||||
|
||||
@ -41,6 +51,7 @@ abstract class UsersDao {
|
||||
@Query("SELECT * FROM users where status != 2")
|
||||
abstract fun getUsers(): List<UserNgEntity>
|
||||
|
||||
|
||||
@Query("SELECT * FROM users where status = 2")
|
||||
abstract fun getUsersScheduledForDeletion(): List<UserNgEntity>
|
||||
|
||||
|
@ -37,17 +37,18 @@ import java.util.HashMap
|
||||
|
||||
@Entity(
|
||||
tableName = "conversations",
|
||||
indices = [Index(value = ["user"])],
|
||||
indices = [Index(value = ["user", "conversation_id"], unique = true)],
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = UserNgEntity::class,
|
||||
parentColumns = arrayOf("id"),
|
||||
childColumns = arrayOf("user"),
|
||||
onDelete = CASCADE,
|
||||
onUpdate = CASCADE,
|
||||
deferred = true
|
||||
)]
|
||||
)
|
||||
data class ConversationEntity(
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long? = null,
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long? = 0,
|
||||
@ColumnInfo(name = "user") var user: Long?,
|
||||
@ColumnInfo(name = "conversation_id") var conversationId: String?,
|
||||
@ColumnInfo(name = "token") var token: String? = null,
|
||||
@ -77,7 +78,7 @@ data class ConversationEntity(
|
||||
) var conversationReadOnlyState: ConversationReadOnlyState? = null,
|
||||
@ColumnInfo(name = "lobby_state") var lobbyState: LobbyState? = null,
|
||||
@ColumnInfo(name = "lobby_timer") var lobbyTimer: Long? = null,
|
||||
@ColumnInfo(name = "last_read_message_id") var lastReadMessageId: Long = 0,
|
||||
@ColumnInfo(name = "last_read_message") var lastReadMessageId: Long = 0,
|
||||
@ColumnInfo(name = "modified_at") var modifiedAt: Long? = null,
|
||||
@ColumnInfo(name = "changing") var changing: Boolean = false
|
||||
)
|
||||
@ -116,8 +117,7 @@ fun ConversationEntity.toConversation(): Conversation {
|
||||
}
|
||||
|
||||
fun Conversation.toConversationEntity(): ConversationEntity {
|
||||
val conversationEntity =
|
||||
ConversationEntity(this.internalId, this.internalUserId, this.conversationId)
|
||||
val conversationEntity = ConversationEntity(null, this.internalUserId, this.conversationId)
|
||||
conversationEntity.token = this.token
|
||||
conversationEntity.name = this.name
|
||||
conversationEntity.displayName = this.displayName
|
||||
|
@ -20,9 +20,13 @@
|
||||
|
||||
package com.nextcloud.talk.newarch.local.models
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.Parcelable.Creator
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.ForeignKey.CASCADE
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.RoomWarnings
|
||||
@ -31,18 +35,18 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType
|
||||
|
||||
@Entity(
|
||||
tableName = "messages",
|
||||
indices = [Index(value = ["conversation"]), Index(value = ["user", "conversation"])],
|
||||
indices = [Index(value = ["conversation"])],
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = ConversationEntity::class,
|
||||
parentColumns = arrayOf("id"),
|
||||
childColumns = arrayOf("conversation"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
onDelete = CASCADE,
|
||||
onUpdate = CASCADE,
|
||||
deferred = true
|
||||
)]
|
||||
)
|
||||
data class MessageEntity(
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long? = null,
|
||||
@ColumnInfo(name = "user") var user: Long? = 0,
|
||||
@ColumnInfo(name = "conversation") var conversation: Long? = null,
|
||||
@ColumnInfo(name = "message_id") var messageId: Long = 0,
|
||||
@ColumnInfo(name = "actor_id") var actorId: String? = null,
|
||||
@ -59,7 +63,6 @@ data class MessageEntity(
|
||||
fun MessageEntity.toChatMessage(): ChatMessage {
|
||||
val chatMessage = ChatMessage()
|
||||
chatMessage.internalMessageId = this.id
|
||||
chatMessage.internalUserId = this.user
|
||||
chatMessage.internalConversationId = this.conversation
|
||||
chatMessage.jsonMessageId = this.messageId
|
||||
chatMessage.actorType = this.actorType
|
||||
@ -74,9 +77,7 @@ fun MessageEntity.toChatMessage(): ChatMessage {
|
||||
|
||||
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||
fun ChatMessage.toMessageEntity(): MessageEntity {
|
||||
val messageEntity = MessageEntity()
|
||||
messageEntity.id = this.internalMessageId
|
||||
messageEntity.user = this.internalUserId
|
||||
val messageEntity = MessageEntity(this.internalMessageId)
|
||||
messageEntity.conversation = this.internalConversationId
|
||||
messageEntity.messageId = this.jsonMessageId
|
||||
messageEntity.actorType = this.actorType
|
||||
@ -88,4 +89,4 @@ fun ChatMessage.toMessageEntity(): MessageEntity {
|
||||
//messageEntity.messageParameters = this.messageParameters
|
||||
|
||||
return messageEntity
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
package com.nextcloud.talk.newarch.local.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
@ -27,17 +28,68 @@ import com.nextcloud.talk.models.ExternalSignalingServer
|
||||
import com.nextcloud.talk.models.json.capabilities.Capabilities
|
||||
import com.nextcloud.talk.models.json.push.PushConfigurationState
|
||||
import com.nextcloud.talk.newarch.local.models.other.UserStatus
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.parcel.RawValue
|
||||
import kotlinx.android.parcel.WriteWith
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "users")
|
||||
data class UserNgEntity(
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long? = null,
|
||||
@ColumnInfo(name = "user_id") var userId: String? = null,
|
||||
@ColumnInfo(name = "username") var username: String? = null,
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long,
|
||||
@ColumnInfo(name = "user_id") var userId: String,
|
||||
@ColumnInfo(name = "username") var username: String,
|
||||
@ColumnInfo(name = "base_url") var baseUrl: String,
|
||||
@ColumnInfo(name = "token") var token: String? = null,
|
||||
@ColumnInfo(name = "display_name") var displayName: String? = null,
|
||||
@ColumnInfo(name = "push_configuration") var pushConfiguration: PushConfigurationState? = null,
|
||||
@ColumnInfo(name = "capabilities") var capabilities: Capabilities? = null,
|
||||
@ColumnInfo(
|
||||
name = "push_configuration"
|
||||
) var pushConfiguration: PushConfigurationState? = null,
|
||||
@ColumnInfo(name = "capabilities") var capabilities: @RawValue Capabilities? = null,
|
||||
@ColumnInfo(name = "client_auth_cert") var clientCertificate: String? = null,
|
||||
@ColumnInfo(name = "external_signaling") var externalSignaling: ExternalSignalingServer? = null,
|
||||
@ColumnInfo(
|
||||
name = "external_signaling"
|
||||
) var externalSignaling: ExternalSignalingServer? = null,
|
||||
@ColumnInfo(name = "status") var status: UserStatus? = null
|
||||
)
|
||||
) : Parcelable {
|
||||
fun hasSpreedFeatureCapability(capabilityName: String): Boolean {
|
||||
val capabilityExists = capabilities?.spreedCapability?.features?.contains(capabilityName)
|
||||
if (capabilityExists != null) {
|
||||
return capabilityExists
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun UserNgEntity.getCredentials() = ApiUtils.getCredentials(username, token)
|
||||
|
||||
fun UserNgEntity.hasExternalCapability(capabilityName: String): Boolean {
|
||||
val capabilityExists = capabilities?.externalCapability?.get("v1")
|
||||
?.contains(capabilityName)
|
||||
if (capabilityExists != null) {
|
||||
return capabilityExists
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun UserNgEntity.hasSpreedFeatureCapability(capabilityName: String): Boolean {
|
||||
val capabilityExists = capabilities?.spreedCapability?.features?.contains(capabilityName)
|
||||
if (capabilityExists != null) {
|
||||
return capabilityExists
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun UserNgEntity.maxMessageLength(): Int {
|
||||
val maxLength = capabilities?.spreedCapability?.config?.get("chat")
|
||||
?.get("max-length")
|
||||
if (maxLength != null) {
|
||||
return maxLength.toInt()
|
||||
} else {
|
||||
return 1000
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
package com.nextcloud.talk.newarch.utils
|
||||
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
|
||||
fun UserEntity.getCredentials() = ApiUtils.getCredentials(username, token)
|
||||
|
@ -27,6 +27,8 @@ import coil.request.LoadRequest
|
||||
import coil.target.Target
|
||||
import coil.transform.Transformation
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||
|
||||
class Images {
|
||||
fun getRequestForUrl(
|
||||
@ -34,7 +36,7 @@ class Images {
|
||||
context: Context,
|
||||
url: String,
|
||||
userEntity:
|
||||
UserEntity?,
|
||||
UserNgEntity?,
|
||||
target: Target?,
|
||||
lifecycleOwner: LifecycleOwner?,
|
||||
vararg transformations: Transformation
|
||||
|
@ -72,6 +72,7 @@ import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.events.UserMentionClickEvent
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.utils.Images
|
||||
import com.nextcloud.talk.utils.text.Spans
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
@ -200,7 +201,7 @@ object DisplayUtils {
|
||||
context: Context,
|
||||
id: String,
|
||||
label: CharSequence,
|
||||
conversationUser: UserEntity,
|
||||
conversationUser: UserNgEntity,
|
||||
type: String,
|
||||
@XmlRes chipResource: Int,
|
||||
emojiEditText: EditText?
|
||||
@ -278,7 +279,7 @@ object DisplayUtils {
|
||||
id: String,
|
||||
label: String,
|
||||
type: String,
|
||||
conversationUser: UserEntity,
|
||||
conversationUser: UserNgEntity,
|
||||
@XmlRes chipXmlRes: Int
|
||||
): Spannable {
|
||||
|
||||
|
@ -30,6 +30,7 @@ import android.os.Build
|
||||
import android.service.notification.StatusBarNotification
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
|
||||
object NotificationUtils {
|
||||
@ -91,7 +92,7 @@ object NotificationUtils {
|
||||
|
||||
fun cancelAllNotificationsForAccount(
|
||||
context: Context?,
|
||||
conversationUser: UserEntity
|
||||
conversationUser: UserNgEntity
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && context != null) {
|
||||
|
||||
@ -115,7 +116,7 @@ object NotificationUtils {
|
||||
|
||||
fun cancelExistingNotificationWithId(
|
||||
context: Context?,
|
||||
conversationUser: UserEntity,
|
||||
conversationUser: UserNgEntity,
|
||||
notificationId: Long
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
|
||||
@ -144,7 +145,7 @@ object NotificationUtils {
|
||||
|
||||
fun findNotificationForRoom(
|
||||
context: Context?,
|
||||
conversationUser: UserEntity,
|
||||
conversationUser: UserNgEntity,
|
||||
roomTokenOrId: String
|
||||
): StatusBarNotification? {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
|
||||
@ -177,7 +178,7 @@ object NotificationUtils {
|
||||
|
||||
fun cancelExistingNotificationsForRoom(
|
||||
context: Context?,
|
||||
conversationUser: UserEntity,
|
||||
conversationUser: UserNgEntity,
|
||||
roomTokenOrId: String
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
|
||||
|
@ -1,445 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import autodagger.AutoInjector;
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.events.EventStatus;
|
||||
import com.nextcloud.talk.models.SignatureVerification;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.push.PushConfigurationState;
|
||||
import com.nextcloud.talk.models.json.push.PushRegistrationOverall;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class PushUtils {
|
||||
private static final String TAG = "PushUtils";
|
||||
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
|
||||
@Inject
|
||||
AppPreferences appPreferences;
|
||||
|
||||
@Inject
|
||||
EventBus eventBus;
|
||||
|
||||
@Inject
|
||||
NcApi ncApi;
|
||||
|
||||
private File keysFile;
|
||||
private File publicKeyFile;
|
||||
private File privateKeyFile;
|
||||
|
||||
private String proxyServer;
|
||||
|
||||
public PushUtils() {
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getComponentApplication()
|
||||
.inject(this);
|
||||
|
||||
keysFile = NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getDir("PushKeyStore", Context.MODE_PRIVATE);
|
||||
|
||||
publicKeyFile =
|
||||
new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeystore",
|
||||
Context.MODE_PRIVATE), "push_key.pub");
|
||||
privateKeyFile =
|
||||
new File(NextcloudTalkApplication.Companion.getSharedApplication().getDir("PushKeystore",
|
||||
Context.MODE_PRIVATE), "push_key.priv");
|
||||
proxyServer = NextcloudTalkApplication.Companion.getSharedApplication().getResources().
|
||||
getString(R.string.nc_push_server_url);
|
||||
}
|
||||
|
||||
public SignatureVerification verifySignature(byte[] signatureBytes, byte[] subjectBytes) {
|
||||
Signature signature = null;
|
||||
PushConfigurationState pushConfigurationState;
|
||||
PublicKey publicKey;
|
||||
SignatureVerification signatureVerification = new SignatureVerification();
|
||||
signatureVerification.setSignatureValid(false);
|
||||
|
||||
List<UserEntity> userEntities = userUtils.getUsers();
|
||||
try {
|
||||
signature = Signature.getInstance("SHA512withRSA");
|
||||
if (userEntities != null && userEntities.size() > 0) {
|
||||
for (UserEntity userEntity : userEntities) {
|
||||
if (!TextUtils.isEmpty(userEntity.getPushConfigurationState())) {
|
||||
pushConfigurationState = LoganSquare.parse(userEntity.getPushConfigurationState(),
|
||||
PushConfigurationState.class);
|
||||
publicKey = (PublicKey) readKeyFromString(true,
|
||||
pushConfigurationState.getUserPublicKey());
|
||||
signature.initVerify(publicKey);
|
||||
signature.update(subjectBytes);
|
||||
if (signature.verify(signatureBytes)) {
|
||||
signatureVerification.setSignatureValid(true);
|
||||
signatureVerification.setUserEntity(userEntity);
|
||||
return signatureVerification;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(TAG, "No such algorithm");
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Error while trying to parse push configuration viewState");
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.d(TAG, "Invalid key while trying to verify");
|
||||
} catch (SignatureException e) {
|
||||
Log.d(TAG, "Signature exception while trying to verify");
|
||||
}
|
||||
|
||||
return signatureVerification;
|
||||
}
|
||||
|
||||
private int saveKeyToFile(Key key, String path) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
|
||||
try {
|
||||
if (!new File(path).exists()) {
|
||||
if (!new File(path).createNewFile()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
try (FileOutputStream keyFileOutputStream = new FileOutputStream(path)) {
|
||||
keyFileOutputStream.write(encoded);
|
||||
return 0;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.d(TAG, "Failed to save key to file");
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Failed to save key to file via IOException");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String generateSHA512Hash(String pushToken) {
|
||||
MessageDigest messageDigest = null;
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("SHA-512");
|
||||
messageDigest.update(pushToken.getBytes());
|
||||
return bytesToHex(messageDigest.digest());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(TAG, "SHA-512 algorithm not supported");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String bytesToHex(byte[] bytes) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte individualByte : bytes) {
|
||||
result.append(Integer.toString((individualByte & 0xff) + 0x100, 16)
|
||||
.substring(1));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public int generateRsa2048KeyPair() {
|
||||
if (!publicKeyFile.exists() && !privateKeyFile.exists()) {
|
||||
if (!keysFile.exists()) {
|
||||
keysFile.mkdirs();
|
||||
}
|
||||
|
||||
KeyPairGenerator keyGen = null;
|
||||
try {
|
||||
keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(2048);
|
||||
|
||||
KeyPair pair = keyGen.generateKeyPair();
|
||||
int statusPrivate = saveKeyToFile(pair.getPrivate(), privateKeyFile.getAbsolutePath());
|
||||
int statusPublic = saveKeyToFile(pair.getPublic(), publicKeyFile.getAbsolutePath());
|
||||
|
||||
if (statusPrivate == 0 && statusPublic == 0) {
|
||||
// all went well
|
||||
return 0;
|
||||
} else {
|
||||
return -2;
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(TAG, "RSA algorithm not supported");
|
||||
}
|
||||
} else {
|
||||
// We already have the key
|
||||
return -1;
|
||||
}
|
||||
|
||||
// we failed to generate the key
|
||||
return -2;
|
||||
}
|
||||
|
||||
public void pushRegistrationToServer() {
|
||||
String token = appPreferences.getPushToken();
|
||||
|
||||
if (!TextUtils.isEmpty(token)) {
|
||||
String credentials;
|
||||
String pushTokenHash = generateSHA512Hash(token).toLowerCase();
|
||||
PublicKey devicePublicKey = (PublicKey) readKeyFromFile(true);
|
||||
if (devicePublicKey != null) {
|
||||
byte[] publicKeyBytes = Base64.encode(devicePublicKey.getEncoded(), Base64.NO_WRAP);
|
||||
String publicKey = new String(publicKeyBytes);
|
||||
publicKey = publicKey.replaceAll("(.{64})", "$1\n");
|
||||
|
||||
publicKey = "-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----\n";
|
||||
|
||||
if (userUtils.anyUserExists()) {
|
||||
String providerValue;
|
||||
PushConfigurationState accountPushData = null;
|
||||
for (Object userEntityObject : userUtils.getUsers()) {
|
||||
UserEntity userEntity = (UserEntity) userEntityObject;
|
||||
providerValue = userEntity.getPushConfigurationState();
|
||||
if (!TextUtils.isEmpty(providerValue)) {
|
||||
try {
|
||||
accountPushData = LoganSquare.parse(providerValue, PushConfigurationState.class);
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Failed to parse account push data");
|
||||
accountPushData = null;
|
||||
}
|
||||
} else {
|
||||
accountPushData = null;
|
||||
}
|
||||
|
||||
if (((TextUtils.isEmpty(providerValue) || accountPushData == null)
|
||||
&& !userEntity.getScheduledForDeletion()) ||
|
||||
(accountPushData != null
|
||||
&& !accountPushData.getPushToken().equals(token)
|
||||
&& !userEntity.getScheduledForDeletion())) {
|
||||
|
||||
Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("format", "json");
|
||||
queryMap.put("pushTokenHash", pushTokenHash);
|
||||
queryMap.put("devicePublicKey", publicKey);
|
||||
queryMap.put("proxyServer", proxyServer);
|
||||
|
||||
credentials =
|
||||
ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken());
|
||||
|
||||
String finalCredentials = credentials;
|
||||
ncApi.registerDeviceForNotificationsWithNextcloud(
|
||||
credentials,
|
||||
ApiUtils.getUrlNextcloudPush(userEntity.getBaseUrl()), queryMap)
|
||||
.subscribe(new Observer<PushRegistrationOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(PushRegistrationOverall pushRegistrationOverall) {
|
||||
Map<String, String> proxyMap = new HashMap<>();
|
||||
proxyMap.put("pushToken", token);
|
||||
proxyMap.put("deviceIdentifier", pushRegistrationOverall.getOcs().getData().
|
||||
getDeviceIdentifier());
|
||||
proxyMap.put("deviceIdentifierSignature", pushRegistrationOverall.getOcs()
|
||||
.getData().getSignature());
|
||||
proxyMap.put("userPublicKey", pushRegistrationOverall.getOcs()
|
||||
.getData().getPublicKey());
|
||||
|
||||
ncApi.registerDeviceForNotificationsWithProxy(
|
||||
ApiUtils.getUrlPushProxy(), proxyMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new Observer<Void>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Void aVoid) {
|
||||
PushConfigurationState pushConfigurationState =
|
||||
new PushConfigurationState();
|
||||
pushConfigurationState.setPushToken(token);
|
||||
pushConfigurationState.setDeviceIdentifier(
|
||||
pushRegistrationOverall.getOcs()
|
||||
.getData().getDeviceIdentifier());
|
||||
pushConfigurationState.setDeviceIdentifierSignature(
|
||||
pushRegistrationOverall
|
||||
.getOcs().getData().getSignature());
|
||||
pushConfigurationState.setUserPublicKey(
|
||||
pushRegistrationOverall.getOcs()
|
||||
.getData().getPublicKey());
|
||||
pushConfigurationState.setUsesRegularPass(false);
|
||||
|
||||
try {
|
||||
userUtils.createOrUpdateUser(null,
|
||||
null, null,
|
||||
userEntity.getDisplayName(),
|
||||
LoganSquare.serialize(pushConfigurationState), null,
|
||||
null, userEntity.getId(), null, null, null)
|
||||
.subscribe(new Observer<UserEntity>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(UserEntity userEntity) {
|
||||
eventBus.post(new EventStatus(userEntity.getId(),
|
||||
EventStatus.EventType.PUSH_REGISTRATION, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
eventBus.post(new EventStatus
|
||||
(userEntity.getId(),
|
||||
EventStatus.EventType
|
||||
.PUSH_REGISTRATION, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException while updating user");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
eventBus.post(new EventStatus(userEntity.getId(),
|
||||
EventStatus.EventType.PUSH_REGISTRATION, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
eventBus.post(new EventStatus(userEntity.getId(),
|
||||
EventStatus.EventType.PUSH_REGISTRATION, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Key readKeyFromString(boolean readPublicKey, String keyString) {
|
||||
if (readPublicKey) {
|
||||
keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----",
|
||||
"").replace("-----END PUBLIC KEY-----", "");
|
||||
} else {
|
||||
keyString = keyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----",
|
||||
"").replace("-----END PRIVATE KEY-----", "");
|
||||
}
|
||||
|
||||
KeyFactory keyFactory = null;
|
||||
try {
|
||||
keyFactory = KeyFactory.getInstance("RSA");
|
||||
if (readPublicKey) {
|
||||
X509EncodedKeySpec keySpec =
|
||||
new X509EncodedKeySpec(Base64.decode(keyString, Base64.DEFAULT));
|
||||
return keyFactory.generatePublic(keySpec);
|
||||
} else {
|
||||
PKCS8EncodedKeySpec keySpec =
|
||||
new PKCS8EncodedKeySpec(Base64.decode(keyString, Base64.DEFAULT));
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d("TAG", "No such algorithm while reading key from string");
|
||||
} catch (InvalidKeySpecException e) {
|
||||
Log.d("TAG", "Invalid key spec while reading key from string");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Key readKeyFromFile(boolean readPublicKey) {
|
||||
String path;
|
||||
|
||||
if (readPublicKey) {
|
||||
path = publicKeyFile.getAbsolutePath();
|
||||
} else {
|
||||
path = privateKeyFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
try (FileInputStream fileInputStream = new FileInputStream(path)) {
|
||||
byte[] bytes = new byte[fileInputStream.available()];
|
||||
fileInputStream.read(bytes);
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
|
||||
if (readPublicKey) {
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
|
||||
return keyFactory.generatePublic(keySpec);
|
||||
} else {
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.d(TAG, "Failed to find path while reading the Key");
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "IOException while reading the key");
|
||||
} catch (InvalidKeySpecException e) {
|
||||
Log.d(TAG, "InvalidKeySpecException while reading the key");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(TAG, "RSA algorithm not supported");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
450
app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt
Normal file
450
app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.R.string
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.events.EventStatus
|
||||
import com.nextcloud.talk.events.EventStatus.EventType.PUSH_REGISTRATION
|
||||
import com.nextcloud.talk.models.SignatureVerification
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.push.PushConfigurationState
|
||||
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
|
||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.Key
|
||||
import java.security.KeyFactory
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.PublicKey
|
||||
import java.security.Signature
|
||||
import java.security.SignatureException
|
||||
import java.security.spec.InvalidKeySpecException
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import java.util.HashMap
|
||||
import javax.inject.Inject
|
||||
import kotlin.experimental.and
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class PushUtils(val usersRepository: UsersRepository) {
|
||||
@JvmField
|
||||
@Inject
|
||||
var userUtils: UserUtils? = null
|
||||
@JvmField
|
||||
@Inject
|
||||
var appPreferences: AppPreferences? = null
|
||||
@JvmField
|
||||
@Inject
|
||||
var eventBus: EventBus? = null
|
||||
@JvmField
|
||||
@Inject
|
||||
var ncApi: NcApi? = null
|
||||
private val keysFile: File
|
||||
private val publicKeyFile: File
|
||||
private val privateKeyFile: File
|
||||
private val proxyServer: String
|
||||
fun verifySignature(
|
||||
signatureBytes: ByteArray?,
|
||||
subjectBytes: ByteArray?
|
||||
): SignatureVerification {
|
||||
val signature: Signature?
|
||||
var pushConfigurationState: PushConfigurationState?
|
||||
var publicKey: PublicKey?
|
||||
val signatureVerification =
|
||||
SignatureVerification()
|
||||
signatureVerification.signatureValid = false
|
||||
val userEntities: List<UserNgEntity> = usersRepository.getUsers()
|
||||
try {
|
||||
signature = Signature.getInstance("SHA512withRSA")
|
||||
if (userEntities.size > 0) {
|
||||
for (userEntity in userEntities) {
|
||||
pushConfigurationState = userEntity.pushConfiguration
|
||||
if (pushConfigurationState?.userPublicKey != null) {
|
||||
publicKey = readKeyFromString(
|
||||
true, pushConfigurationState.userPublicKey!!
|
||||
) as PublicKey?
|
||||
signature.initVerify(publicKey)
|
||||
signature.update(subjectBytes)
|
||||
if (signature.verify(signatureBytes)) {
|
||||
signatureVerification.signatureValid = true
|
||||
signatureVerification.userEntity = userEntity
|
||||
return signatureVerification
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
Log.d(TAG, "No such algorithm")
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Error while trying to parse push configuration viewState")
|
||||
} catch (e: InvalidKeyException) {
|
||||
Log.d(TAG, "Invalid key while trying to verify")
|
||||
} catch (e: SignatureException) {
|
||||
Log.d(TAG, "Signature exception while trying to verify")
|
||||
}
|
||||
return signatureVerification
|
||||
}
|
||||
|
||||
private fun saveKeyToFile(
|
||||
key: Key,
|
||||
path: String?
|
||||
): Int {
|
||||
val encoded: ByteArray? = key.encoded
|
||||
try {
|
||||
if (!File(path).exists()) {
|
||||
if (!File(path).createNewFile()) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
FileOutputStream(path)
|
||||
.use { keyFileOutputStream ->
|
||||
keyFileOutputStream.write(encoded)
|
||||
return 0
|
||||
}
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.d(TAG, "Failed to save key to file")
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Failed to save key to file via IOException")
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun generateSHA512Hash(pushToken: String): String {
|
||||
var messageDigest: MessageDigest? = null
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("SHA-512")
|
||||
messageDigest.update(pushToken.toByteArray())
|
||||
return bytesToHex(messageDigest.digest())
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
Log.d(TAG, "SHA-512 algorithm not supported")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
private fun bytesToHex(bytes: ByteArray): String {
|
||||
val result = StringBuilder()
|
||||
for (individualByte in bytes) {
|
||||
result.append(
|
||||
((individualByte and 0xff.toByte()) + 0x100).toString(16)
|
||||
.substring(1)
|
||||
)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
fun generateRsa2048KeyPair(): Int {
|
||||
if (!publicKeyFile.exists() && !privateKeyFile.exists()) {
|
||||
if (!keysFile.exists()) {
|
||||
keysFile.mkdirs()
|
||||
}
|
||||
var keyGen: KeyPairGenerator? = null
|
||||
try {
|
||||
keyGen = KeyPairGenerator.getInstance("RSA")
|
||||
keyGen.initialize(2048)
|
||||
val pair: KeyPair = keyGen.generateKeyPair()
|
||||
val statusPrivate =
|
||||
saveKeyToFile(pair.private, privateKeyFile.absolutePath)
|
||||
val statusPublic =
|
||||
saveKeyToFile(pair.public, publicKeyFile.absolutePath)
|
||||
return if (statusPrivate == 0 && statusPublic == 0) {
|
||||
// all went well
|
||||
|
||||
0
|
||||
} else {
|
||||
-2
|
||||
}
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
Log.d(TAG, "RSA algorithm not supported")
|
||||
}
|
||||
}
|
||||
|
||||
// we failed to generate the key
|
||||
else {
|
||||
// We already have the key
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
return -2
|
||||
}
|
||||
|
||||
fun pushRegistrationToServer() {
|
||||
val token: String = appPreferences!!.pushToken
|
||||
if (!TextUtils.isEmpty(token)) {
|
||||
var credentials: String
|
||||
val pushTokenHash = generateSHA512Hash(token).toLowerCase()
|
||||
val devicePublicKey =
|
||||
readKeyFromFile(true) as PublicKey?
|
||||
if (devicePublicKey != null) {
|
||||
val publicKeyBytes: ByteArray? =
|
||||
Base64.encode(devicePublicKey.encoded, Base64.NO_WRAP)
|
||||
var publicKey = String(publicKeyBytes!!)
|
||||
publicKey = publicKey.replace("(.{64})".toRegex(), "$1\n")
|
||||
publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey\n-----END PUBLIC KEY-----\n"
|
||||
if (userUtils!!.anyUserExists()) {
|
||||
var accountPushData: PushConfigurationState? = null
|
||||
for (userEntityObject in usersRepository.getUsers()) {
|
||||
val userEntity = userEntityObject
|
||||
accountPushData = userEntity.pushConfiguration
|
||||
if (accountPushData == null || accountPushData.pushToken != token) {
|
||||
val queryMap: MutableMap<String, String> =
|
||||
HashMap()
|
||||
queryMap["format"] = "json"
|
||||
queryMap["pushTokenHash"] = pushTokenHash
|
||||
queryMap["devicePublicKey"] = publicKey
|
||||
queryMap["proxyServer"] = proxyServer
|
||||
credentials = ApiUtils.getCredentials(
|
||||
userEntity.username, userEntity.token
|
||||
)
|
||||
val finalCredentials = credentials
|
||||
ncApi!!.registerDeviceForNotificationsWithNextcloud(
|
||||
credentials,
|
||||
ApiUtils.getUrlNextcloudPush(userEntity.baseUrl),
|
||||
queryMap
|
||||
)
|
||||
.subscribe(object : Observer<PushRegistrationOverall> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(pushRegistrationOverall: PushRegistrationOverall) {
|
||||
val proxyMap: MutableMap<String, String> =
|
||||
HashMap()
|
||||
proxyMap["pushToken"] = token
|
||||
proxyMap["deviceIdentifier"] =
|
||||
pushRegistrationOverall.ocs.data.deviceIdentifier
|
||||
proxyMap["deviceIdentifierSignature"] = pushRegistrationOverall.ocs
|
||||
.data.signature
|
||||
proxyMap["userPublicKey"] = pushRegistrationOverall.ocs
|
||||
.data.publicKey
|
||||
ncApi!!.registerDeviceForNotificationsWithProxy(
|
||||
ApiUtils.getUrlPushProxy(), proxyMap
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Observer<Void> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(aVoid: Void) {
|
||||
val pushConfigurationState =
|
||||
PushConfigurationState()
|
||||
pushConfigurationState.pushToken = token
|
||||
pushConfigurationState.deviceIdentifier = pushRegistrationOverall
|
||||
.ocs.data.deviceIdentifier
|
||||
pushConfigurationState.deviceIdentifierSignature =
|
||||
pushRegistrationOverall.ocs.data.signature
|
||||
pushConfigurationState.userPublicKey = pushRegistrationOverall.ocs
|
||||
.data.publicKey
|
||||
pushConfigurationState.usesRegularPass = false
|
||||
try {
|
||||
userUtils!!.createOrUpdateUser(
|
||||
null,
|
||||
null, null,
|
||||
userEntity.displayName,
|
||||
LoganSquare.serialize(
|
||||
pushConfigurationState
|
||||
), null,
|
||||
null, userEntity.id, null, null, null
|
||||
)
|
||||
.subscribe(object : Observer<UserEntity> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(userEntity: UserEntity) {
|
||||
eventBus!!.post(
|
||||
EventStatus(
|
||||
userEntity.id,
|
||||
PUSH_REGISTRATION,
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
eventBus!!.post(
|
||||
EventStatus(
|
||||
userEntity.id,
|
||||
PUSH_REGISTRATION, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "IOException while updating user")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
eventBus!!.post(
|
||||
EventStatus(
|
||||
userEntity.id,
|
||||
PUSH_REGISTRATION,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
eventBus!!.post(
|
||||
EventStatus(
|
||||
userEntity.id,
|
||||
PUSH_REGISTRATION,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readKeyFromString(
|
||||
readPublicKey: Boolean,
|
||||
keyString: String
|
||||
): Key? {
|
||||
var keyString = keyString
|
||||
keyString = if (readPublicKey) {
|
||||
keyString.replace("\\n".toRegex(), "")
|
||||
.replace(
|
||||
"-----BEGIN PUBLIC KEY-----",
|
||||
""
|
||||
)
|
||||
.replace("-----END PUBLIC KEY-----", "")
|
||||
} else {
|
||||
keyString.replace("\\n".toRegex(), "")
|
||||
.replace(
|
||||
"-----BEGIN PRIVATE KEY-----",
|
||||
""
|
||||
)
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
}
|
||||
var keyFactory: KeyFactory? = null
|
||||
try {
|
||||
keyFactory = KeyFactory.getInstance("RSA")
|
||||
return if (readPublicKey) {
|
||||
val keySpec = X509EncodedKeySpec(
|
||||
Base64.decode(keyString, Base64.DEFAULT)
|
||||
)
|
||||
keyFactory.generatePublic(keySpec)
|
||||
} else {
|
||||
val keySpec =
|
||||
PKCS8EncodedKeySpec(
|
||||
Base64.decode(keyString, Base64.DEFAULT)
|
||||
)
|
||||
keyFactory.generatePrivate(keySpec)
|
||||
}
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
Log.d("TAG", "No such algorithm while reading key from string")
|
||||
} catch (e: InvalidKeySpecException) {
|
||||
Log.d("TAG", "Invalid key spec while reading key from string")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun readKeyFromFile(readPublicKey: Boolean): Key? {
|
||||
val path: String?
|
||||
path = if (readPublicKey) {
|
||||
publicKeyFile.absolutePath
|
||||
} else {
|
||||
privateKeyFile.absolutePath
|
||||
}
|
||||
try {
|
||||
FileInputStream(path)
|
||||
.use { fileInputStream ->
|
||||
val bytes = ByteArray(fileInputStream.available())
|
||||
fileInputStream.read(bytes)
|
||||
val keyFactory: KeyFactory = KeyFactory.getInstance("RSA")
|
||||
return if (readPublicKey) {
|
||||
val keySpec =
|
||||
X509EncodedKeySpec(bytes)
|
||||
keyFactory.generatePublic(keySpec)
|
||||
} else {
|
||||
val keySpec =
|
||||
PKCS8EncodedKeySpec(bytes)
|
||||
keyFactory.generatePrivate(keySpec)
|
||||
}
|
||||
}
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.d(TAG, "Failed to find path while reading the Key")
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "IOException while reading the key")
|
||||
} catch (e: InvalidKeySpecException) {
|
||||
Log.d(TAG, "InvalidKeySpecException while reading the key")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
Log.d(TAG, "RSA algorithm not supported")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PushUtils"
|
||||
}
|
||||
|
||||
init {
|
||||
sharedApplication!!
|
||||
.componentApplication
|
||||
.inject(this)
|
||||
keysFile = sharedApplication!!
|
||||
.getDir("PushKeyStore", Context.MODE_PRIVATE)
|
||||
publicKeyFile = File(
|
||||
sharedApplication!!.getDir(
|
||||
"PushKeystore",
|
||||
Context.MODE_PRIVATE
|
||||
), "push_key.pub"
|
||||
)
|
||||
privateKeyFile = File(
|
||||
sharedApplication!!.getDir(
|
||||
"PushKeystore",
|
||||
Context.MODE_PRIVATE
|
||||
), "push_key.priv"
|
||||
)
|
||||
proxyServer =
|
||||
sharedApplication!!.resources
|
||||
.getString(string.nc_push_server_url)
|
||||
}
|
||||
}
|
@ -23,14 +23,15 @@ package com.nextcloud.talk.utils.preferences.preferencestorage;
|
||||
import android.content.Context;
|
||||
import com.nextcloud.talk.interfaces.ConversationInfoInterface;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.yarolegovich.mp.io.StorageModule;
|
||||
|
||||
public class DatabaseStorageFactory implements StorageModule.Factory {
|
||||
private UserEntity conversationUser;
|
||||
private UserNgEntity conversationUser;
|
||||
private String conversationToken;
|
||||
private ConversationInfoInterface conversationInfoInterface;
|
||||
|
||||
public DatabaseStorageFactory(UserEntity conversationUser, String conversationToken,
|
||||
public DatabaseStorageFactory(UserNgEntity conversationUser, String conversationToken,
|
||||
ConversationInfoInterface conversationInfoInterface) {
|
||||
this.conversationUser = conversationUser;
|
||||
this.conversationToken = conversationToken;
|
||||
|
@ -1,297 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils.preferences.preferencestorage;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import autodagger.AutoInjector;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.interfaces.ConversationInfoInterface;
|
||||
import com.nextcloud.talk.models.database.ArbitraryStorageEntity;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils;
|
||||
import com.yarolegovich.mp.io.StorageModule;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class DatabaseStorageModule implements StorageModule {
|
||||
@Inject
|
||||
ArbitraryStorageUtils arbitraryStorageUtils;
|
||||
|
||||
@Inject
|
||||
NcApi ncApi;
|
||||
|
||||
private UserEntity conversationUser;
|
||||
private String conversationToken;
|
||||
private long accountIdentifier;
|
||||
|
||||
private boolean lobbyValue;
|
||||
private boolean favoriteConversationValue;
|
||||
private boolean allowGuestsValue;
|
||||
|
||||
private Boolean hasPassword;
|
||||
private String conversationNameValue;
|
||||
|
||||
private String messageNotificationLevel;
|
||||
private ConversationInfoInterface conversationInfoInterface;
|
||||
|
||||
public DatabaseStorageModule(UserEntity conversationUser, String conversationToken,
|
||||
ConversationInfoInterface conversationInfoInterface) {
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getComponentApplication()
|
||||
.inject(this);
|
||||
|
||||
this.conversationUser = conversationUser;
|
||||
this.accountIdentifier = conversationUser.getId();
|
||||
this.conversationToken = conversationToken;
|
||||
this.conversationInfoInterface = conversationInfoInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBoolean(String key, boolean value) {
|
||||
if (!key.equals("conversation_lobby") && !key.equals("allow_guests") && !key.equals(
|
||||
"favorite_conversation")) {
|
||||
arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, Boolean.toString(value),
|
||||
conversationToken);
|
||||
} else {
|
||||
switch (key) {
|
||||
case "conversation_lobby":
|
||||
lobbyValue = value;
|
||||
break;
|
||||
case "allow_guests":
|
||||
allowGuestsValue = value;
|
||||
break;
|
||||
case "favorite_conversation":
|
||||
favoriteConversationValue = value;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveString(String key, String value) {
|
||||
if (!key.equals("message_notification_level")
|
||||
&& !key.equals("conversation_name")
|
||||
&& !key.equals("conversation_password")) {
|
||||
arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, value, conversationToken);
|
||||
} else {
|
||||
if (key.equals("message_notification_level")) {
|
||||
if (conversationUser.hasSpreedFeatureCapability("notification-levels")) {
|
||||
if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(
|
||||
value)) {
|
||||
int intValue;
|
||||
switch (value) {
|
||||
case "never":
|
||||
intValue = 3;
|
||||
break;
|
||||
case "mention":
|
||||
intValue = 2;
|
||||
break;
|
||||
case "always":
|
||||
intValue = 1;
|
||||
break;
|
||||
default:
|
||||
intValue = 0;
|
||||
}
|
||||
|
||||
ncApi.setNotificationLevel(
|
||||
ApiUtils.getCredentials(conversationUser.getUsername(),
|
||||
conversationUser.getToken()),
|
||||
ApiUtils.getUrlForSettingNotificationlevel(conversationUser.getBaseUrl(),
|
||||
conversationToken),
|
||||
intValue)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(GenericOverall genericOverall) {
|
||||
messageNotificationLevel = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
messageNotificationLevel = value;
|
||||
}
|
||||
}
|
||||
} else if (key.equals("conversation_password")) {
|
||||
if (hasPassword != null) {
|
||||
ncApi.setPassword(ApiUtils.getCredentials(conversationUser.getUsername(),
|
||||
conversationUser.getToken()),
|
||||
ApiUtils.getUrlForPassword(conversationUser.getBaseUrl(),
|
||||
conversationToken), value)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(GenericOverall genericOverall) {
|
||||
hasPassword = !TextUtils.isEmpty(value);
|
||||
conversationInfoInterface.passwordSet(TextUtils.isEmpty(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
hasPassword = Boolean.parseBoolean(value);
|
||||
}
|
||||
} else if (key.equals("conversation_name")) {
|
||||
if (!TextUtils.isEmpty(conversationNameValue) && !conversationNameValue.equals(value)) {
|
||||
ncApi.renameRoom(ApiUtils.getCredentials(conversationUser.getUsername(),
|
||||
conversationUser.getToken()), ApiUtils.getRoom(conversationUser.getBaseUrl(),
|
||||
conversationToken), value)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(GenericOverall genericOverall) {
|
||||
conversationNameValue = value;
|
||||
conversationInfoInterface.conversationNameSet(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
conversationNameValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(String key, int value) {
|
||||
arbitraryStorageUtils.storeStorageSetting(accountIdentifier, key, Integer.toString(value),
|
||||
conversationToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveStringSet(String key, Set<String> value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultVal) {
|
||||
if (key.equals("conversation_lobby")) {
|
||||
return lobbyValue;
|
||||
} else if (key.equals("allow_guests")) {
|
||||
return allowGuestsValue;
|
||||
} else if (key.equals("favorite_conversation")) {
|
||||
return favoriteConversationValue;
|
||||
} else {
|
||||
ArbitraryStorageEntity valueFromDb =
|
||||
arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken);
|
||||
if (valueFromDb == null) {
|
||||
return defaultVal;
|
||||
} else {
|
||||
return Boolean.parseBoolean(valueFromDb.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String defaultVal) {
|
||||
if (!key.equals("message_notification_level")
|
||||
&& !key.equals("conversation_name")
|
||||
&& !key.equals("conversation_password")) {
|
||||
ArbitraryStorageEntity valueFromDb =
|
||||
arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken);
|
||||
if (valueFromDb == null) {
|
||||
return defaultVal;
|
||||
} else {
|
||||
return valueFromDb.getValue();
|
||||
}
|
||||
} else if (key.equals("message_notification_level")) {
|
||||
return messageNotificationLevel;
|
||||
} else if (key.equals("conversation_name")) {
|
||||
return conversationNameValue;
|
||||
} else if (key.equals("conversation_password")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defaultVal) {
|
||||
ArbitraryStorageEntity valueFromDb =
|
||||
arbitraryStorageUtils.getStorageSetting(accountIdentifier, key, conversationToken);
|
||||
if (valueFromDb == null) {
|
||||
return defaultVal;
|
||||
} else {
|
||||
return Integer.parseInt(valueFromDb.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStringSet(String key, Set<String> defaultVal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Bundle savedState) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils.preferences.preferencestorage
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import autodagger.AutoInjector
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.interfaces.ConversationInfoInterface
|
||||
import com.nextcloud.talk.models.database.ArbitraryStorageEntity
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||
import com.nextcloud.talk.newarch.local.models.hasSpreedFeatureCapability
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils
|
||||
import com.yarolegovich.mp.io.StorageModule
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class DatabaseStorageModule(
|
||||
private val conversationUser: UserNgEntity,
|
||||
private val conversationToken: String,
|
||||
private val conversationInfoInterface: ConversationInfoInterface
|
||||
) : StorageModule {
|
||||
@JvmField
|
||||
@Inject
|
||||
var arbitraryStorageUtils: ArbitraryStorageUtils? =
|
||||
null
|
||||
@JvmField
|
||||
@Inject
|
||||
var ncApi: NcApi? = null
|
||||
private val accountIdentifier: Long
|
||||
private var lobbyValue = false
|
||||
private var favoriteConversationValue = false
|
||||
private var allowGuestsValue = false
|
||||
private var hasPassword: Boolean? = null
|
||||
private var conversationNameValue: String? = null
|
||||
private var messageNotificationLevel: String? = null
|
||||
override fun saveBoolean(
|
||||
key: String,
|
||||
value: Boolean
|
||||
) {
|
||||
if (key != "conversation_lobby" && key != "allow_guests" && key != "favorite_conversation"
|
||||
) {
|
||||
arbitraryStorageUtils!!.storeStorageSetting(
|
||||
accountIdentifier, key, value.toString(),
|
||||
conversationToken
|
||||
)
|
||||
} else {
|
||||
when (key) {
|
||||
"conversation_lobby" -> lobbyValue = value
|
||||
"allow_guests" -> allowGuestsValue = value
|
||||
"favorite_conversation" -> favoriteConversationValue = value
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveString(
|
||||
key: String,
|
||||
value: String
|
||||
) {
|
||||
if (key != "message_notification_level"
|
||||
&& key != "conversation_name"
|
||||
&& key != "conversation_password"
|
||||
) {
|
||||
arbitraryStorageUtils!!.storeStorageSetting(accountIdentifier, key, value, conversationToken)
|
||||
} else {
|
||||
if (key == "message_notification_level") {
|
||||
if (conversationUser.hasSpreedFeatureCapability("notification-levels")) {
|
||||
if (!TextUtils.isEmpty(
|
||||
messageNotificationLevel
|
||||
) && messageNotificationLevel != value
|
||||
) {
|
||||
val intValue: Int
|
||||
intValue = when (value) {
|
||||
"never" -> 3
|
||||
"mention" -> 2
|
||||
"always" -> 1
|
||||
else -> 0
|
||||
}
|
||||
ncApi!!.setNotificationLevel(
|
||||
ApiUtils.getCredentials(
|
||||
conversationUser.username,
|
||||
conversationUser.token
|
||||
),
|
||||
ApiUtils.getUrlForSettingNotificationlevel(
|
||||
conversationUser.baseUrl,
|
||||
conversationToken
|
||||
),
|
||||
intValue
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
messageNotificationLevel = value
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {}
|
||||
override fun onComplete() {}
|
||||
})
|
||||
} else {
|
||||
messageNotificationLevel = value
|
||||
}
|
||||
}
|
||||
} else if (key == "conversation_password") {
|
||||
if (hasPassword != null) {
|
||||
ncApi!!.setPassword(
|
||||
ApiUtils.getCredentials(
|
||||
conversationUser.username,
|
||||
conversationUser.token
|
||||
),
|
||||
ApiUtils.getUrlForPassword(
|
||||
conversationUser.baseUrl,
|
||||
conversationToken
|
||||
), value
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
hasPassword = !TextUtils.isEmpty(value)
|
||||
conversationInfoInterface.passwordSet(TextUtils.isEmpty(value))
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {}
|
||||
override fun onComplete() {}
|
||||
})
|
||||
} else {
|
||||
hasPassword = value.toBoolean()
|
||||
}
|
||||
} else if (key == "conversation_name") {
|
||||
if (!TextUtils.isEmpty(
|
||||
conversationNameValue
|
||||
) && conversationNameValue != value
|
||||
) {
|
||||
ncApi!!.renameRoom(
|
||||
ApiUtils.getCredentials(
|
||||
conversationUser.username,
|
||||
conversationUser.token
|
||||
), ApiUtils.getRoom(
|
||||
conversationUser.baseUrl,
|
||||
conversationToken
|
||||
), value
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
conversationNameValue = value
|
||||
conversationInfoInterface.conversationNameSet(value)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {}
|
||||
override fun onComplete() {}
|
||||
})
|
||||
} else {
|
||||
conversationNameValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveInt(
|
||||
key: String,
|
||||
value: Int
|
||||
) {
|
||||
arbitraryStorageUtils!!.storeStorageSetting(
|
||||
accountIdentifier, key, Integer.toString(value),
|
||||
conversationToken
|
||||
)
|
||||
}
|
||||
|
||||
override fun saveStringSet(
|
||||
key: String,
|
||||
value: Set<String>
|
||||
) {
|
||||
}
|
||||
|
||||
override fun getBoolean(
|
||||
key: String,
|
||||
defaultVal: Boolean
|
||||
): Boolean {
|
||||
return if (key == "conversation_lobby") {
|
||||
lobbyValue
|
||||
} else if (key == "allow_guests") {
|
||||
allowGuestsValue
|
||||
} else if (key == "favorite_conversation") {
|
||||
favoriteConversationValue
|
||||
} else {
|
||||
val valueFromDb: ArbitraryStorageEntity? =
|
||||
arbitraryStorageUtils!!.getStorageSetting(accountIdentifier, key, conversationToken)
|
||||
if (valueFromDb == null) {
|
||||
defaultVal
|
||||
} else {
|
||||
valueFromDb.value!!.toBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getString(
|
||||
key: String,
|
||||
defaultVal: String
|
||||
): String {
|
||||
if (key != "message_notification_level"
|
||||
&& key != "conversation_name"
|
||||
&& key != "conversation_password"
|
||||
) {
|
||||
val valueFromDb: ArbitraryStorageEntity? =
|
||||
arbitraryStorageUtils!!.getStorageSetting(accountIdentifier, key, conversationToken)
|
||||
return if (valueFromDb == null) {
|
||||
defaultVal
|
||||
} else {
|
||||
valueFromDb.value
|
||||
}
|
||||
} else if (key == "message_notification_level") {
|
||||
return messageNotificationLevel!!
|
||||
} else if (key == "conversation_name") {
|
||||
return conversationNameValue!!
|
||||
} else if (key == "conversation_password") {
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun getInt(
|
||||
key: String,
|
||||
defaultVal: Int
|
||||
): Int {
|
||||
val valueFromDb: ArbitraryStorageEntity? =
|
||||
arbitraryStorageUtils!!.getStorageSetting(accountIdentifier, key, conversationToken)
|
||||
return if (valueFromDb == null) {
|
||||
defaultVal
|
||||
} else {
|
||||
Integer.parseInt(valueFromDb.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStringSet(
|
||||
key: String,
|
||||
defaultVal: Set<String>
|
||||
): Set<String>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {}
|
||||
override fun onRestoreInstanceState(savedState: Bundle) {}
|
||||
|
||||
init {
|
||||
sharedApplication!!
|
||||
.componentApplication
|
||||
.inject(this)
|
||||
accountIdentifier = conversationUser.id
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils.singletons;
|
||||
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
|
||||
public class ApplicationWideCurrentRoomHolder {
|
||||
private static final ApplicationWideCurrentRoomHolder holder =
|
||||
new ApplicationWideCurrentRoomHolder();
|
||||
private String currentRoomId = "";
|
||||
private String currentRoomToken = "";
|
||||
private UserEntity userInRoom = new UserEntity();
|
||||
private boolean inCall = false;
|
||||
private String session = "";
|
||||
|
||||
public static ApplicationWideCurrentRoomHolder getInstance() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
currentRoomId = "";
|
||||
userInRoom = new UserEntity();
|
||||
inCall = false;
|
||||
currentRoomToken = "";
|
||||
session = "";
|
||||
}
|
||||
|
||||
public String getCurrentRoomToken() {
|
||||
return currentRoomToken;
|
||||
}
|
||||
|
||||
public void setCurrentRoomToken(String currentRoomToken) {
|
||||
this.currentRoomToken = currentRoomToken;
|
||||
}
|
||||
|
||||
public String getCurrentRoomId() {
|
||||
return currentRoomId;
|
||||
}
|
||||
|
||||
public void setCurrentRoomId(String currentRoomId) {
|
||||
this.currentRoomId = currentRoomId;
|
||||
}
|
||||
|
||||
public UserEntity getUserInRoom() {
|
||||
return userInRoom;
|
||||
}
|
||||
|
||||
public void setUserInRoom(UserEntity userInRoom) {
|
||||
this.userInRoom = userInRoom;
|
||||
}
|
||||
|
||||
public boolean isInCall() {
|
||||
return inCall;
|
||||
}
|
||||
|
||||
public void setInCall(boolean inCall) {
|
||||
this.inCall = inCall;
|
||||
}
|
||||
|
||||
public String getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void setSession(String session) {
|
||||
this.session = session;
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ import com.nextcloud.talk.models.json.websocket.ErrorOverallWebSocketMessage;
|
||||
import com.nextcloud.talk.models.json.websocket.EventOverallWebSocketMessage;
|
||||
import com.nextcloud.talk.models.json.websocket.HelloResponseOverallWebSocketMessage;
|
||||
import com.nextcloud.talk.models.json.websocket.JoinedRoomOverallWebSocketMessage;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.nextcloud.talk.utils.LoggingUtils;
|
||||
import com.nextcloud.talk.utils.MagicMap;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
@ -72,7 +73,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
|
||||
@Inject
|
||||
Context context;
|
||||
|
||||
private UserEntity conversationUser;
|
||||
private UserNgEntity conversationUser;
|
||||
private String webSocketTicket;
|
||||
private String resumeId;
|
||||
private String sessionId;
|
||||
@ -91,7 +92,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
|
||||
|
||||
private List<String> messagesQueue = new ArrayList<>();
|
||||
|
||||
MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl,
|
||||
MagicWebSocketInstance(UserNgEntity conversationUser, String connectionUrl,
|
||||
String webSocketTicket) {
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getComponentApplication()
|
||||
|
@ -36,6 +36,7 @@ import com.nextcloud.talk.models.json.websocket.RequestOfferSignalingMessage;
|
||||
import com.nextcloud.talk.models.json.websocket.RoomOverallWebSocketMessage;
|
||||
import com.nextcloud.talk.models.json.websocket.RoomWebSocketMessage;
|
||||
import com.nextcloud.talk.models.json.websocket.SignalingDataWebSocketMessageForOffer;
|
||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -65,7 +66,7 @@ public class WebSocketConnectionHelper {
|
||||
}
|
||||
|
||||
public static synchronized MagicWebSocketInstance getExternalSignalingInstanceForServer(
|
||||
String url, UserEntity userEntity, String webSocketTicket, boolean isGuest) {
|
||||
String url, UserNgEntity userEntity, String webSocketTicket, boolean isGuest) {
|
||||
String generatedURL = url.replace("https://", "wss://").replace("http://", "ws://");
|
||||
|
||||
if (generatedURL.endsWith("/")) {
|
||||
@ -102,7 +103,7 @@ public class WebSocketConnectionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
HelloOverallWebSocketMessage getAssembledHelloModel(UserEntity userEntity, String ticket) {
|
||||
HelloOverallWebSocketMessage getAssembledHelloModel(UserNgEntity userEntity, String ticket) {
|
||||
HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage();
|
||||
helloOverallWebSocketMessage.setType("hello");
|
||||
HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage();
|
||||
|
Loading…
Reference in New Issue
Block a user