mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-11 18:10:44 +00:00
Partly implement mention highlight
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
cfd2d0402d
commit
08f44a04e1
@ -129,7 +129,7 @@ dependencies {
|
||||
|
||||
implementation 'com.github.HITGIF:TextFieldBoxes:1.4.3'
|
||||
|
||||
implementation 'eu.davidea:flexible-adapter:5.0.3'
|
||||
implementation 'eu.davidea:flexible-adapter:5.0.4'
|
||||
implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b3'
|
||||
|
||||
implementation 'com.github.bumptech.glide:glide:4.3.0'
|
||||
|
@ -794,38 +794,38 @@ public class CallActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void checkCapabilities() {
|
||||
ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl))
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<CapabilitiesOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl))
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<CapabilitiesOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(CapabilitiesOverall capabilitiesOverall) {
|
||||
isMultiSession = capabilitiesOverall.getOcs().getData()
|
||||
.getCapabilities().getSpreedCapability() != null &&
|
||||
capabilitiesOverall.getOcs().getData()
|
||||
.getCapabilities().getSpreedCapability()
|
||||
.getFeatures() != null && capabilitiesOverall.getOcs().getData()
|
||||
.getCapabilities().getSpreedCapability()
|
||||
.getFeatures().contains("multi-room-users");
|
||||
@Override
|
||||
public void onNext(CapabilitiesOverall capabilitiesOverall) {
|
||||
isMultiSession = capabilitiesOverall.getOcs().getData()
|
||||
.getCapabilities().getSpreedCapability() != null &&
|
||||
capabilitiesOverall.getOcs().getData()
|
||||
.getCapabilities().getSpreedCapability()
|
||||
.getFeatures() != null && capabilitiesOverall.getOcs().getData()
|
||||
.getCapabilities().getSpreedCapability()
|
||||
.getFeatures().contains("multi-room-users");
|
||||
|
||||
joinRoomAndCall();
|
||||
}
|
||||
joinRoomAndCall();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
isMultiSession = false;
|
||||
}
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
isMultiSession = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void joinRoomAndCall() {
|
||||
|
@ -91,7 +91,7 @@ public class MenuItem extends AbstractFlexibleItem<MenuItem.MenuItemViewHolder>
|
||||
holder.menuTitle.setText(spannableString);
|
||||
} else {
|
||||
holder.menuTitle.setText(title);
|
||||
holder.menuTitle.setCompoundDrawablesWithIntrinsicBounds(icon,null,null,null);
|
||||
holder.menuTitle.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
|
||||
holder.menuTitle.setCompoundDrawablePadding(padding);
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,8 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
public TextView contactDisplayName;
|
||||
@BindView(R.id.avatar_flip_view)
|
||||
public FlipView avatarFlipView;
|
||||
@Nullable @BindView(R.id.secondary_text)
|
||||
@Nullable
|
||||
@BindView(R.id.secondary_text)
|
||||
public TextView contactMentionId;
|
||||
|
||||
/**
|
||||
|
@ -20,26 +20,48 @@
|
||||
|
||||
package com.nextcloud.talk.adapters.messages;
|
||||
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
import com.stfalcon.chatkit.messages.MessageHolders;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class MagicIncomingTextMessageViewHolder
|
||||
extends MessageHolders.IncomingTextMessageViewHolder<ChatMessage> {
|
||||
|
||||
@BindView(R.id.messageAuthor)
|
||||
TextView messageAuthor;
|
||||
|
||||
@BindView(R.id.messageText)
|
||||
TextView messageText;
|
||||
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
|
||||
private UserEntity currentUser;
|
||||
|
||||
public MagicIncomingTextMessageViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
|
||||
|
||||
currentUser = userUtils.getCurrentUser();
|
||||
}
|
||||
|
||||
|
||||
@ -52,5 +74,32 @@ public class MagicIncomingTextMessageViewHolder
|
||||
} else {
|
||||
messageAuthor.setText(R.string.nc_nick_guest);
|
||||
}
|
||||
|
||||
HashMap<String, HashMap<String, String>> messageParameters = message.getMessageParameters();
|
||||
|
||||
String messageString = message.getText();
|
||||
|
||||
if (messageParameters != null && message.getMessageParameters().size() > 0) {
|
||||
for (String key : message.getMessageParameters().keySet()) {
|
||||
HashMap<String, String> individualHashMap = message.getMessageParameters().get(key);
|
||||
if (individualHashMap.get("type").equals("user")) {
|
||||
int color;
|
||||
|
||||
if (messageParameters.get(key).get("id").equals(currentUser.getUserId())) {
|
||||
color = NextcloudTalkApplication.getSharedApplication().getResources().getColor(R.color
|
||||
.colorAccent);
|
||||
} else {
|
||||
color = NextcloudTalkApplication.getSharedApplication().getResources().getColor(R.color
|
||||
.colorAccentComplement);
|
||||
}
|
||||
|
||||
messageString = DisplayUtils.searchAndColor(messageString,
|
||||
"@" + messageParameters.get(key).get("name"), color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
messageText.setText(Html.fromHtml(messageString));
|
||||
}
|
||||
}
|
@ -261,13 +261,13 @@ public interface NcApi {
|
||||
@GET
|
||||
Observable<CapabilitiesOverall> getCapabilities(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
/*
|
||||
QueryMap items are as follows:
|
||||
- "lookIntoFuture": int (0 or 1),
|
||||
- "limit" : int, range 100-200,
|
||||
- "timeout": used with look into future, 30 default, 60 at most
|
||||
- "lastKnownMessageId", int, use one from X-Chat-Last-Given
|
||||
*/
|
||||
/*
|
||||
QueryMap items are as follows:
|
||||
- "lookIntoFuture": int (0 or 1),
|
||||
- "limit" : int, range 100-200,
|
||||
- "timeout": used with look into future, 30 default, 60 at most
|
||||
- "lastKnownMessageId", int, use one from X-Chat-Last-Given
|
||||
*/
|
||||
@GET
|
||||
Observable<Response<ChatOverall>> pullChatMessages(@Header("Authorization") String authorization, @Url String url,
|
||||
@QueryMap Map<String, Integer> fields);
|
||||
@ -286,7 +286,7 @@ public interface NcApi {
|
||||
|
||||
@GET
|
||||
Observable<MentionOverall> getMentionAutocompleteSuggestions(@Header("Authorization") String authorization,
|
||||
@Url String url, @Query("search") String query,
|
||||
@Nullable @Query("limit") Integer limit);
|
||||
@Url String url, @Query("search") String query,
|
||||
@Nullable @Query("limit") Integer limit);
|
||||
|
||||
}
|
||||
|
@ -118,6 +118,7 @@ public class ChatController extends BaseController implements MessagesListAdapte
|
||||
private Menu globalMenu;
|
||||
|
||||
private Autocomplete mentionAutocomplete;
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- format mentions
|
||||
|
@ -99,15 +99,15 @@ public class ServerSelectionController extends BaseController {
|
||||
@OnClick(R.id.cert_text_view)
|
||||
public void onCertClick() {
|
||||
if (getActivity() != null) {
|
||||
KeyChain.choosePrivateKeyAlias(getActivity(), alias -> {
|
||||
if (alias != null) {
|
||||
appPreferences.setTemporaryClientCertAlias(alias);
|
||||
} else {
|
||||
appPreferences.removeTemporaryClientCertAlias();
|
||||
}
|
||||
KeyChain.choosePrivateKeyAlias(getActivity(), alias -> {
|
||||
if (alias != null) {
|
||||
appPreferences.setTemporaryClientCertAlias(alias);
|
||||
} else {
|
||||
appPreferences.removeTemporaryClientCertAlias();
|
||||
}
|
||||
|
||||
setCertTextView();
|
||||
}, new String[]{"RSA", "EC"}, null, null, -1, null);
|
||||
setCertTextView();
|
||||
}, new String[]{"RSA", "EC"}, null, null, -1, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,12 +43,10 @@ import autodagger.AutoInjector;
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public abstract class BaseController extends RefWatchingController {
|
||||
|
||||
private static final String TAG = "BaseController";
|
||||
@Inject
|
||||
AppPreferences appPreferences;
|
||||
|
||||
|
||||
private static final String TAG = "BaseController";
|
||||
|
||||
protected BaseController() {
|
||||
cleanTempCertPreference();
|
||||
}
|
||||
@ -72,6 +70,7 @@ public abstract class BaseController extends RefWatchingController {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
@ -55,9 +55,10 @@ import butterknife.BindView;
|
||||
* The backstack of each {@link MenuItem} is switched out, in order to maintain a separate backstack
|
||||
* for each {@link MenuItem} - even though that is against the Google Design Guidelines:
|
||||
*
|
||||
* @author chris6647@gmail.com
|
||||
* @see <a
|
||||
* href="https://material.io/guidelines/components/bottom-navigation.html#bottom-navigation-behavior">Material
|
||||
* Design Guidelines</a>
|
||||
* href="https://material.io/guidelines/components/bottom-navigation.html#bottom-navigation-behavior">Material
|
||||
* Design Guidelines</a>
|
||||
*
|
||||
* Internally works similarly to {@link com.bluelinelabs.conductor.support.RouterPagerAdapter},
|
||||
* in the sense that it keeps track of the currently active {@link MenuItem} and the paired
|
||||
@ -66,18 +67,14 @@ import butterknife.BindView;
|
||||
* of the Child {@link Router}, and cache it, so we have it available when we navigate to
|
||||
* another {@link MenuItem} and can then restore the correct Child {@link Router}
|
||||
* (and thus the entire backstack)
|
||||
*
|
||||
* @author chris6647@gmail.com
|
||||
*/
|
||||
public abstract class BottomNavigationController extends BaseController {
|
||||
|
||||
public static final String TAG = "BottomNavigationContr";
|
||||
|
||||
public static final int INVALID_INT = -1;
|
||||
private static final String KEY_MENU_RESOURCE = "key_menu_resource";
|
||||
private static final String KEY_STATE_ROUTER_BUNDLES = "key_state_router_bundles";
|
||||
private static final String KEY_STATE_CURRENTLY_SELECTED_ID = "key_state_currently_selected_id";
|
||||
public static final int INVALID_INT = -1;
|
||||
|
||||
@BindView(R.id.navigation)
|
||||
BottomNavigationView bottomNavigationView;
|
||||
|
||||
|
@ -24,7 +24,6 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -160,21 +160,21 @@ public class AccountRemovalJob extends Job {
|
||||
userEntity.getBaseUrl())
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.subscribe(new CompletableObserver() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Something went wrong while removing job at parsing PushConfigurationState");
|
||||
@ -182,21 +182,21 @@ public class AccountRemovalJob extends Job {
|
||||
userEntity.getBaseUrl())
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.subscribe(new CompletableObserver() {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return Result.SUCCESS;
|
||||
|
@ -37,6 +37,25 @@ import lombok.Data;
|
||||
@JsonObject
|
||||
public class ChatMessage implements IMessage {
|
||||
String baseUrl;
|
||||
@JsonField(name = "id")
|
||||
int jsonMessageId;
|
||||
@JsonField(name = "token")
|
||||
String token;
|
||||
// guests or users
|
||||
@JsonField(name = "actorType")
|
||||
String actorType;
|
||||
@JsonField(name = "actorId")
|
||||
String actorId;
|
||||
// send when crafting a message
|
||||
@JsonField(name = "actorDisplayName")
|
||||
String actorDisplayName;
|
||||
@JsonField(name = "timestamp")
|
||||
long timestamp;
|
||||
// send when crafting a message, max 1000 lines
|
||||
@JsonField(name = "message")
|
||||
String message;
|
||||
@JsonField(name = "messageParameters")
|
||||
HashMap<String, HashMap<String, String>> messageParameters;
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
@ -46,33 +65,6 @@ public class ChatMessage implements IMessage {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
@JsonField(name = "id")
|
||||
int jsonMessageId;
|
||||
|
||||
@JsonField(name = "token")
|
||||
String token;
|
||||
|
||||
// guests or users
|
||||
@JsonField(name = "actorType")
|
||||
String actorType;
|
||||
|
||||
@JsonField(name = "actorId")
|
||||
String actorId;
|
||||
|
||||
// send when crafting a message
|
||||
@JsonField(name = "actorDisplayName")
|
||||
String actorDisplayName;
|
||||
|
||||
@JsonField(name = "timestamp")
|
||||
long timestamp;
|
||||
|
||||
// send when crafting a message, max 1000 lines
|
||||
@JsonField(name = "message")
|
||||
String message;
|
||||
|
||||
@JsonField(name = "messageParameters")
|
||||
HashMap<String, HashMap<String, String>> messageParameters;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return Integer.toString(jsonMessageId);
|
||||
|
@ -35,6 +35,7 @@ import lombok.Data;
|
||||
@Parcel
|
||||
@JsonObject
|
||||
public class ChatOCS extends GenericOCS {
|
||||
@Nullable @JsonField(name = "data")
|
||||
@Nullable
|
||||
@JsonField(name = "data")
|
||||
List<ChatMessage> data;
|
||||
}
|
||||
|
@ -25,15 +25,19 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.v7.widget.AppCompatDrawableManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DisplayUtils {
|
||||
|
||||
@ -75,4 +79,28 @@ public class DisplayUtils {
|
||||
drawable.setTint(color);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
|
||||
public static String searchAndColor(String text, String searchText, @ColorInt int color) {
|
||||
|
||||
if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchText)) {
|
||||
return text;
|
||||
}
|
||||
|
||||
Matcher m = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
|
||||
.matcher(text);
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
while (m.find()) {
|
||||
String replacement = m.group().replace(
|
||||
m.group(),
|
||||
"<font color='" + color + "'><b>" + m.group() + "</b></font>"
|
||||
);
|
||||
m.appendReplacement(sb, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
m.appendTail(sb);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<color name="colorPrimary">#0082C9</color>
|
||||
<color name="colorPrimaryDark">#006AA3</color>
|
||||
<color name="colorAccent">#007CC2</color>
|
||||
<color name="colorAccentComplement">#C34700</color>
|
||||
|
||||
<color name="nc_darkRed">#D32F2F</color>
|
||||
<color name="nc_darkGreen">#006400</color>
|
||||
|
Loading…
Reference in New Issue
Block a user