From 059aaedca8294641909a454fd6511d94f6aa7d3b Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 25 Oct 2019 13:22:14 +0200 Subject: [PATCH] New work Signed-off-by: Mario Danic --- app/build.gradle | 26 +- .../talk/adapters/items/MenuItem.java | 2 +- .../MagicOutcomingTextMessageViewHolder.java | 6 +- .../MagicPreviewMessageViewHolder.java | 8 +- .../MagicSystemMessageViewHolder.java | 4 +- .../MentionAutocompleteCallback.java | 2 +- .../talk/controllers/CallController.java | 2 +- .../CallNotificationController.java | 6 +- .../talk/controllers/ChatController.kt | 11 +- .../bottomsheet/OperationsMenuController.java | 6 +- .../talk/events/UserMentionClickEvent.java | 4 + .../talk/jobs/NotificationWorker.java | 2 +- .../json/userprofile/UserProfileData.java | 2 +- .../talk/newarch/di/module/NetworkModule.kt | 2 +- .../ConversationsListView.kt | 4 +- .../nextcloud/talk/newarch/utils/Images.kt | 4 +- .../talk/newarch/utils/NetworkUtils.kt | 12 +- .../nextcloud/talk/utils/DisplayUtils.java | 436 ------------------ .../com/nextcloud/talk/utils/DisplayUtils.kt | 413 +++++++++++++++++ .../res/drawable/asl_password_visibility.xml | 9 +- .../main/res/drawable/avd_hide_password.xml | 56 +-- .../main/res/drawable/avd_show_password.xml | 61 +-- app/src/main/res/values/strings.xml | 4 + 23 files changed, 540 insertions(+), 542 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java create mode 100644 app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt diff --git a/app/build.gradle b/app/build.gradle index 196bbd713..342609a6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,6 +30,12 @@ if (taskRequest.contains("Gplay") || taskRequest.contains("findbugs") || apply from: 'gplay.gradle' } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + jvmTarget = "1.8" + } +} + android { compileSdkVersion 29 buildToolsVersion '29.0.2' @@ -148,7 +154,7 @@ android { ext { work_version = "2.3.0-alpha02" koin_version = "2.1.0-alpha-1" - lifecycle_version = "2.2.0-beta01" + lifecycle_version = "2.2.0-rc01" coil_version = "0.8.0" room_version = "2.2.0" } @@ -218,8 +224,8 @@ dependencies { androidTestImplementation "androidx.work:work-testing:$work_version" implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'com.google.android.material:material:1.1.0-beta01' - implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2' + implementation 'com.google.android.material:material:1.2.0-alpha01' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3' implementation 'com.github.vanniktech:Emoji:0.6.0' implementation group: 'androidx.emoji', name: 'emoji-bundled', version: '1.0.0' implementation 'org.michaelevans.colorart:library:0.0.3' @@ -229,7 +235,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' - implementation 'androidx.biometric:biometric:1.0.0-rc01' + implementation 'androidx.biometric:biometric:1.0.0-rc02' implementation "androidx.lifecycle:lifecycle-extensions:2.1.0" implementation 'androidx.multidex:multidex:2.0.1' @@ -244,9 +250,9 @@ dependencies { implementation 'com.bluelinelabs:conductor-autodispose:3.0.0-rc2' implementation "com.github.miquelbeltran:conductor-viewmodel:1.0.3" - implementation 'com.squareup.okhttp3:okhttp:3.14.4' - implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.14.4' - implementation 'com.squareup.okhttp3:logging-interceptor:3.14.4' + implementation 'com.squareup.okhttp3:okhttp:4.2.2' + implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.2.2' + implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2' implementation 'com.squareup.retrofit2:retrofit:2.6.2' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2' @@ -327,8 +333,8 @@ dependencies { androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0-alpha02', { exclude group: 'com.android.support', module: 'support-annotations' }) - findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' - findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.6' + findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.0' + findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.7' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0' implementation 'com.github.Kennyc1012:BottomSheet:2.4.1' -} +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java index 9ddc09c50..59f3b537a 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MenuItem.java @@ -48,7 +48,7 @@ public class MenuItem extends AbstractFlexibleItem this.title = title; this.tag = tag; this.icon = icon; - padding = (int) DisplayUtils.convertDpToPixel(16, + padding = (int) DisplayUtils.INSTANCE.convertDpToPixel(16, NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext()); } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java index 971bb2a2f..8a6f6efbc 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java @@ -98,7 +98,7 @@ public class MagicOutcomingTextMessageViewHolder if (individualHashMap.get("type").equals("user") || individualHashMap.get("type") .equals("guest") || individualHashMap.get("type").equals("call")) { messageString = - DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), + DisplayUtils.INSTANCE.searchAndReplaceWithMentionSpan(messageText.getContext(), messageString, individualHashMap.get("id"), individualHashMap.get("name"), @@ -124,14 +124,14 @@ public class MagicOutcomingTextMessageViewHolder Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources(); if (message.isGrouped) { Drawable bubbleDrawable = - DisplayUtils.getMessageSelector( + DisplayUtils.INSTANCE.getMessageSelector( resources.getColor(R.color.bg_message_list_outcoming_bubble), resources.getColor(R.color.transparent), resources.getColor(R.color.bg_message_list_outcoming_bubble), R.drawable.shape_grouped_outcoming_message); ViewCompat.setBackground(bubble, bubbleDrawable); } else { - Drawable bubbleDrawable = DisplayUtils.getMessageSelector( + Drawable bubbleDrawable = DisplayUtils.INSTANCE.getMessageSelector( resources.getColor(R.color.bg_message_list_outcoming_bubble), resources.getColor(R.color.transparent), resources.getColor(R.color.bg_message_list_outcoming_bubble), diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index 15db7da4f..bde4ec75c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -96,7 +96,7 @@ public class MagicPreviewMessageViewHolder LayerDrawable layerDrawable = new LayerDrawable(layers); userAvatar.getHierarchy() - .setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)); + .setPlaceholderImage(DisplayUtils.INSTANCE.getRoundedDrawable(layerDrawable)); } } } @@ -104,7 +104,7 @@ public class MagicPreviewMessageViewHolder if (message.getMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) { // it's a preview for a Nextcloud share messageText.setText(message.getSelectedIndividualHashMap().get("name")); - DisplayUtils.setClickableString(message.getSelectedIndividualHashMap().get("name"), + DisplayUtils.INSTANCE.setClickableString(message.getSelectedIndividualHashMap().get("name"), message.getSelectedIndividualHashMap().get("link"), messageText); if (message.getSelectedIndividualHashMap().containsKey("mimetype")) { image.getHierarchy() @@ -145,10 +145,10 @@ public class MagicPreviewMessageViewHolder }); } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { messageText.setText("GIPHY"); - DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText); + DisplayUtils.INSTANCE.setClickableString("GIPHY", "https://giphy.com", messageText); } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE) { messageText.setText("Tenor"); - DisplayUtils.setClickableString("Tenor", "https://tenor.com", messageText); + DisplayUtils.INSTANCE.setClickableString("Tenor", "https://tenor.com", messageText); } else { if (message.getMessageType().equals(ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { image.setOnClickListener(v -> { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java index fc09ad2f3..95ee871d2 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java @@ -66,7 +66,7 @@ public class MagicSystemMessageViewHolder pressedColor = normalColor; mentionColor = resources.getColor(R.color.nc_author_text); - Drawable bubbleDrawable = DisplayUtils.getMessageSelector(normalColor, + Drawable bubbleDrawable = DisplayUtils.INSTANCE.getMessageSelector(normalColor, resources.getColor(R.color.transparent), pressedColor, R.drawable.shape_grouped_incoming_message); ViewCompat.setBackground(bubble, bubbleDrawable); @@ -80,7 +80,7 @@ public class MagicSystemMessageViewHolder || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call"))) { messageString = - DisplayUtils.searchAndColor(messageString, "@" + individualHashMap.get("name"), + DisplayUtils.INSTANCE.searchAndColor(messageString, "@" + individualHashMap.get("name"), mentionColor); } } diff --git a/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java b/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java index 23ca7855c..e58780364 100644 --- a/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java +++ b/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java @@ -62,7 +62,7 @@ public class MentionAutocompleteCallback implements AutocompleteCallback - val draweeController = Fresco.newDraweeControllerBuilder() + /*val draweeController = Fresco.newDraweeControllerBuilder() .setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser)) .setControllerListener(DisplayUtils.getImageControllerListener(imageView)) .setOldController(imageView.controller) .setAutoPlayAnimations(true) .build() - imageView.controller = draweeController + imageView.controller = draweeController*/ }) } else { messagesListView?.visibility = View.VISIBLE @@ -1136,9 +1136,8 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter isFromTheFuture: Boolean, timeout: Int ) { - val xChatLastGivenHeader: String? = response.headers() - .get("X-Chat-Last-Given") - if (response.headers().size() > 0 && !TextUtils.isEmpty(xChatLastGivenHeader)) { + val xChatLastGivenHeader: String? = response.headers().get("X-Chat-Last-Given") + if (response.headers().size > 0 && !TextUtils.isEmpty(xChatLastGivenHeader)) { val header = Integer.parseInt(xChatLastGivenHeader!!) if (header > 0) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index 0a23d3c04..0554c5948 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -417,10 +417,10 @@ public class OperationsMenuController extends BaseController { progressBar.setVisibility(View.GONE); if (everythingOK) { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable + resultImageView.setImageDrawable(DisplayUtils.INSTANCE.getTintedDrawable(getResources(), R.drawable .ic_check_circle_black_24dp, R.color.nc_darkGreen)); } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable + resultImageView.setImageDrawable(DisplayUtils.INSTANCE.getTintedDrawable(getResources(), R.drawable .ic_cancel_black_24dp, R.color.nc_darkRed)); } @@ -448,7 +448,7 @@ public class OperationsMenuController extends BaseController { if (everythingOK) { eventBus.post(new BottomSheetLockEvent(true, 2500, true, true)); } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable + resultImageView.setImageDrawable(DisplayUtils.INSTANCE.getTintedDrawable(getResources(), R.drawable .ic_cancel_black_24dp, R.color.nc_darkRed)); okButton.setOnClickListener( v -> eventBus.post(new BottomSheetLockEvent(true, 0, operationCode != 99 diff --git a/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java b/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java index 5ab37f8b4..ec2913ab6 100644 --- a/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java +++ b/app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java @@ -25,4 +25,8 @@ import lombok.Data; @Data public class UserMentionClickEvent { public final String userId; + + public UserMentionClickEvent(String userId) { + this.userId = userId; + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java index 3eeee34e8..ac01abfb7 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java @@ -438,7 +438,7 @@ public class NotificationWorker extends Worker { } ImageRequest imageRequest = - DisplayUtils.getImageRequestForUrl(avatarUrl, null); + DisplayUtils.INSTANCE.getImageRequestForUrl(avatarUrl, null); ImagePipeline imagePipeline = Fresco.getImagePipeline(); DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context); diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java index 924c271fb..c4959903f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java @@ -27,7 +27,7 @@ import org.parceler.Parcel; @Parcel @Data -@JsonObject() +@JsonObject public class UserProfileData { @JsonField(name = "display-name") public String displayName; diff --git a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt index d1196cbce..e7824751d 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt @@ -117,7 +117,7 @@ fun createOkHttpClient( // Trust own CA and all self-signed certs httpClient.sslSocketFactory(sslSocketFactoryCompat, magicTrustManager) httpClient.retryOnConnectionFailure(true) - httpClient.hostnameVerifier(magicTrustManager.getHostnameVerifier(OkHostnameVerifier.INSTANCE)) + httpClient.hostnameVerifier(magicTrustManager.getHostnameVerifier(OkHostnameVerifier)) httpClient.dispatcher(dispatcher) if (Proxy.NO_PROXY != proxy) { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt index dd64cbdf1..3b6d7929e 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt @@ -122,7 +122,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, ?.let { DisplayUtils.convertDpToPixel( it, - activity + activity!! ) .toInt() } @@ -419,7 +419,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, negativeButton(R.string.nc_cancel) icon( drawable = DisplayUtils.getTintedDrawable( - resources, drawable + resources!!, drawable .ic_delete_grey600_24dp, R.color.nc_darkRed ) ) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt index 9b6994bac..bec9e2f72 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt @@ -34,7 +34,7 @@ class Images { context: Context, url: String, userEntity: - UserEntity, + UserEntity?, target: Target?, lifecycleOwner: LifecycleOwner?, vararg transformations: Transformation @@ -50,7 +50,7 @@ class Images { target(target) } - if (url.startsWith(userEntity.baseUrl) && url.contains( + if (userEntity != null && url.startsWith(userEntity.baseUrl) && url.contains( "index.php/core/preview?fileId=" ) ) { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/NetworkUtils.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/NetworkUtils.kt index 7c658071e..a3ab6a1eb 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/utils/NetworkUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/NetworkUtils.kt @@ -45,14 +45,14 @@ class NetworkUtils { .header("User-Agent", ApiUtils.getUserAgent()) .header("Accept", "application/json") .header("OCS-APIRequest", "true") - .method(original.method(), original.body()) + .method(original.method, original.body) .build() val response = chain.proceed(request) - if (request.url().encodedPath().contains("/avatar/")) { + if (request.url.encodedPath.contains("/avatar/")) { AvatarStatusCodeHolder.getInstance() - .statusCode = response.code() + .statusCode = response.code } return response @@ -68,7 +68,7 @@ class NetworkUtils { route: Route?, response: Response ): Request? { - if (response.request().header(authenticatorType) != null) { + if (response.request.header(authenticatorType) != null) { return null } @@ -77,13 +77,13 @@ class NetworkUtils { var attemptsCount = 0 - while ({ countedResponse = countedResponse?.priorResponse(); countedResponse }() != null) { + while ({ countedResponse = countedResponse?.priorResponse; countedResponse }() != null) { attemptsCount++ if (attemptsCount == 3) { return null } } - return response.request() + return response.request .newBuilder() .header(authenticatorType, credentials) .build() diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java deleted file mode 100644 index db25c811f..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * 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 . - */ - -package com.nextcloud.talk.utils; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Typeface; -import android.graphics.drawable.Animatable; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.VectorDrawable; -import android.net.Uri; -import android.os.Build; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.method.LinkMovementMethod; -import android.text.style.AbsoluteSizeSpan; -import android.text.style.ClickableSpan; -import android.text.style.ForegroundColorSpan; -import android.text.style.StyleSpan; -import android.util.Log; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.TextView; -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.XmlRes; -import androidx.appcompat.widget.AppCompatDrawableManager; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.emoji.text.EmojiCompat; -import com.facebook.common.executors.UiThreadImmediateExecutorService; -import com.facebook.common.references.CloseableReference; -import com.facebook.datasource.DataSource; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.controller.ControllerListener; -import com.facebook.drawee.view.SimpleDraweeView; -import com.facebook.imagepipeline.common.RotationOptions; -import com.facebook.imagepipeline.core.ImagePipeline; -import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; -import com.facebook.imagepipeline.image.CloseableImage; -import com.facebook.imagepipeline.image.ImageInfo; -import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor; -import com.facebook.imagepipeline.postprocessors.RoundPostprocessor; -import com.facebook.imagepipeline.request.ImageRequest; -import com.facebook.imagepipeline.request.ImageRequestBuilder; -import com.facebook.widget.text.span.BetterImageSpan; -import com.google.android.material.chip.ChipDrawable; -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.utils.preferences.AppPreferences; -import com.nextcloud.talk.utils.text.Spans; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.greenrobot.eventbus.EventBus; - -public class DisplayUtils { - - private static final String TAG = "DisplayUtils"; - - public static void setClickableString(String string, String url, TextView textView) { - SpannableString spannableString = new SpannableString(string); - spannableString.setSpan(new ClickableSpan() { - @Override - public void onClick(@Nonnull View widget) { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - NextcloudTalkApplication.Companion.getSharedApplication() - .getApplicationContext() - .startActivity(browserIntent); - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - } - }, 0, string.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - textView.setText(spannableString); - textView.setMovementMethod(LinkMovementMethod.getInstance()); - } - - private static void updateViewSize(@Nullable ImageInfo imageInfo, SimpleDraweeView draweeView) { - if (imageInfo != null) { - int maxSize = draweeView.getContext() - .getResources() - .getDimensionPixelSize(R.dimen.maximum_file_preview_size); - draweeView.getLayoutParams().width = - imageInfo.getWidth() > maxSize ? maxSize : imageInfo.getWidth(); - draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; - draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); - draweeView.requestLayout(); - } - } - - public static Drawable getRoundedDrawable(Drawable drawable) { - Bitmap bitmap = getBitmap(drawable); - new RoundAsCirclePostprocessor(true).process(bitmap); - return new BitmapDrawable(bitmap); - } - - public static Bitmap getRoundedBitmapFromVectorDrawableResource(Resources resources, - int resource) { - VectorDrawable vectorDrawable = (VectorDrawable) resources.getDrawable(resource); - Bitmap bitmap = getBitmap(vectorDrawable); - new RoundPostprocessor(true).process(bitmap); - return bitmap; - } - - public static Drawable getRoundedBitmapDrawableFromVectorDrawableResource(Resources resources, - int resource) { - return new BitmapDrawable(getRoundedBitmapFromVectorDrawableResource(resources, resource)); - } - - private static Bitmap getBitmap(Drawable drawable) { - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } - - public static ImageRequest getImageRequestForUrl(String url, @Nullable UserEntity userEntity) { - Map headers = new HashMap<>(); - if (userEntity != null && url.startsWith(userEntity.getBaseUrl()) && url.contains( - "index.php/core/preview?fileId=")) { - headers.put("Authorization", - ApiUtils.getCredentials(userEntity.getUsername(), userEntity.getToken())); - } - - return ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) - .setProgressiveRenderingEnabled(true) - .setRotationOptions(RotationOptions.autoRotate()) - .disableDiskCache() - .setHeaders(headers) - .build(); - } - - public static ControllerListener getImageControllerListener(SimpleDraweeView draweeView) { - return new ControllerListener() { - @Override - public void onSubmit(String id, Object callerContext) { - - } - - @Override - public void onFinalImageSet(String id, @javax.annotation.Nullable Object imageInfo, - @javax.annotation.Nullable Animatable animatable) { - updateViewSize((ImageInfo) imageInfo, draweeView); - } - - @Override - public void onIntermediateImageSet(String id, @javax.annotation.Nullable Object imageInfo) { - updateViewSize((ImageInfo) imageInfo, draweeView); - } - - @Override - public void onIntermediateImageFailed(String id, Throwable throwable) { - - } - - @Override - public void onFailure(String id, Throwable throwable) { - - } - - @Override - public void onRelease(String id) { - - } - }; - } - - public static float convertDpToPixel(float dp, Context context) { - return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, - context.getResources().getDisplayMetrics()) + 0.5f); - } - - // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected - public static void useCompatVectorIfNeeded() { - if (Build.VERSION.SDK_INT < 23) { - try { - @SuppressLint("RestrictedApi") AppCompatDrawableManager drawableManager = - AppCompatDrawableManager.get(); - Class inflateDelegateClass = - Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate"); - Class vdcInflateDelegateClass = - Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate"); - - Constructor constructor = vdcInflateDelegateClass.getDeclaredConstructor(); - constructor.setAccessible(true); - Object vdcInflateDelegate = constructor.newInstance(); - - Class[] args = { String.class, inflateDelegateClass }; - Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args); - addDelegate.setAccessible(true); - addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate); - } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | - InvocationTargetException | IllegalAccessException e) { - Log.e(TAG, "Failed to use reflection to enable proper vector scaling"); - } - } - } - - public static Drawable getTintedDrawable(Resources res, @DrawableRes int drawableResId, - @ColorRes int colorResId) { - Drawable drawable = res.getDrawable(drawableResId); - Drawable mutableDrawable = drawable.mutate(); - int color = res.getColor(colorResId); - mutableDrawable.setTint(color); - return mutableDrawable; - } - - public static Drawable getDrawableForMentionChipSpan(Context context, String id, - CharSequence label, - UserEntity conversationUser, String type, - @XmlRes int chipResource, - @Nullable EditText emojiEditText) { - ChipDrawable chip = ChipDrawable.createFromResource(context, chipResource); - chip.setText(EmojiCompat.get().process(label)); - chip.setEllipsize(TextUtils.TruncateAt.MIDDLE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - Configuration config = context.getResources().getConfiguration(); - chip.setLayoutDirection(config.getLayoutDirection()); - } - - int drawable; - - boolean isCall = "call".equals(type) || "calls".equals(type); - - if (!isCall) { - if (chipResource == R.xml.chip_you) { - drawable = R.drawable.mention_chip; - } else { - drawable = R.drawable.accent_circle; - } - - chip.setChipIcon(context.getDrawable(drawable)); - } else { - chip.setChipIcon( - getRoundedDrawable(context.getDrawable(R.drawable.ic_people_group_white_24px))); - } - - chip.setBounds(0, 0, chip.getIntrinsicWidth(), chip.getIntrinsicHeight()); - - if (!isCall) { - String url = ApiUtils.getUrlForAvatarWithName(conversationUser.getBaseUrl(), id, - R.dimen.avatar_size_big); - if ("guests".equals(type) || "guest".equals(type)) { - url = ApiUtils.getUrlForAvatarWithNameForGuests(conversationUser.getBaseUrl(), - String.valueOf(label), R.dimen.avatar_size_big); - } - - ImageRequest imageRequest = getImageRequestForUrl(url, null); - ImagePipeline imagePipeline = Fresco.getImagePipeline(); - DataSource> dataSource = - imagePipeline.fetchDecodedImage(imageRequest, context); - - dataSource.subscribe( - new BaseBitmapDataSubscriber() { - @Override - protected void onNewResultImpl(Bitmap bitmap) { - if (bitmap != null) { - chip.setChipIcon(getRoundedDrawable(new BitmapDrawable(bitmap))); - - // A hack to refresh the chip icon - if (emojiEditText != null) { - emojiEditText.post(() -> emojiEditText.setTextKeepState(emojiEditText.getText(), - TextView.BufferType.SPANNABLE)); - } - } - } - - @Override - protected void onFailureImpl( - DataSource> dataSource) { - } - }, - UiThreadImmediateExecutorService.getInstance()); - } - - return chip; - } - - public static Spannable searchAndReplaceWithMentionSpan(Context context, Spannable text, - String id, String label, String type, - UserEntity conversationUser, - @XmlRes int chipXmlRes) { - - Spannable spannableString = new SpannableString(text); - String stringText = text.toString(); - - Matcher m = Pattern.compile("@" + label, - Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.MULTILINE) - .matcher(spannableString); - - ClickableSpan clickableSpan = new ClickableSpan() { - @Override - public void onClick(@NonNull View widget) { - EventBus.getDefault().post(new UserMentionClickEvent(id)); - } - }; - - int lastStartIndex = -1; - Spans.MentionChipSpan mentionChipSpan; - while (m.find()) { - int start = stringText.indexOf(m.group(), lastStartIndex); - int end = start + m.group().length(); - lastStartIndex = end; - mentionChipSpan = - new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context, - id, label, conversationUser, type, chipXmlRes, null), - BetterImageSpan.ALIGN_CENTER, id, - label); - spannableString.setSpan(mentionChipSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - if ("user".equals(type) && !conversationUser.getUserId().equals(id)) { - spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - - return spannableString; - } - - public static Spannable searchAndColor(Spannable text, String searchText, @ColorInt int color) { - - Spannable spannableString = new SpannableString(text); - String stringText = text.toString(); - if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchText)) { - return spannableString; - } - - Matcher m = Pattern.compile(searchText, - Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.MULTILINE) - .matcher(spannableString); - - int textSize = NextcloudTalkApplication.Companion.getSharedApplication() - .getResources() - .getDimensionPixelSize(R.dimen - .chat_text_size); - - int lastStartIndex = -1; - while (m.find()) { - int start = stringText.indexOf(m.group(), lastStartIndex); - int end = start + m.group().length(); - lastStartIndex = end; - spannableString.setSpan(new ForegroundColorSpan(color), start, end, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - spannableString.setSpan(new AbsoluteSizeSpan(textSize), start, end, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - return spannableString; - } - - public static Drawable getMessageSelector(@ColorInt int normalColor, @ColorInt int selectedColor, - @ColorInt int pressedColor, @DrawableRes int shape) { - - Drawable vectorDrawable = - ContextCompat.getDrawable(NextcloudTalkApplication.Companion.getSharedApplication() - .getApplicationContext(), - shape); - Drawable drawable = DrawableCompat.wrap(vectorDrawable).mutate(); - DrawableCompat.setTintList( - drawable, - new ColorStateList( - new int[][] { - new int[] { android.R.attr.state_selected }, - new int[] { android.R.attr.state_pressed }, - new int[] { -android.R.attr.state_pressed, -android.R.attr.state_selected } - }, - new int[] { selectedColor, pressedColor, normalColor } - )); - return drawable; - } - - public static boolean isDarkModeActive(Context context, AppPreferences prefs) { - if (prefs.getTheme().equals("night_yes")) { - return true; - } - - int currentNightMode = - context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - switch (currentNightMode) { - case Configuration.UI_MODE_NIGHT_NO: - return false; - case Configuration.UI_MODE_NIGHT_YES: - return true; - default: - return false; - } - } -} diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt new file mode 100644 index 000000000..498237bac --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt @@ -0,0 +1,413 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * 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 . + */ + +package com.nextcloud.talk.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.content.res.ColorStateList +import android.content.res.Configuration +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Typeface +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.VectorDrawable +import android.net.Uri +import android.os.Build +import android.text.Spannable +import android.text.SpannableString +import android.text.Spanned +import android.text.TextPaint +import android.text.TextUtils +import android.text.method.LinkMovementMethod +import android.text.style.AbsoluteSizeSpan +import android.text.style.ClickableSpan +import android.text.style.ForegroundColorSpan +import android.text.style.StyleSpan +import android.util.Log +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.TextView +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.annotation.XmlRes +import androidx.appcompat.widget.AppCompatDrawableManager +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.DrawableCompat +import androidx.emoji.text.EmojiCompat +import coil.Coil +import coil.ImageLoader +import coil.api.load +import coil.target.Target +import coil.transform.CircleCropTransformation +import com.facebook.common.executors.UiThreadImmediateExecutorService +import com.facebook.common.references.CloseableReference +import com.facebook.datasource.DataSource +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.view.SimpleDraweeView +import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber +import com.facebook.imagepipeline.image.CloseableImage +import com.facebook.imagepipeline.image.ImageInfo +import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor +import com.facebook.imagepipeline.postprocessors.RoundPostprocessor +import com.facebook.widget.text.span.BetterImageSpan +import com.google.android.material.chip.ChipDrawable +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.utils.Images +import com.nextcloud.talk.utils.preferences.AppPreferences +import com.nextcloud.talk.utils.text.Spans +import org.greenrobot.eventbus.EventBus +import java.lang.reflect.InvocationTargetException +import java.util.regex.Pattern +import org.koin.android.ext.android.inject + +object DisplayUtils { + + private val TAG = "DisplayUtils" + + fun setClickableString( + string: String, + url: String, + textView: TextView + ) { + val spannableString = SpannableString(string) + spannableString.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + NextcloudTalkApplication.sharedApplication!! + .applicationContext + .startActivity(browserIntent) + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.isUnderlineText = false + } + }, 0, string.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) + textView.text = spannableString + textView.movementMethod = LinkMovementMethod.getInstance() + } + + private fun updateViewSize( + imageInfo: ImageInfo?, + draweeView: SimpleDraweeView + ) { + if (imageInfo != null) { + val maxSize = draweeView.context + .resources + .getDimensionPixelSize(R.dimen.maximum_file_preview_size) + draweeView.layoutParams.width = if (imageInfo.width > maxSize) maxSize else imageInfo.width + draweeView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT + draweeView.aspectRatio = imageInfo.width.toFloat() / imageInfo.height + draweeView.requestLayout() + } + } + + fun getRoundedDrawable(drawable: Drawable?): Drawable { + val bitmap = getBitmap(drawable!!) + RoundAsCirclePostprocessor(true).process(bitmap) + return BitmapDrawable(bitmap) + } + + fun getRoundedBitmapFromVectorDrawableResource( + resources: Resources, + resource: Int + ): Bitmap { + val vectorDrawable = resources.getDrawable(resource) as VectorDrawable + val bitmap = getBitmap(vectorDrawable) + RoundPostprocessor(true).process(bitmap) + return bitmap + } + + private fun getBitmap(drawable: Drawable): Bitmap { + val bitmap = Bitmap.createBitmap( + drawable.intrinsicWidth, + drawable.intrinsicHeight, Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(bitmap) + drawable.setBounds(0, 0, canvas.width, canvas.height) + drawable.draw(canvas) + return bitmap + } + + fun convertDpToPixel( + dp: Float, + context: Context + ): Float { + return Math.round( + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, dp, + context.resources.displayMetrics + ) + 0.5f + ) + .toFloat() + } + + // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected + fun useCompatVectorIfNeeded() { + if (Build.VERSION.SDK_INT < 23) { + try { + @SuppressLint("RestrictedApi") val drawableManager = AppCompatDrawableManager.get() + val inflateDelegateClass = + Class.forName("android.support.v7.widget.AppCompatDrawableManager\$InflateDelegate") + val vdcInflateDelegateClass = + Class.forName("android.support.v7.widget.AppCompatDrawableManager\$VdcInflateDelegate") + + val constructor = vdcInflateDelegateClass.getDeclaredConstructor() + constructor.isAccessible = true + val vdcInflateDelegate = constructor.newInstance() + + val args = arrayOf(String::class.java, inflateDelegateClass) + val addDelegate = + AppCompatDrawableManager::class.java.getDeclaredMethod("addDelegate", *args) + addDelegate.isAccessible = true + addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate) + } catch (e: ClassNotFoundException) { + Log.e(TAG, "Failed to use reflection to enable proper vector scaling") + } catch (e: NoSuchMethodException) { + Log.e(TAG, "Failed to use reflection to enable proper vector scaling") + } catch (e: InstantiationException) { + Log.e(TAG, "Failed to use reflection to enable proper vector scaling") + } catch (e: InvocationTargetException) { + Log.e(TAG, "Failed to use reflection to enable proper vector scaling") + } catch (e: IllegalAccessException) { + Log.e(TAG, "Failed to use reflection to enable proper vector scaling") + } + + } + } + + fun getTintedDrawable( + res: Resources, @DrawableRes drawableResId: Int, + @ColorRes colorResId: Int + ): Drawable { + val drawable = res.getDrawable(drawableResId) + val mutableDrawable = drawable.mutate() + val color = res.getColor(colorResId) + mutableDrawable.setTint(color) + return mutableDrawable + } + + fun getDrawableForMentionChipSpan( + context: Context, + id: String, + label: CharSequence, + conversationUser: UserEntity, + type: String, + @XmlRes chipResource: Int, + emojiEditText: EditText? + ): Drawable { + val chip = ChipDrawable.createFromResource(context, chipResource) + chip.text = EmojiCompat.get() + .process(label) + chip.ellipsize = TextUtils.TruncateAt.MIDDLE + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val config = context.resources.configuration + chip.layoutDirection = config.layoutDirection + } + + val drawable: Int + + val isCall = "call" == type || "calls" == type + + val target = object : Target { + override fun onSuccess(result: Drawable) { + super.onSuccess(result) + chip.chipIcon = result + + // A hack to refresh the chip icon + emojiEditText?.post { + emojiEditText.setTextKeepState( + emojiEditText.text, + TextView.BufferType.SPANNABLE + ) + } + } + } + + if (!isCall) { + if (chipResource == R.xml.chip_you) { + drawable = R.drawable.mention_chip + } else { + drawable = R.drawable.accent_circle + } + + Coil.load(context, drawable) { + target(target) + } + } else { + Coil.load(context, R.drawable.ic_people_group_white_24px) { + transformations(CircleCropTransformation()) + target(target) + } + } + + chip.setBounds(0, 0, chip.intrinsicWidth, chip.intrinsicHeight) + + if (!isCall) { + var url = ApiUtils.getUrlForAvatarWithName( + conversationUser.baseUrl, id, + R.dimen.avatar_size_big + ) + if ("guests" == type || "guest" == type) { + url = ApiUtils.getUrlForAvatarWithNameForGuests( + conversationUser.baseUrl, + label.toString(), R.dimen.avatar_size_big + ) + } + + val request = Images().getRequestForUrl(Coil.loader(), context, url, null, target, null, CircleCropTransformation()) + Coil.loader().load(request) + } + + return chip + } + + fun searchAndReplaceWithMentionSpan( + context: Context, + text: Spannable, + id: String, + label: String, + type: String, + conversationUser: UserEntity, + @XmlRes chipXmlRes: Int + ): Spannable { + + val spannableString = SpannableString(text) + val stringText = text.toString() + + val m = Pattern.compile( + "@$label", + Pattern.CASE_INSENSITIVE or Pattern.LITERAL or Pattern.MULTILINE + ) + .matcher(spannableString) + + val clickableSpan = object : ClickableSpan() { + override fun onClick(widget: View) { + EventBus.getDefault() + .post(UserMentionClickEvent(id)) + } + } + + var lastStartIndex = -1 + var mentionChipSpan: Spans.MentionChipSpan + while (m.find()) { + val start = stringText.indexOf(m.group(), lastStartIndex) + val end = start + m.group().length + lastStartIndex = end + mentionChipSpan = Spans.MentionChipSpan( + getDrawableForMentionChipSpan( + context, + id, label, conversationUser, type, chipXmlRes, null + ), + BetterImageSpan.ALIGN_CENTER, id, + label + ) + spannableString.setSpan(mentionChipSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + if ("user" == type && conversationUser.userId != id) { + spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) + } + } + + return spannableString + } + + fun searchAndColor( + text: Spannable, + searchText: String, @ColorInt color: Int + ): Spannable { + + val spannableString = SpannableString(text) + val stringText = text.toString() + if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchText)) { + return spannableString + } + + val m = Pattern.compile( + searchText, + Pattern.CASE_INSENSITIVE or Pattern.LITERAL or Pattern.MULTILINE + ) + .matcher(spannableString) + + val textSize = NextcloudTalkApplication.sharedApplication!! + .resources + .getDimensionPixelSize( + R.dimen + .chat_text_size + ) + + var lastStartIndex = -1 + while (m.find()) { + val start = stringText.indexOf(m.group(), lastStartIndex) + val end = start + m.group().length + lastStartIndex = end + spannableString.setSpan( + ForegroundColorSpan(color), start, end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + spannableString.setSpan( + StyleSpan(Typeface.BOLD), start, end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + spannableString.setSpan( + AbsoluteSizeSpan(textSize), start, end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + + return spannableString + } + + fun getMessageSelector( + @ColorInt normalColor: Int, @ColorInt selectedColor: Int, + @ColorInt pressedColor: Int, @DrawableRes shape: Int + ): Drawable { + + val vectorDrawable = ContextCompat.getDrawable( + NextcloudTalkApplication.sharedApplication!! + .applicationContext, + shape + ) + val drawable = DrawableCompat.wrap(vectorDrawable!!) + .mutate() + DrawableCompat.setTintList( + drawable, + ColorStateList( + arrayOf( + intArrayOf(android.R.attr.state_selected), intArrayOf(android.R.attr.state_pressed), + intArrayOf(-android.R.attr.state_pressed, -android.R.attr.state_selected) + ), + intArrayOf(selectedColor, pressedColor, normalColor) + ) + ) + return drawable + } +} diff --git a/app/src/main/res/drawable/asl_password_visibility.xml b/app/src/main/res/drawable/asl_password_visibility.xml index 692d1888d..f3870a3e9 100644 --- a/app/src/main/res/drawable/asl_password_visibility.xml +++ b/app/src/main/res/drawable/asl_password_visibility.xml @@ -15,7 +15,8 @@ limitations under the License. --> - + + android:drawable="@drawable/avd_show_password" + tools:ignore="PrivateResource" /> + android:drawable="@drawable/avd_hide_password" + tools:ignore="PrivateResource" /> diff --git a/app/src/main/res/drawable/avd_hide_password.xml b/app/src/main/res/drawable/avd_hide_password.xml index e942ce865..6741fb1b6 100644 --- a/app/src/main/res/drawable/avd_hide_password.xml +++ b/app/src/main/res/drawable/avd_hide_password.xml @@ -1,49 +1,51 @@ + xmlns:aapt="http://schemas.android.com/aapt" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="NewApi"> + android:width="24dp"> + android:strokeLineCap="square" + android:strokeWidth="1.8"/> + android:pathData="@string/path_password_eye_mask_strike_through"/> + android:name="eye" + android:pathData="@string/path_password_eye"/> @@ -56,12 +58,12 @@ + android:valueType="pathType"/> @@ -72,11 +74,11 @@ + android:valueTo="0"/> diff --git a/app/src/main/res/drawable/avd_show_password.xml b/app/src/main/res/drawable/avd_show_password.xml index e942ce865..4b233c769 100644 --- a/app/src/main/res/drawable/avd_show_password.xml +++ b/app/src/main/res/drawable/avd_show_password.xml @@ -1,49 +1,52 @@ + xmlns:aapt="http://schemas.android.com/aapt" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="NewApi"> + android:width="24dp"> + android:trimPathEnd="0"/> + android:pathData="@string/path_password_eye_mask_visible"/> + android:name="eye" + android:pathData="@string/path_password_eye"/> @@ -56,12 +59,12 @@ + android:propertyName="pathData" + android:valueFrom="@string/path_password_eye_mask_visible" + android:valueTo="@string/path_password_eye_mask_strike_through" + android:valueType="pathType"/> @@ -72,11 +75,11 @@ + android:valueFrom="0" + android:valueTo="1"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e098bc06..04d83fd3f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -320,4 +320,8 @@ Could not leave conversation You need to promote a new moderator before you can leave %1$s. + + + M3.27,4.27L19.74,20.74