mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 12:09:45 +01:00
Merge pull request #2449 from nextcloud/bugfix/2376/fresco-blocks-increase-of-minsdkversion
Set minSdkVersion to 23 (Android 6) II
This commit is contained in:
commit
13f60d1b2d
@ -42,7 +42,7 @@ android {
|
||||
namespace 'com.nextcloud.talk'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@ -245,15 +245,8 @@ dependencies {
|
||||
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||
implementation 'com.github.wooplr:Spotlight:1.3'
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
implementation('com.github.nextcloud-deps:ChatKit:0.3.0-1', {
|
||||
exclude group: 'com.facebook.fresco'
|
||||
})
|
||||
implementation 'com.github.nextcloud-deps:ChatKit:0.3.1'
|
||||
|
||||
implementation 'com.github.nextcloud-deps.fresco:fresco:v111'
|
||||
implementation 'com.github.nextcloud-deps.fresco:animated-webp:v111'
|
||||
implementation 'com.github.nextcloud-deps.fresco:webpsupport:v111'
|
||||
implementation 'com.github.nextcloud-deps.fresco:animated-gif:v111'
|
||||
implementation 'com.github.nextcloud-deps.fresco:imagepipeline-okhttp3:v111'
|
||||
implementation 'joda-time:joda-time:2.12.2'
|
||||
implementation "io.coil-kt:coil:${coilKtVersion}"
|
||||
implementation "io.coil-kt:coil-gif:${coilKtVersion}"
|
||||
|
@ -22,7 +22,6 @@ package com.nextcloud.talk.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
@ -76,9 +75,7 @@ open class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
if (appPreferences.isScreenLocked) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
SecurityUtils.createKey(appPreferences.screenLockTimeout)
|
||||
}
|
||||
SecurityUtils.createKey(appPreferences.screenLockTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import autodagger.AutoInjector;
|
||||
@ -540,20 +539,16 @@ public class CallActivity extends CallBaseActivity {
|
||||
private void updateAudioOutputButton(WebRtcAudioManager.AudioDevice activeAudioDevice) {
|
||||
switch (activeAudioDevice) {
|
||||
case BLUETOOTH:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_bluetooth_audio_24));
|
||||
binding.audioOutputButton.setImageResource ( R.drawable.ic_baseline_bluetooth_audio_24);
|
||||
break;
|
||||
case SPEAKER_PHONE:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_volume_up_white_24dp));
|
||||
binding.audioOutputButton.setImageResource(R.drawable.ic_volume_up_white_24dp);
|
||||
break;
|
||||
case EARPIECE:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_phone_in_talk_24));
|
||||
binding.audioOutputButton.setImageResource(R.drawable.ic_baseline_phone_in_talk_24);
|
||||
break;
|
||||
case WIRED_HEADSET:
|
||||
binding.audioOutputButton.getHierarchy().setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_headset_mic_24));
|
||||
binding.audioOutputButton.setImageResource(R.drawable.ic_baseline_headset_mic_24);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Icon for audio output not available");
|
||||
@ -737,10 +732,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
} else {
|
||||
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CALL)) {
|
||||
onPermissionsGranted();
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(PERMISSIONS_CALL, 100);
|
||||
} else {
|
||||
onRequestPermissionsResult(100, PERMISSIONS_CALL, new int[]{1, 1});
|
||||
requestPermissions(PERMISSIONS_CALL, 100);
|
||||
}
|
||||
}
|
||||
|
||||
@ -795,7 +788,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
onCameraClick();
|
||||
}
|
||||
} else {
|
||||
binding.cameraButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_off_white_24px);
|
||||
binding.cameraButton.setImageResource(R.drawable.ic_videocam_off_white_24px);
|
||||
binding.cameraButton.setAlpha(0.7f);
|
||||
binding.switchSelfVideoButton.setVisibility(View.GONE);
|
||||
}
|
||||
@ -806,7 +799,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
onMicrophoneClick();
|
||||
}
|
||||
} else {
|
||||
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
|
||||
binding.microphoneButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
||||
}
|
||||
|
||||
if (!isConnectionEstablished()) {
|
||||
@ -917,7 +910,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
if (!canPublishAudioStream) {
|
||||
microphoneOn = false;
|
||||
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
|
||||
binding.microphoneButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
||||
toggleMedia(false, false);
|
||||
}
|
||||
|
||||
@ -961,12 +954,12 @@ public class CallActivity extends CallBaseActivity {
|
||||
microphoneOn = !microphoneOn;
|
||||
|
||||
if (microphoneOn) {
|
||||
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_white_24px);
|
||||
binding.microphoneButton.setImageResource(R.drawable.ic_mic_white_24px);
|
||||
updatePictureInPictureActions(R.drawable.ic_mic_white_24px,
|
||||
getResources().getString(R.string.nc_pip_microphone_mute),
|
||||
MICROPHONE_PIP_REQUEST_MUTE);
|
||||
} else {
|
||||
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
|
||||
binding.microphoneButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
||||
updatePictureInPictureActions(R.drawable.ic_mic_off_white_24px,
|
||||
getResources().getString(R.string.nc_pip_microphone_unmute),
|
||||
MICROPHONE_PIP_REQUEST_UNMUTE);
|
||||
@ -974,7 +967,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
toggleMedia(microphoneOn, false);
|
||||
} else {
|
||||
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_white_24px);
|
||||
binding.microphoneButton.setImageResource(R.drawable.ic_mic_white_24px);
|
||||
pulseAnimation.start();
|
||||
toggleMedia(true, false);
|
||||
}
|
||||
@ -985,11 +978,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
R.string.nc_microphone_permission_permanently_denied,
|
||||
R.string.nc_permissions_settings, (AppCompatActivity) this);
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(PERMISSIONS_MICROPHONE, 100);
|
||||
} else {
|
||||
onRequestPermissionsResult(100, PERMISSIONS_MICROPHONE, new int[]{1});
|
||||
}
|
||||
requestPermissions(PERMISSIONS_MICROPHONE, 100);
|
||||
}
|
||||
}
|
||||
|
||||
@ -997,7 +986,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
if (!canPublishVideoStream) {
|
||||
videoOn = false;
|
||||
binding.cameraButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_off_white_24px);
|
||||
binding.cameraButton.setImageResource(R.drawable.ic_videocam_off_white_24px);
|
||||
binding.switchSelfVideoButton.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
@ -1006,12 +995,12 @@ public class CallActivity extends CallBaseActivity {
|
||||
videoOn = !videoOn;
|
||||
|
||||
if (videoOn) {
|
||||
binding.cameraButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_white_24px);
|
||||
binding.cameraButton.setImageResource(R.drawable.ic_videocam_white_24px);
|
||||
if (cameraEnumerator.getDeviceNames().length > 1) {
|
||||
binding.switchSelfVideoButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
binding.cameraButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_off_white_24px);
|
||||
binding.cameraButton.setImageResource(R.drawable.ic_videocam_off_white_24px);
|
||||
binding.switchSelfVideoButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@ -1022,12 +1011,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
R.string.nc_camera_permission_permanently_denied,
|
||||
R.string.nc_permissions_settings, (AppCompatActivity) this);
|
||||
} else {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(PERMISSIONS_CAMERA, 100);
|
||||
} else {
|
||||
onRequestPermissionsResult(100, PERMISSIONS_CAMERA, new int[]{1});
|
||||
}
|
||||
requestPermissions(PERMISSIONS_CAMERA, 100);
|
||||
}
|
||||
|
||||
}
|
||||
@ -2675,7 +2659,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
v.onTouchEvent(event);
|
||||
if (event.getAction() == MotionEvent.ACTION_UP && isPushToTalkActive) {
|
||||
isPushToTalkActive = false;
|
||||
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
|
||||
binding.microphoneButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
||||
pulseAnimation.stop();
|
||||
toggleMedia(false, false);
|
||||
animateCallControls(false, 5000);
|
||||
|
@ -28,8 +28,6 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.media.AudioAttributes
|
||||
import android.media.MediaPlayer
|
||||
import android.os.Build
|
||||
@ -41,24 +39,18 @@ import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import autodagger.AutoInjector
|
||||
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.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.CallNotificationActivityBinding
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound
|
||||
import com.nextcloud.talk.utils.NotificationUtils
|
||||
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
|
||||
@ -75,6 +67,7 @@ import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.call_item.*
|
||||
import okhttp3.Cache
|
||||
import org.parceler.Parcels
|
||||
import java.io.IOException
|
||||
@ -356,7 +349,7 @@ class CallNotificationActivity : CallBaseActivity() {
|
||||
private fun setUpAfterConversationIsKnown() {
|
||||
binding!!.conversationNameTextView.text = currentConversation!!.displayName
|
||||
if (currentConversation!!.type === Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
setAvatarForOneToOneCall()
|
||||
avatarImageView.loadAvatar(userBeingCalled!!, currentConversation!!.name!!)
|
||||
} else {
|
||||
binding!!.avatarImageView.setImageResource(R.drawable.ic_circular_group)
|
||||
}
|
||||
@ -364,34 +357,6 @@ class CallNotificationActivity : CallBaseActivity() {
|
||||
showAnswerControls()
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun setAvatarForOneToOneCall() {
|
||||
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
userBeingCalled!!.baseUrl,
|
||||
currentConversation!!.name,
|
||||
true
|
||||
)
|
||||
)
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
||||
dataSource.subscribe(
|
||||
object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(bitmap: Bitmap?) {
|
||||
binding!!.avatarImageView.hierarchy.setImage(
|
||||
BitmapDrawable(resources, bitmap), 100f,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage?>>) {
|
||||
Log.e(TAG, "failed to load avatar")
|
||||
}
|
||||
},
|
||||
UiThreadImmediateExecutorService.getInstance()
|
||||
)
|
||||
}
|
||||
|
||||
private fun endMediaNotifications() {
|
||||
if (mediaPlayer != null) {
|
||||
if (mediaPlayer!!.isPlaying) {
|
||||
|
@ -24,12 +24,10 @@ package com.nextcloud.talk.activities
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.ContactsContract
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
@ -148,10 +146,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
||||
super.onStart()
|
||||
Log.d(TAG, "onStart: Activity: " + System.identityHashCode(this).toString())
|
||||
logRouterBackStack(router!!)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
lockScreenIfConditionsApply()
|
||||
}
|
||||
lockScreenIfConditionsApply()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -323,7 +318,6 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
||||
})
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
fun lockScreenIfConditionsApply() {
|
||||
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) {
|
||||
|
@ -12,12 +12,9 @@ import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.activities.CallActivity;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
|
||||
import org.webrtc.MediaStream;
|
||||
import org.webrtc.MediaStreamTrack;
|
||||
@ -109,7 +106,7 @@ public class ParticipantsAdapter extends BaseAdapter {
|
||||
convertView.setLayoutParams(layoutParams);
|
||||
|
||||
TextView nickTextView = convertView.findViewById(R.id.peer_nick_text_view);
|
||||
SimpleDraweeView imageView = convertView.findViewById(R.id.avatarImageView);
|
||||
ImageView imageView = convertView.findViewById(R.id.avatarImageView);
|
||||
|
||||
MediaStream mediaStream = participantDisplayItem.getMediaStream();
|
||||
if (hasVideoStream(participantDisplayItem, mediaStream)) {
|
||||
@ -128,13 +125,7 @@ public class ParticipantsAdapter extends BaseAdapter {
|
||||
nickTextView.setVisibility(View.VISIBLE);
|
||||
nickTextView.setText(participantDisplayItem.getNick());
|
||||
}
|
||||
|
||||
imageView.setController(null);
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(imageView.getController())
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(participantDisplayItem.getUrlForAvatar()))
|
||||
.build();
|
||||
imageView.setController(draweeController);
|
||||
ImageViewExtensionsKt.loadAvatarWithUrl(imageView,null, participantDisplayItem.getUrlForAvatar());
|
||||
}
|
||||
|
||||
ImageView audioOffView = convertView.findViewById(R.id.remote_audio_off);
|
||||
|
@ -34,7 +34,7 @@ class ReactionsAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReactionsViewHolder {
|
||||
val itemBinding = ReactionItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return ReactionsViewHolder(itemBinding, user?.baseUrl)
|
||||
return ReactionsViewHolder(itemBinding, user)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ReactionsViewHolder, position: Int) {
|
||||
|
@ -22,18 +22,17 @@ package com.nextcloud.talk.adapters
|
||||
|
||||
import android.text.TextUtils
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ReactionItemBinding
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.extensions.loadGuestAvatar
|
||||
import com.nextcloud.talk.models.json.reactions.ReactionVoter
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
|
||||
class ReactionsViewHolder(
|
||||
private val binding: ReactionItemBinding,
|
||||
private val baseUrl: String?
|
||||
private val user: User?
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(reactionItem: ReactionItem, clickListener: ReactionItemClickListener) {
|
||||
@ -41,7 +40,7 @@ class ReactionsViewHolder(
|
||||
binding.reaction.text = reactionItem.reaction
|
||||
binding.name.text = reactionItem.reactionVoter.actorDisplayName
|
||||
|
||||
if (baseUrl != null && baseUrl.isNotEmpty()) {
|
||||
if (user != null && user.baseUrl?.isNotEmpty() == true) {
|
||||
loadAvatar(reactionItem)
|
||||
}
|
||||
}
|
||||
@ -52,35 +51,13 @@ class ReactionsViewHolder(
|
||||
if (!TextUtils.isEmpty(reactionItem.reactionVoter.actorDisplayName)) {
|
||||
displayName = reactionItem.reactionVoter.actorDisplayName!!
|
||||
}
|
||||
val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(binding.avatar.controller)
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForGuestAvatar(
|
||||
baseUrl,
|
||||
displayName,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
binding.avatar.controller = draweeController
|
||||
binding.avatar.loadGuestAvatar(user!!.baseUrl!!, displayName!!, false)
|
||||
} else if (reactionItem.reactionVoter.actorType == ReactionVoter.ReactionActorType.USERS) {
|
||||
val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(binding.avatar.controller)
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
baseUrl,
|
||||
reactionItem.reactionVoter.actorId,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
binding.avatar.controller = draweeController
|
||||
binding.avatar.loadAvatar(
|
||||
user!!,
|
||||
reactionItem.reactionVoter.actorId!!,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,15 +27,12 @@ import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.databinding.AccountItemBinding;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
@ -108,7 +105,6 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) {
|
||||
holder.binding.userIcon.setController(null);
|
||||
|
||||
if (adapter.hasFilter()) {
|
||||
FlexibleUtils.highlightText(
|
||||
@ -129,24 +125,10 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
|
||||
}
|
||||
}
|
||||
|
||||
holder.binding.userIcon.getHierarchy().setPlaceholderImage(R.drawable.account_circle_48dp);
|
||||
holder.binding.userIcon.getHierarchy().setFailureImage(R.drawable.account_circle_48dp);
|
||||
|
||||
if (user != null && user.getBaseUrl() != null &&
|
||||
user.getBaseUrl().startsWith("http://") ||
|
||||
user.getBaseUrl().startsWith("https://")) {
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.binding.userIcon.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
user.getBaseUrl(),
|
||||
participant.getCalculatedActorId(),
|
||||
true)))
|
||||
.build();
|
||||
holder.binding.userIcon.setController(draweeController);
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.userIcon, user, participant.getCalculatedActorId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,16 +29,13 @@ import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.databinding.RvItemContactBinding;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
@ -110,7 +107,6 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, ContactItemViewHolder holder, int position, List payloads) {
|
||||
holder.binding.avatarDraweeView.setController(null);
|
||||
|
||||
if (participant.getSelected()) {
|
||||
viewThemeUtils.platform.colorImageView(holder.binding.checkedImageView);
|
||||
@ -121,18 +117,18 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
|
||||
|
||||
if (!isOnline) {
|
||||
holder.binding.nameText.setTextColor(ResourcesCompat.getColor(
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.medium_emphasis_text,
|
||||
null)
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.medium_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.binding.avatarDraweeView.setAlpha(0.38f);
|
||||
holder.binding.avatarView.setAlpha(0.38f);
|
||||
} else {
|
||||
holder.binding.nameText.setTextColor(ResourcesCompat.getColor(
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.high_emphasis_text,
|
||||
null)
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.high_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.binding.avatarDraweeView.setAlpha(1.0f);
|
||||
holder.binding.avatarView.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
holder.binding.nameText.setText(participant.getDisplayName());
|
||||
@ -179,38 +175,29 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
|
||||
.getResources().getString(R.string.nc_guest);
|
||||
}
|
||||
|
||||
setUserStyleAvatar(holder,
|
||||
ApiUtils.getUrlForGuestAvatar(user.getBaseUrl(), displayName, false));
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, user, displayName, true);
|
||||
} else if (participant.getCalculatedActorType() == Participant.ActorType.USERS ||
|
||||
PARTICIPANT_SOURCE_USERS.equals(participant.getSource())) {
|
||||
setUserStyleAvatar(holder,
|
||||
ApiUtils.getUrlForAvatar(user.getBaseUrl(),
|
||||
participant.getCalculatedActorId(),
|
||||
false));
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, user, participant.getCalculatedActorId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUserStyleAvatar(ContactItemViewHolder holder, String avatarUrl) {
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.binding.avatarDraweeView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(avatarUrl))
|
||||
.build();
|
||||
holder.binding.avatarDraweeView.setController(draweeController);
|
||||
}
|
||||
|
||||
private void setGenericAvatar(
|
||||
ContactItemViewHolder holder,
|
||||
int roundPlaceholderDrawable,
|
||||
int fallbackImageResource) {
|
||||
Object avatar;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
|
||||
roundPlaceholderDrawable)));
|
||||
avatar = viewThemeUtils.talk.themePlaceholderAvatar(
|
||||
holder.binding.avatarView,
|
||||
roundPlaceholderDrawable
|
||||
);
|
||||
|
||||
} else {
|
||||
holder.binding.avatarDraweeView.setImageResource(fallbackImageResource);
|
||||
avatar = fallbackImageResource;
|
||||
}
|
||||
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, avatar);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,367 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* 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.items;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.databinding.RvItemConversationWithLastMessageBinding;
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage;
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation;
|
||||
import com.nextcloud.talk.ui.StatusDrawable;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import eu.davidea.flexibleadapter.items.IFilterable;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.flexibleadapter.items.ISectionable;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
|
||||
public class ConversationItem extends AbstractFlexibleItem<ConversationItem.ConversationItemViewHolder> implements
|
||||
ISectionable<ConversationItem.ConversationItemViewHolder, GenericTextHeaderItem>, IFilterable<String> {
|
||||
|
||||
public static final int VIEW_TYPE = R.layout.rv_item_conversation_with_last_message;
|
||||
|
||||
private static final float STATUS_SIZE_IN_DP = 9f;
|
||||
|
||||
private final Conversation conversation;
|
||||
private final User user;
|
||||
private final Context context;
|
||||
private GenericTextHeaderItem header;
|
||||
private final ViewThemeUtils viewThemeUtils;
|
||||
|
||||
|
||||
public ConversationItem(Conversation conversation, User user, Context activityContext, final ViewThemeUtils viewThemeUtils) {
|
||||
this.conversation = conversation;
|
||||
this.user = user;
|
||||
this.context = activityContext;
|
||||
this.viewThemeUtils = viewThemeUtils;
|
||||
}
|
||||
|
||||
public ConversationItem(Conversation conversation, User user,
|
||||
Context activityContext, GenericTextHeaderItem genericTextHeaderItem,
|
||||
final ViewThemeUtils viewThemeUtils) {
|
||||
this(conversation, user, activityContext, viewThemeUtils);
|
||||
this.header = genericTextHeaderItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof ConversationItem) {
|
||||
ConversationItem inItem = (ConversationItem) o;
|
||||
return conversation.equals(inItem.getModel());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Conversation getModel() {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = conversation.hashCode();
|
||||
result = 31 * result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayoutRes() {
|
||||
return R.layout.rv_item_conversation_with_last_message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType() {
|
||||
return VIEW_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversationItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new ConversationItemViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter,
|
||||
ConversationItemViewHolder holder,
|
||||
int position,
|
||||
List<Object> payloads) {
|
||||
Context appContext =
|
||||
NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext();
|
||||
holder.binding.dialogAvatar.setController(null);
|
||||
|
||||
holder.binding.dialogName.setTextColor(ResourcesCompat.getColor(context.getResources(),
|
||||
R.color.conversation_item_header,
|
||||
null));
|
||||
|
||||
if (adapter.hasFilter()) {
|
||||
viewThemeUtils.platform.highlightText(holder.binding.dialogName,
|
||||
conversation.getDisplayName(),
|
||||
String.valueOf(adapter.getFilter(String.class)));
|
||||
} else {
|
||||
holder.binding.dialogName.setText(conversation.getDisplayName());
|
||||
}
|
||||
|
||||
if (conversation.getUnreadMessages() > 0) {
|
||||
holder.binding.dialogName.setTypeface(holder.binding.dialogName.getTypeface(), Typeface.BOLD);
|
||||
holder.binding.dialogLastMessage.setTypeface(holder.binding.dialogLastMessage.getTypeface(), Typeface.BOLD);
|
||||
holder.binding.dialogUnreadBubble.setVisibility(View.VISIBLE);
|
||||
if (conversation.getUnreadMessages() < 1000) {
|
||||
holder.binding.dialogUnreadBubble.setText(Long.toString(conversation.getUnreadMessages()));
|
||||
} else {
|
||||
holder.binding.dialogUnreadBubble.setText(R.string.tooManyUnreadMessages);
|
||||
}
|
||||
|
||||
ColorStateList lightBubbleFillColor = ColorStateList.valueOf(
|
||||
ContextCompat.getColor(context,
|
||||
R.color.conversation_unread_bubble));
|
||||
int lightBubbleTextColor = ContextCompat.getColor(
|
||||
context,
|
||||
R.color.conversation_unread_bubble_text);
|
||||
|
||||
if (conversation.getType() == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble);
|
||||
} else if (conversation.getUnreadMention()) {
|
||||
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "direct-mention-flag")) {
|
||||
if (conversation.getUnreadMentionDirect()) {
|
||||
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble);
|
||||
} else {
|
||||
viewThemeUtils.material.colorChipOutlined(holder.binding.dialogUnreadBubble, 6.0f);
|
||||
}
|
||||
} else {
|
||||
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble);
|
||||
}
|
||||
} else {
|
||||
holder.binding.dialogUnreadBubble.setChipBackgroundColor(lightBubbleFillColor);
|
||||
holder.binding.dialogUnreadBubble.setTextColor(lightBubbleTextColor);
|
||||
}
|
||||
} else {
|
||||
holder.binding.dialogName.setTypeface(null, Typeface.NORMAL);
|
||||
holder.binding.dialogDate.setTypeface(null, Typeface.NORMAL);
|
||||
holder.binding.dialogLastMessage.setTypeface(null, Typeface.NORMAL);
|
||||
holder.binding.dialogUnreadBubble.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (conversation.getFavorite()) {
|
||||
holder.binding.favoriteConversationImageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.binding.favoriteConversationImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (conversation.getStatus() != null && Conversation.ConversationType.ROOM_SYSTEM != conversation.getType()) {
|
||||
float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, appContext);
|
||||
|
||||
holder.binding.userStatusImage.setVisibility(View.VISIBLE);
|
||||
holder.binding.userStatusImage.setImageDrawable(new StatusDrawable(
|
||||
conversation.getStatus(),
|
||||
conversation.getStatusIcon(),
|
||||
size,
|
||||
context.getResources().getColor(R.color.bg_default),
|
||||
appContext));
|
||||
} else {
|
||||
holder.binding.userStatusImage.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (conversation.getLastMessage() != null) {
|
||||
holder.binding.dialogDate.setVisibility(View.VISIBLE);
|
||||
holder.binding.dialogDate.setText(
|
||||
DateUtils.getRelativeTimeSpanString(conversation.getLastActivity() * 1000L,
|
||||
System.currentTimeMillis(),
|
||||
0,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE));
|
||||
|
||||
if (!TextUtils.isEmpty(conversation.getLastMessage().getSystemMessage()) ||
|
||||
Conversation.ConversationType.ROOM_SYSTEM == conversation.getType()) {
|
||||
holder.binding.dialogLastMessage.setText(conversation.getLastMessage().getText());
|
||||
} else {
|
||||
String authorDisplayName = "";
|
||||
conversation.getLastMessage().setActiveUser(user);
|
||||
String text;
|
||||
if (conversation.getLastMessage().getCalculateMessageType() == ChatMessage.MessageType.REGULAR_TEXT_MESSAGE) {
|
||||
if (conversation.getLastMessage().getActorId().equals(user.getUserId())) {
|
||||
text = String.format(appContext.getString(R.string.nc_formatted_message_you),
|
||||
conversation.getLastMessage().getLastMessageDisplayText());
|
||||
} else {
|
||||
authorDisplayName = !TextUtils.isEmpty(conversation.getLastMessage().getActorDisplayName()) ?
|
||||
conversation.getLastMessage().getActorDisplayName() :
|
||||
"guests".equals(conversation.getLastMessage().getActorType()) ?
|
||||
appContext.getString(R.string.nc_guest) : "";
|
||||
text = String.format(appContext.getString(R.string.nc_formatted_message),
|
||||
authorDisplayName,
|
||||
conversation.getLastMessage().getLastMessageDisplayText());
|
||||
}
|
||||
} else {
|
||||
text = conversation.getLastMessage().getLastMessageDisplayText();
|
||||
}
|
||||
|
||||
holder.binding.dialogLastMessage.setText(text);
|
||||
}
|
||||
} else {
|
||||
holder.binding.dialogDate.setVisibility(View.GONE);
|
||||
holder.binding.dialogLastMessage.setText(R.string.nc_no_messages_yet);
|
||||
}
|
||||
|
||||
holder.binding.dialogAvatar.setVisibility(View.VISIBLE);
|
||||
|
||||
boolean shouldLoadAvatar = true;
|
||||
String objectType;
|
||||
if (!TextUtils.isEmpty(objectType = conversation.getObjectType())) {
|
||||
switch (objectType) {
|
||||
case "share:password":
|
||||
shouldLoadAvatar = false;
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
ContextCompat.getDrawable(context,
|
||||
R.drawable.ic_circular_lock));
|
||||
break;
|
||||
case "file":
|
||||
shouldLoadAvatar = false;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
|
||||
R.drawable.ic_avatar_document)));
|
||||
} else {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_circular_document));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation.getType() == Conversation.ConversationType.ROOM_SYSTEM) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
Drawable[] layers = new Drawable[2];
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background);
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground);
|
||||
LayerDrawable layerDrawable = new LayerDrawable(layers);
|
||||
|
||||
holder.binding.dialogAvatar.getHierarchy().setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(layerDrawable));
|
||||
} else {
|
||||
holder.binding.dialogAvatar.getHierarchy().setPlaceholderImage(R.mipmap.ic_launcher);
|
||||
}
|
||||
shouldLoadAvatar = false;
|
||||
}
|
||||
|
||||
if (shouldLoadAvatar) {
|
||||
switch (conversation.getType()) {
|
||||
case ROOM_TYPE_ONE_TO_ONE_CALL:
|
||||
if (!TextUtils.isEmpty(conversation.getName())) {
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.binding.dialogAvatar.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(user.getBaseUrl(),
|
||||
conversation.getName(),
|
||||
true),
|
||||
user))
|
||||
.build();
|
||||
holder.binding.dialogAvatar.setController(draweeController);
|
||||
} else {
|
||||
holder.binding.dialogAvatar.setVisibility(View.GONE);
|
||||
}
|
||||
break;
|
||||
case ROOM_GROUP_CALL:
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
|
||||
R.drawable.ic_avatar_group)));
|
||||
} else {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_circular_group));
|
||||
}
|
||||
break;
|
||||
case ROOM_PUBLIC_CALL:
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
|
||||
R.drawable.ic_avatar_link)));
|
||||
} else {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_circular_link));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
holder.binding.dialogAvatar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(String constraint) {
|
||||
return conversation.getDisplayName() != null &&
|
||||
Pattern
|
||||
.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
|
||||
.matcher(conversation.getDisplayName().trim())
|
||||
.find();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericTextHeaderItem getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(GenericTextHeaderItem header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
static class ConversationItemViewHolder extends FlexibleViewHolder {
|
||||
|
||||
RvItemConversationWithLastMessageBinding binding;
|
||||
|
||||
ConversationItemViewHolder(View view, FlexibleAdapter adapter) {
|
||||
super(view, adapter);
|
||||
binding = RvItemConversationWithLastMessageBinding.bind(view);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* @author Marcel Hibbe
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* 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.items
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.adapters.items.ConversationItem.ConversationItemViewHolder
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.RvItemConversationWithLastMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.extensions.loadGroupCallAvatar
|
||||
import com.nextcloud.talk.extensions.loadPublicCallAvatar
|
||||
import com.nextcloud.talk.extensions.loadSystemAvatar
|
||||
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
|
||||
import com.nextcloud.talk.ui.StatusDrawable
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFilterable
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.flexibleadapter.items.ISectionable
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class ConversationItem(
|
||||
val model: Conversation,
|
||||
private val user: User,
|
||||
private val context: Context,
|
||||
private val viewThemeUtils: ViewThemeUtils
|
||||
) : AbstractFlexibleItem<ConversationItemViewHolder>(),
|
||||
ISectionable<ConversationItemViewHolder, GenericTextHeaderItem?>,
|
||||
IFilterable<String?> {
|
||||
private var header: GenericTextHeaderItem? = null
|
||||
|
||||
constructor(
|
||||
conversation: Conversation,
|
||||
user: User,
|
||||
activityContext: Context,
|
||||
genericTextHeaderItem: GenericTextHeaderItem?,
|
||||
viewThemeUtils: ViewThemeUtils
|
||||
) : this(conversation, user, activityContext, viewThemeUtils) {
|
||||
header = genericTextHeaderItem
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is ConversationItem) {
|
||||
return model == other.model
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = model.hashCode()
|
||||
result *= 31
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.rv_item_conversation_with_last_message
|
||||
}
|
||||
|
||||
override fun getItemViewType(): Int {
|
||||
return VIEW_TYPE
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>?>?): ConversationItemViewHolder {
|
||||
return ConversationItemViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>?>,
|
||||
holder: ConversationItemViewHolder,
|
||||
position: Int,
|
||||
payloads: List<Any>
|
||||
) {
|
||||
val appContext = sharedApplication!!.applicationContext
|
||||
holder.binding.dialogName.setTextColor(
|
||||
ResourcesCompat.getColor(
|
||||
context.resources,
|
||||
R.color.conversation_item_header,
|
||||
null
|
||||
)
|
||||
)
|
||||
if (adapter.hasFilter()) {
|
||||
viewThemeUtils.platform.highlightText(
|
||||
holder.binding.dialogName,
|
||||
model.displayName!!, adapter.getFilter(String::class.java).toString()
|
||||
)
|
||||
} else {
|
||||
holder.binding.dialogName.text = model.displayName
|
||||
}
|
||||
if (model.unreadMessages > 0) {
|
||||
showUnreadMessages(holder)
|
||||
} else {
|
||||
holder.binding.dialogName.setTypeface(null, Typeface.NORMAL)
|
||||
holder.binding.dialogDate.setTypeface(null, Typeface.NORMAL)
|
||||
holder.binding.dialogLastMessage.setTypeface(null, Typeface.NORMAL)
|
||||
holder.binding.dialogUnreadBubble.visibility = View.GONE
|
||||
}
|
||||
if (model.favorite) {
|
||||
holder.binding.favoriteConversationImageView.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.binding.favoriteConversationImageView.visibility = View.GONE
|
||||
}
|
||||
if (ConversationType.ROOM_SYSTEM !== model.type) {
|
||||
val size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, appContext)
|
||||
holder.binding.userStatusImage.visibility = View.VISIBLE
|
||||
holder.binding.userStatusImage.setImageDrawable(
|
||||
StatusDrawable(
|
||||
model.status,
|
||||
model.statusIcon,
|
||||
size,
|
||||
context.resources.getColor(R.color.bg_default),
|
||||
appContext
|
||||
)
|
||||
)
|
||||
} else {
|
||||
holder.binding.userStatusImage.visibility = View.GONE
|
||||
}
|
||||
setLastMessage(holder, appContext)
|
||||
showAvatar(holder)
|
||||
}
|
||||
|
||||
private fun showAvatar(holder: ConversationItemViewHolder) {
|
||||
holder.binding.dialogAvatar.visibility = View.VISIBLE
|
||||
var shouldLoadAvatar = shouldLoadAvatar(holder)
|
||||
if (ConversationType.ROOM_SYSTEM == model.type) {
|
||||
holder.binding.dialogAvatar.loadSystemAvatar()
|
||||
shouldLoadAvatar = false
|
||||
}
|
||||
if (shouldLoadAvatar) {
|
||||
when (model.type) {
|
||||
ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(model.name)) {
|
||||
holder.binding.dialogAvatar.loadAvatar(user, model.name!!)
|
||||
} else {
|
||||
holder.binding.dialogAvatar.visibility = View.GONE
|
||||
}
|
||||
ConversationType.ROOM_GROUP_CALL ->
|
||||
holder.binding.dialogAvatar.loadGroupCallAvatar(viewThemeUtils)
|
||||
ConversationType.ROOM_PUBLIC_CALL ->
|
||||
holder.binding.dialogAvatar.loadPublicCallAvatar(viewThemeUtils)
|
||||
else -> holder.binding.dialogAvatar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldLoadAvatar(
|
||||
holder: ConversationItemViewHolder
|
||||
): Boolean {
|
||||
var objectType: String?
|
||||
var returnValue = true
|
||||
if (!TextUtils.isEmpty(model.objectType.also { objectType = it })) {
|
||||
when (objectType) {
|
||||
"share:password" -> {
|
||||
holder.binding.dialogAvatar.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.ic_circular_lock
|
||||
)
|
||||
)
|
||||
returnValue = false
|
||||
}
|
||||
"file" -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.dialogAvatar.loadAvatar(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(
|
||||
holder.binding.dialogAvatar,
|
||||
R.drawable.ic_avatar_document
|
||||
)
|
||||
)
|
||||
} else {
|
||||
holder.binding.dialogAvatar.loadAvatar(
|
||||
R.drawable.ic_circular_document
|
||||
)
|
||||
}
|
||||
returnValue = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
|
||||
private fun setLastMessage(
|
||||
holder: ConversationItemViewHolder,
|
||||
appContext: Context
|
||||
) {
|
||||
if (model.lastMessage != null) {
|
||||
holder.binding.dialogDate.visibility = View.VISIBLE
|
||||
holder.binding.dialogDate.text = DateUtils.getRelativeTimeSpanString(
|
||||
model.lastActivity * MILLIES,
|
||||
System.currentTimeMillis(),
|
||||
0,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
)
|
||||
if (!TextUtils.isEmpty(model.lastMessage!!.systemMessage) ||
|
||||
ConversationType.ROOM_SYSTEM === model.type
|
||||
) {
|
||||
holder.binding.dialogLastMessage.text = model.lastMessage!!.text
|
||||
} else {
|
||||
model.lastMessage!!.activeUser = user
|
||||
|
||||
val text = if (model.lastMessage!!.getCalculateMessageType() === ChatMessage.MessageType
|
||||
.REGULAR_TEXT_MESSAGE
|
||||
) {
|
||||
calculateRegularLastMessageText(appContext)
|
||||
} else {
|
||||
model.lastMessage!!.lastMessageDisplayText
|
||||
}
|
||||
holder.binding.dialogLastMessage.text = text
|
||||
}
|
||||
} else {
|
||||
holder.binding.dialogDate.visibility = View.GONE
|
||||
holder.binding.dialogLastMessage.setText(R.string.nc_no_messages_yet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateRegularLastMessageText(appContext: Context): String {
|
||||
return if (model.lastMessage!!.actorId == user.userId) {
|
||||
String.format(
|
||||
appContext.getString(R.string.nc_formatted_message_you),
|
||||
model.lastMessage!!.lastMessageDisplayText
|
||||
)
|
||||
} else {
|
||||
val authorDisplayName =
|
||||
if (!TextUtils.isEmpty(model.lastMessage!!.actorDisplayName)) {
|
||||
model.lastMessage!!.actorDisplayName
|
||||
} else if ("guests" == model.lastMessage!!.actorType) {
|
||||
appContext.getString(R.string.nc_guest)
|
||||
} else {
|
||||
""
|
||||
}
|
||||
String.format(
|
||||
appContext.getString(R.string.nc_formatted_message),
|
||||
authorDisplayName,
|
||||
model.lastMessage!!.lastMessageDisplayText
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUnreadMessages(holder: ConversationItemViewHolder) {
|
||||
holder.binding.dialogName.setTypeface(holder.binding.dialogName.typeface, Typeface.BOLD)
|
||||
holder.binding.dialogLastMessage.setTypeface(holder.binding.dialogLastMessage.typeface, Typeface.BOLD)
|
||||
holder.binding.dialogUnreadBubble.visibility = View.VISIBLE
|
||||
if (model.unreadMessages < UNREAD_MESSAGES_TRESHOLD) {
|
||||
holder.binding.dialogUnreadBubble.text = model.unreadMessages.toLong().toString()
|
||||
} else {
|
||||
holder.binding.dialogUnreadBubble.setText(R.string.tooManyUnreadMessages)
|
||||
}
|
||||
val lightBubbleFillColor = ColorStateList.valueOf(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.conversation_unread_bubble
|
||||
)
|
||||
)
|
||||
val lightBubbleTextColor = ContextCompat.getColor(
|
||||
context,
|
||||
R.color.conversation_unread_bubble_text
|
||||
)
|
||||
if (model.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
|
||||
} else if (model.unreadMention) {
|
||||
if (hasSpreedFeatureCapability(user, "direct-mention-flag")) {
|
||||
if (model.unreadMentionDirect!!) {
|
||||
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
|
||||
} else {
|
||||
viewThemeUtils.material.colorChipOutlined(
|
||||
holder.binding.dialogUnreadBubble,
|
||||
UNREAD_BUBBLE_STROKE_WIDTH
|
||||
)
|
||||
}
|
||||
} else {
|
||||
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
|
||||
}
|
||||
} else {
|
||||
holder.binding.dialogUnreadBubble.chipBackgroundColor = lightBubbleFillColor
|
||||
holder.binding.dialogUnreadBubble.setTextColor(lightBubbleTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun filter(constraint: String?): Boolean {
|
||||
return model.displayName != null &&
|
||||
Pattern
|
||||
.compile(constraint!!, Pattern.CASE_INSENSITIVE or Pattern.LITERAL)
|
||||
.matcher(model.displayName!!.trim { it <= ' ' })
|
||||
.find()
|
||||
}
|
||||
|
||||
override fun getHeader(): GenericTextHeaderItem? {
|
||||
return header
|
||||
}
|
||||
|
||||
override fun setHeader(header: GenericTextHeaderItem?) {
|
||||
this.header = header
|
||||
}
|
||||
|
||||
class ConversationItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter) {
|
||||
var binding: RvItemConversationWithLastMessageBinding
|
||||
|
||||
init {
|
||||
binding = RvItemConversationWithLastMessageBinding.bind(view!!)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VIEW_TYPE = R.layout.rv_item_conversation_with_last_message
|
||||
private const val MILLIES = 1000L
|
||||
private const val STATUS_SIZE_IN_DP = 9f
|
||||
private const val UNREAD_BUBBLE_STROKE_WIDTH = 6.0f
|
||||
private const val UNREAD_MESSAGES_TRESHOLD = 1000
|
||||
}
|
||||
}
|
@ -29,15 +29,13 @@ import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.models.json.mention.Mention;
|
||||
import com.nextcloud.talk.models.json.status.StatusType;
|
||||
import com.nextcloud.talk.ui.StatusDrawable;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
import java.util.List;
|
||||
@ -151,34 +149,22 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
|
||||
|
||||
if (SOURCE_CALLS.equals(source)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
|
||||
R.drawable.ic_avatar_group)));
|
||||
ImageViewExtensionsKt.loadAvatar(
|
||||
holder.binding.avatarView,
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(
|
||||
holder.binding.avatarView,
|
||||
R.drawable.ic_avatar_group
|
||||
)
|
||||
);
|
||||
} else {
|
||||
holder.binding.avatarDraweeView.setImageResource(R.drawable.ic_circular_group);
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, R.drawable.ic_circular_group);
|
||||
}
|
||||
} else {
|
||||
String avatarId = objectId;
|
||||
String avatarUrl = ApiUtils.getUrlForAvatar(currentUser.getBaseUrl(),
|
||||
avatarId, true);
|
||||
|
||||
if (SOURCE_GUESTS.equals(source)) {
|
||||
avatarId = displayName;
|
||||
avatarUrl = ApiUtils.getUrlForGuestAvatar(
|
||||
currentUser.getBaseUrl(),
|
||||
avatarId,
|
||||
false);
|
||||
}
|
||||
|
||||
holder.binding.avatarDraweeView.setController(null);
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.binding.avatarDraweeView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(avatarUrl))
|
||||
.build();
|
||||
holder.binding.avatarDraweeView.setController(draweeController);
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, currentUser, avatarId, true);
|
||||
}
|
||||
|
||||
drawStatus(holder);
|
||||
|
@ -2,6 +2,8 @@
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Álvaro Brey
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2022 Álvaro Brey
|
||||
* Copyright (C) 2022 Nextcloud GmbH
|
||||
*
|
||||
@ -27,9 +29,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.RvItemSearchMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadThumbnail
|
||||
import com.nextcloud.talk.models.domain.SearchMessageEntry
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFilterable
|
||||
@ -72,7 +74,7 @@ data class MessageResultItem constructor(
|
||||
) {
|
||||
holder.binding.conversationTitle.text = messageEntry.title
|
||||
bindMessageExcerpt(holder)
|
||||
loadImage(holder)
|
||||
messageEntry.thumbnailURL?.let { holder.binding.thumbnail.loadThumbnail(it, currentUser) }
|
||||
}
|
||||
|
||||
private fun bindMessageExcerpt(holder: ViewHolder) {
|
||||
@ -83,17 +85,6 @@ data class MessageResultItem constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadImage(holder: ViewHolder) {
|
||||
DisplayUtils.loadAvatarPlaceholder(holder.binding.thumbnail)
|
||||
if (messageEntry.thumbnailURL != null) {
|
||||
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
||||
messageEntry.thumbnailURL,
|
||||
currentUser
|
||||
)
|
||||
DisplayUtils.loadImage(holder.binding.thumbnail, imageRequest)
|
||||
}
|
||||
}
|
||||
|
||||
override fun filter(constraint: String?): Boolean = true
|
||||
|
||||
override fun getItemViewType(): Int {
|
||||
|
@ -27,23 +27,20 @@ package com.nextcloud.talk.adapters.items;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.databinding.RvItemConversationInfoParticipantBinding;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.participants.Participant.InCallFlags;
|
||||
import com.nextcloud.talk.models.json.status.StatusType;
|
||||
import com.nextcloud.talk.ui.StatusDrawable;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
import java.util.List;
|
||||
@ -111,24 +108,22 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, ParticipantItemViewHolder holder, int position, List payloads) {
|
||||
|
||||
holder.binding.avatarDraweeView.setController(null);
|
||||
|
||||
drawStatus(holder);
|
||||
|
||||
if (!isOnline) {
|
||||
holder.binding.nameText.setTextColor(ResourcesCompat.getColor(
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.medium_emphasis_text,
|
||||
null)
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.medium_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.binding.avatarDraweeView.setAlpha(0.38f);
|
||||
holder.binding.avatarView.setAlpha(0.38f);
|
||||
} else {
|
||||
holder.binding.nameText.setTextColor(ResourcesCompat.getColor(
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.high_emphasis_text,
|
||||
null)
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.high_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.binding.avatarDraweeView.setAlpha(1.0f);
|
||||
holder.binding.avatarView.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
holder.binding.nameText.setText(participant.getDisplayName());
|
||||
@ -152,23 +147,9 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
|
||||
"groups".equals(participant.getSource()) ||
|
||||
participant.getCalculatedActorType() == Participant.ActorType.CIRCLES ||
|
||||
"circles".equals(participant.getSource())) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
|
||||
R.drawable.ic_avatar_group)));
|
||||
} else {
|
||||
holder.binding.avatarDraweeView.setImageResource(R.drawable.ic_circular_group);
|
||||
}
|
||||
ImageViewExtensionsKt.loadGroupCallAvatar(holder.binding.avatarView, viewThemeUtils);
|
||||
} else if (participant.getCalculatedActorType() == Participant.ActorType.EMAILS) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
|
||||
R.drawable.ic_avatar_mail)));
|
||||
} else {
|
||||
holder.binding.avatarDraweeView.setImageResource(R.drawable.ic_circular_mail);
|
||||
}
|
||||
ImageViewExtensionsKt.loadMailAvatar(holder.binding.avatarView, viewThemeUtils);
|
||||
} else if (participant.getCalculatedActorType() == Participant.ActorType.GUESTS ||
|
||||
participant.getType() == Participant.ParticipantType.GUEST ||
|
||||
participant.getType() == Participant.ParticipantType.GUEST_MODERATOR) {
|
||||
@ -180,25 +161,11 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
|
||||
displayName = participant.getDisplayName();
|
||||
}
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.binding.avatarDraweeView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForGuestAvatar(user.getBaseUrl(),
|
||||
displayName, false)))
|
||||
.build();
|
||||
holder.binding.avatarDraweeView.setController(draweeController);
|
||||
ImageViewExtensionsKt.loadGuestAvatar(holder.binding.avatarView, user, displayName, false);
|
||||
|
||||
} else if (participant.getCalculatedActorType() == Participant.ActorType.USERS ||
|
||||
participant.getSource().equals("users")) {
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.binding.avatarDraweeView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(user.getBaseUrl(),
|
||||
participant.getCalculatedActorId(), false)))
|
||||
.build();
|
||||
holder.binding.avatarDraweeView.setController(draweeController);
|
||||
ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, user, participant.getCalculatedActorId(), true);
|
||||
}
|
||||
|
||||
Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources();
|
||||
|
@ -24,25 +24,21 @@ package com.nextcloud.talk.adapters.messages
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import autodagger.AutoInjector
|
||||
import coil.load
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.databinding.ItemCustomIncomingLinkPreviewMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadBotsAvatar
|
||||
import com.nextcloud.talk.extensions.loadChangelogBotAvatar
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.stfalcon.chatkit.messages.MessageHolders
|
||||
import javax.inject.Inject
|
||||
@ -143,26 +139,9 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) : M
|
||||
if (message.actorType == "guests") {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.actorType == "bots" && message.actorId == "changelog") {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
} else {
|
||||
binding.messageUserAvatar.setImageResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
binding.messageUserAvatar.loadChangelogBotAvatar()
|
||||
} else if (message.actorType == "bots") {
|
||||
val drawable = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(
|
||||
">",
|
||||
ResourcesCompat.getColor(context.resources, R.color.black, null)
|
||||
)
|
||||
binding.messageUserAvatar.visibility = View.VISIBLE
|
||||
binding.messageUserAvatar.setImageDrawable(drawable)
|
||||
binding.messageUserAvatar.loadBotsAvatar()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,6 @@ package com.nextcloud.talk.adapters.messages
|
||||
import android.annotation.SuppressLint
|
||||
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.TextUtils
|
||||
import android.util.Log
|
||||
@ -40,18 +38,17 @@ import android.view.View
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import autodagger.AutoInjector
|
||||
import coil.load
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.databinding.ItemCustomIncomingLocationMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadBotsAvatar
|
||||
import com.nextcloud.talk.extensions.loadChangelogBotAvatar
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.UriUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.stfalcon.chatkit.messages.MessageHolders
|
||||
@ -136,22 +133,9 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess
|
||||
if (message.actorType == "guests") {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.actorType == "bots" && message.actorId == "changelog") {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = AppCompatResources.getDrawable(context!!, R.drawable.ic_launcher_background)
|
||||
layers[1] = AppCompatResources.getDrawable(context!!, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
binding.messageUserAvatar.loadChangelogBotAvatar()
|
||||
} else if (message.actorType == "bots") {
|
||||
val drawable = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(
|
||||
">",
|
||||
context!!.resources.getColor(R.color.black)
|
||||
)
|
||||
binding.messageUserAvatar.visibility = View.VISIBLE
|
||||
binding.messageUserAvatar.setImageDrawable(drawable)
|
||||
binding.messageUserAvatar.loadBotsAvatar()
|
||||
}
|
||||
} else {
|
||||
if (message.isOneToOneConversation) {
|
||||
|
@ -22,27 +22,23 @@ package com.nextcloud.talk.adapters.messages
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import autodagger.AutoInjector
|
||||
import coil.load
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.MainActivity
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadBotsAvatar
|
||||
import com.nextcloud.talk.extensions.loadChangelogBotAvatar
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.polls.ui.PollMainDialogFragment
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.stfalcon.chatkit.messages.MessageHolders
|
||||
import javax.inject.Inject
|
||||
@ -170,26 +166,9 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH
|
||||
if (message.actorType == "guests") {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.actorType == "bots" && message.actorId == "changelog") {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
} else {
|
||||
binding.messageUserAvatar.setImageResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
binding.messageUserAvatar.loadChangelogBotAvatar()
|
||||
} else if (message.actorType == "bots") {
|
||||
val drawable = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(
|
||||
">",
|
||||
ResourcesCompat.getColor(context.resources, R.color.black, null)
|
||||
)
|
||||
binding.messageUserAvatar.visibility = View.VISIBLE
|
||||
binding.messageUserAvatar.setImageDrawable(drawable)
|
||||
binding.messageUserAvatar.loadBotsAvatar()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021-2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -23,9 +23,9 @@
|
||||
package com.nextcloud.talk.adapters.messages;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.databinding.ItemCustomIncomingPreviewMessageBinding;
|
||||
@ -74,7 +74,7 @@ public class IncomingPreviewMessageViewHolder extends PreviewMessageViewHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleDraweeView getPreviewContactPhoto() {
|
||||
public ImageView getPreviewContactPhoto() {
|
||||
return binding.contactPhoto;
|
||||
}
|
||||
|
||||
|
@ -26,24 +26,21 @@ 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.os.Build
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import autodagger.AutoInjector
|
||||
import coil.load
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadBotsAvatar
|
||||
import com.nextcloud.talk.extensions.loadChangelogBotAvatar
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
@ -179,28 +176,9 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) : MessageHolde
|
||||
if (message.actorType == "guests") {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.actorType == "bots" && message.actorId == "changelog") {
|
||||
if (context != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
} else {
|
||||
binding.messageUserAvatar.setImageResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
binding.messageUserAvatar.loadChangelogBotAvatar()
|
||||
} else if (message.actorType == "bots") {
|
||||
val drawable = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(
|
||||
">",
|
||||
ResourcesCompat.getColor(context!!.resources, R.color.black, null)
|
||||
)
|
||||
binding.messageUserAvatar.visibility = View.VISIBLE
|
||||
binding.messageUserAvatar.setImageDrawable(drawable)
|
||||
binding.messageUserAvatar.loadBotsAvatar()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,6 @@ package com.nextcloud.talk.adapters.messages
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
@ -46,10 +43,10 @@ import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.databinding.ItemCustomIncomingVoiceMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadChangelogBotAvatar
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.stfalcon.chatkit.messages.MessageHolders
|
||||
import java.util.concurrent.ExecutionException
|
||||
@ -245,15 +242,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
|
||||
if (message.actorType == "guests") {
|
||||
// do nothing, avatar is set
|
||||
} else if (message.actorType == "bots" && message.actorId == "changelog") {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
} else {
|
||||
binding.messageUserAvatar.setImageResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
binding.messageUserAvatar.loadChangelogBotAvatar()
|
||||
} else if (message.actorType == "bots") {
|
||||
val drawable = TextDrawable.builder()
|
||||
.beginConfig()
|
||||
|
@ -25,14 +25,12 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import coil.load
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.databinding.ReferenceInsideMessageBinding
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.opengraph.OpenGraphOverall
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
@ -92,12 +90,7 @@ class LinkPreview {
|
||||
val referenceThumbUrl = reference.openGraphObject?.thumb
|
||||
if (!referenceThumbUrl.isNullOrEmpty()) {
|
||||
binding.referenceThumbImage.visibility = View.VISIBLE
|
||||
val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(referenceThumbUrl))
|
||||
.build()
|
||||
binding.referenceThumbImage.controller =
|
||||
draweeController
|
||||
binding.referenceThumbImage.load(referenceThumbUrl)
|
||||
} else {
|
||||
binding.referenceThumbImage.visibility = View.GONE
|
||||
}
|
||||
|
@ -23,9 +23,9 @@
|
||||
package com.nextcloud.talk.adapters.messages;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.databinding.ItemCustomOutcomingPreviewMessageBinding;
|
||||
@ -75,7 +75,7 @@ public class OutcomingPreviewMessageViewHolder extends PreviewMessageViewHolder
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleDraweeView getPreviewContactPhoto() {
|
||||
public ImageView getPreviewContactPhoto() {
|
||||
return binding.contactPhoto;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @author Marcel Hibbe
|
||||
* @author Andy Scherzinger
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021-2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
@ -30,7 +30,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.util.Base64
|
||||
@ -38,13 +37,13 @@ import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.ProgressBar
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.emoji.widget.EmojiTextView
|
||||
import autodagger.AutoInjector
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
@ -53,6 +52,7 @@ import com.nextcloud.talk.components.filebrowser.models.BrowserFile
|
||||
import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding
|
||||
import com.nextcloud.talk.extensions.loadChangelogBotAvatar
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
@ -92,6 +92,8 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
lateinit var commonMessageInterface: CommonMessageInterface
|
||||
var previewMessageInterface: PreviewMessageInterface? = null
|
||||
|
||||
private var placeholder: Drawable? = null
|
||||
|
||||
init {
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
}
|
||||
@ -118,13 +120,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
}
|
||||
}
|
||||
if (ACTOR_TYPE_BOTS == message.actorType && ACTOR_ID_CHANGELOG == message.actorId) {
|
||||
if (context != null) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
userAvatar.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
}
|
||||
userAvatar.loadChangelogBotAvatar()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,11 +146,10 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
}
|
||||
if (message.selectedIndividualHashMap!!.containsKey(KEY_CONTACT_PHOTO)) {
|
||||
image = previewContactPhoto
|
||||
val drawable = getDrawableFromContactDetails(
|
||||
placeholder = getDrawableFromContactDetails(
|
||||
context,
|
||||
message.selectedIndividualHashMap!![KEY_CONTACT_PHOTO]
|
||||
)
|
||||
image.hierarchy.setPlaceholderImage(drawable)
|
||||
} else if (message.selectedIndividualHashMap!!.containsKey(KEY_MIMETYPE)) {
|
||||
val mimetype = message.selectedIndividualHashMap!![KEY_MIMETYPE]
|
||||
val drawableResourceId = getDrawableResourceIdForMimeType(mimetype)
|
||||
@ -170,7 +165,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
}
|
||||
image.hierarchy.setPlaceholderImage(drawable)
|
||||
placeholder = drawable
|
||||
} else {
|
||||
fetchFileInformation(
|
||||
"/" + message.selectedIndividualHashMap!![KEY_PATH],
|
||||
@ -208,13 +203,13 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
DisplayUtils.setClickableString("Tenor", "https://tenor.com", messageText)
|
||||
} else {
|
||||
if (message.messageType == ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE.name) {
|
||||
(clickView as SimpleDraweeView?)?.setOnClickListener {
|
||||
clickView!!.setOnClickListener {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(message.imageUrl))
|
||||
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context!!.startActivity(browserIntent)
|
||||
}
|
||||
} else {
|
||||
(clickView as SimpleDraweeView?)?.setOnClickListener(null)
|
||||
clickView!!.setOnClickListener(null)
|
||||
}
|
||||
messageText.text = ""
|
||||
}
|
||||
@ -238,6 +233,10 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
commonMessageInterface.onClickReaction(chatMessage, emoji)
|
||||
}
|
||||
|
||||
override fun getPayloadForImageLoader(message: ChatMessage?): Any? {
|
||||
return placeholder
|
||||
}
|
||||
|
||||
private fun getDrawableFromContactDetails(context: Context?, base64: String?): Drawable? {
|
||||
var drawable: Drawable? = null
|
||||
if (base64 != "") {
|
||||
@ -300,8 +299,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
if (browserFileList.isNotEmpty()) {
|
||||
Handler(context!!.mainLooper).post {
|
||||
val resourceId = getDrawableResourceIdForMimeType(browserFileList[0].mimeType)
|
||||
val drawable = ContextCompat.getDrawable(context!!, resourceId)
|
||||
image.hierarchy.setPlaceholderImage(drawable)
|
||||
placeholder = ContextCompat.getDrawable(context!!, resourceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -324,7 +322,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) :
|
||||
abstract val messageText: EmojiTextView
|
||||
abstract val previewContainer: View
|
||||
abstract val previewContactContainer: MaterialCardView
|
||||
abstract val previewContactPhoto: SimpleDraweeView
|
||||
abstract val previewContactPhoto: ImageView
|
||||
abstract val previewContactName: EmojiTextView
|
||||
abstract val previewContactProgressBar: ProgressBar?
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
* @author Marcel Hibbe
|
||||
* @author Andy Scherzinger
|
||||
* @author Mario Danic
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
@ -46,9 +48,7 @@ import coil.decode.GifDecoder
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.decode.SvgDecoder
|
||||
import coil.memory.MemoryCache
|
||||
import com.facebook.cache.disk.DiskCacheConfig
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.imagepipeline.core.ImagePipelineConfig
|
||||
import coil.util.DebugLogger
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
import com.nextcloud.talk.components.filebrowser.webdav.DavUtils
|
||||
import com.nextcloud.talk.dagger.modules.BusModule
|
||||
@ -64,9 +64,7 @@ import com.nextcloud.talk.jobs.SignalingSettingsWorker
|
||||
import com.nextcloud.talk.ui.theme.ThemeModule
|
||||
import com.nextcloud.talk.utils.ClosedInterfaceImpl
|
||||
import com.nextcloud.talk.utils.DeviceUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.NotificationUtils
|
||||
import com.nextcloud.talk.utils.OkHttpNetworkFetcherWithCache
|
||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageModule
|
||||
import com.nextcloud.talk.utils.database.user.UserModule
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
@ -164,7 +162,6 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
||||
securityKeyManager.init(this, securityKeyConfig)
|
||||
|
||||
initializeWebRtc()
|
||||
DisplayUtils.useCompatVectorIfNeeded()
|
||||
buildComponent()
|
||||
DavUtils.registerCustomFactories()
|
||||
|
||||
@ -174,18 +171,6 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
||||
setAppTheme(appPreferences.theme)
|
||||
super.onCreate()
|
||||
|
||||
val imagePipelineConfig = ImagePipelineConfig.newBuilder(this)
|
||||
.setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
|
||||
.setMainDiskCacheConfig(
|
||||
DiskCacheConfig.newBuilder(this)
|
||||
.setMaxCacheSize(0)
|
||||
.setMaxCacheSizeOnLowDiskSpace(0)
|
||||
.setMaxCacheSizeOnVeryLowDiskSpace(0)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
Fresco.initialize(this, imagePipelineConfig)
|
||||
Security.insertProviderAt(Conscrypt.newProvider(), 1)
|
||||
|
||||
ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync()
|
||||
@ -240,7 +225,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
||||
}
|
||||
|
||||
private fun buildDefaultImageLoader(): ImageLoader {
|
||||
return ImageLoader.Builder(applicationContext)
|
||||
val imageLoaderBuilder = ImageLoader.Builder(applicationContext)
|
||||
.memoryCache {
|
||||
// Use 50% of the application's available memory.
|
||||
MemoryCache.Builder(applicationContext).maxSizePercent(FIFTY_PERCENT).build()
|
||||
@ -254,8 +239,12 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
||||
}
|
||||
add(SvgDecoder.Factory())
|
||||
}
|
||||
.okHttpClient(okHttpClient)
|
||||
.build()
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
imageLoaderBuilder.logger(DebugLogger())
|
||||
}
|
||||
|
||||
return imageLoaderBuilder.build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -27,7 +27,7 @@ import android.text.Editable;
|
||||
import android.text.Spanned;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.facebook.widget.text.span.BetterImageSpan;
|
||||
import third.parties.fresco.BetterImageSpan;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.models.json.mention.Mention;
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @author Marcel Hibbe
|
||||
* @author Andy Scherzinger
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021-2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2021-2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||
@ -37,8 +37,9 @@ import android.content.pm.PackageManager
|
||||
import android.content.res.AssetFileDescriptor
|
||||
import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.MediaPlayer
|
||||
import android.media.MediaRecorder
|
||||
import android.net.Uri
|
||||
@ -78,7 +79,7 @@ import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.emoji.text.EmojiCompat
|
||||
import androidx.emoji.widget.EmojiTextView
|
||||
@ -90,15 +91,13 @@ import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import coil.imageLoader
|
||||
import coil.load
|
||||
import coil.request.ImageRequest
|
||||
import coil.target.Target
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
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.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
@ -134,6 +133,7 @@ import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ControllerChatBinding
|
||||
import com.nextcloud.talk.events.UserMentionClickEvent
|
||||
import com.nextcloud.talk.events.WebSocketCommunicationEvent
|
||||
import com.nextcloud.talk.extensions.loadAvatarOrImagePreview
|
||||
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
||||
import com.nextcloud.talk.jobs.ShareOperationWorker
|
||||
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
||||
@ -165,7 +165,6 @@ import com.nextcloud.talk.utils.ConductorRemapping
|
||||
import com.nextcloud.talk.utils.ConductorRemapping.remapChatController
|
||||
import com.nextcloud.talk.utils.ContactUtils
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.FileUtils
|
||||
import com.nextcloud.talk.utils.ImageEmojiEditText
|
||||
import com.nextcloud.talk.utils.MagicCharPolicy
|
||||
@ -462,42 +461,48 @@ class ChatController(args: Bundle) :
|
||||
|
||||
private fun loadAvatarForStatusBar() {
|
||||
if (isOneToOneConversation() && activity != null) {
|
||||
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
conversationUser?.baseUrl,
|
||||
currentConversation?.name,
|
||||
true
|
||||
),
|
||||
conversationUser!!
|
||||
|
||||
val url = ApiUtils.getUrlForAvatar(
|
||||
conversationUser!!.baseUrl,
|
||||
currentConversation!!.name,
|
||||
true
|
||||
)
|
||||
val target = object : Target {
|
||||
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
||||
private fun setIcon(drawable: Drawable?) {
|
||||
|
||||
dataSource.subscribe(
|
||||
object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(bitmap: Bitmap?) {
|
||||
if (actionBar != null && bitmap != null && resources != null) {
|
||||
val avatarSize = (actionBar?.height!! / TOOLBAR_AVATAR_RATIO).roundToInt()
|
||||
if (avatarSize > 0) {
|
||||
val bitmapResized = Bitmap.createScaledBitmap(bitmap, avatarSize, avatarSize, false)
|
||||
actionBar?.let {
|
||||
val avatarSize = (it.height / TOOLBAR_AVATAR_RATIO).roundToInt()
|
||||
|
||||
val roundedBitmapDrawable =
|
||||
RoundedBitmapDrawableFactory.create(resources!!, bitmapResized)
|
||||
roundedBitmapDrawable.isCircular = true
|
||||
roundedBitmapDrawable.setAntiAlias(true)
|
||||
actionBar?.setIcon(roundedBitmapDrawable)
|
||||
} else {
|
||||
Log.d(TAG, "loadAvatarForStatusBar avatarSize <= 0")
|
||||
}
|
||||
if (drawable != null && avatarSize > 0) {
|
||||
val bitmap = drawable.toBitmap(avatarSize, avatarSize)
|
||||
it.setIcon(BitmapDrawable(resources, bitmap))
|
||||
} else {
|
||||
Log.d(TAG, "loadAvatarForStatusBar avatarSize <= 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
|
||||
// unused atm
|
||||
}
|
||||
},
|
||||
UiThreadImmediateExecutorService.getInstance()
|
||||
override fun onStart(placeholder: Drawable?) {
|
||||
this.setIcon(placeholder)
|
||||
}
|
||||
|
||||
override fun onSuccess(result: Drawable) {
|
||||
this.setIcon(result)
|
||||
}
|
||||
}
|
||||
|
||||
val credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)
|
||||
|
||||
context.imageLoader.enqueue(
|
||||
ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.addHeader("Authorization", credentials)
|
||||
.placeholder(R.drawable.ic_user)
|
||||
.transformations(CircleCropTransformation())
|
||||
.crossfade(true)
|
||||
.target(target)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -617,14 +622,8 @@ class ChatController(args: Bundle) :
|
||||
adapter = TalkMessagesListAdapter(
|
||||
senderId,
|
||||
messageHolders,
|
||||
ImageLoader { imageView, url, payload ->
|
||||
val draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser))
|
||||
.setControllerListener(DisplayUtils.getImageControllerListener(imageView))
|
||||
.setOldController(imageView.controller)
|
||||
.setAutoPlayAnimations(true)
|
||||
.build()
|
||||
imageView.controller = draweeController
|
||||
ImageLoader { imageView, url, _ ->
|
||||
imageView.loadAvatarOrImagePreview(url!!, conversationUser, placeholder = payload as? Drawable)
|
||||
},
|
||||
this
|
||||
)
|
||||
@ -1150,14 +1149,10 @@ class ChatController(args: Bundle) :
|
||||
}
|
||||
|
||||
private fun isRecordAudioPermissionGranted(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return PermissionChecker.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.RECORD_AUDIO
|
||||
) == PermissionChecker.PERMISSION_GRANTED
|
||||
} else {
|
||||
true
|
||||
}
|
||||
return PermissionChecker.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.RECORD_AUDIO
|
||||
) == PermissionChecker.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
private fun startAudioRecording(file: String) {
|
||||
|
@ -28,9 +28,6 @@ package com.nextcloud.talk.controllers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
@ -42,7 +39,6 @@ import android.view.View.VISIBLE
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
@ -53,7 +49,6 @@ import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||
import com.afollestad.materialdialogs.datetime.dateTimePicker
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.adapters.items.ParticipantItem
|
||||
@ -67,6 +62,10 @@ import com.nextcloud.talk.conversation.info.GuestAccessHelper
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ControllerConversationInfoBinding
|
||||
import com.nextcloud.talk.events.EventStatus
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.extensions.loadSystemAvatar
|
||||
import com.nextcloud.talk.extensions.loadGroupCallAvatar
|
||||
import com.nextcloud.talk.extensions.loadPublicCallAvatar
|
||||
import com.nextcloud.talk.jobs.DeleteConversationWorker
|
||||
import com.nextcloud.talk.jobs.LeaveConversationWorker
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
@ -83,7 +82,6 @@ import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DateConstants
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule
|
||||
@ -778,54 +776,16 @@ class ConversationInfoController(args: Bundle) :
|
||||
private fun loadConversationAvatar() {
|
||||
when (conversation!!.type) {
|
||||
Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) {
|
||||
val draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(binding.avatarImage.controller)
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
conversationUser!!.baseUrl,
|
||||
conversation!!.name,
|
||||
true
|
||||
),
|
||||
conversationUser
|
||||
)
|
||||
)
|
||||
.build()
|
||||
binding.avatarImage.controller = draweeController
|
||||
conversation!!.name?.let { binding.avatarImage.loadAvatar(conversationUser!!, it) }
|
||||
}
|
||||
Conversation.ConversationType.ROOM_GROUP_CALL -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
binding.avatarImage.hierarchy.setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(binding.avatarImage, R.drawable.ic_avatar_group)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding.avatarImage.hierarchy.setPlaceholderImage(
|
||||
R.drawable.ic_circular_group
|
||||
)
|
||||
}
|
||||
binding.avatarImage.loadGroupCallAvatar(viewThemeUtils)
|
||||
}
|
||||
Conversation.ConversationType.ROOM_PUBLIC_CALL -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
binding.avatarImage.hierarchy.setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(binding.avatarImage, R.drawable.ic_avatar_link)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding.avatarImage.hierarchy.setPlaceholderImage(
|
||||
R.drawable.ic_circular_link
|
||||
)
|
||||
}
|
||||
binding.avatarImage.loadPublicCallAvatar(viewThemeUtils)
|
||||
}
|
||||
Conversation.ConversationType.ROOM_SYSTEM -> {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
binding.avatarImage.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable))
|
||||
binding.avatarImage.loadSystemAvatar()
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
@ -31,7 +31,7 @@ import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@ -49,8 +49,6 @@ import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.view.MenuItemCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -58,15 +56,13 @@ import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.target.Target
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
||||
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.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.nextcloud.talk.R
|
||||
@ -104,7 +100,6 @@ import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ClosedInterfaceImpl
|
||||
import com.nextcloud.talk.utils.ConductorRemapping.remapChatController
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.FileUtils
|
||||
import com.nextcloud.talk.utils.Mimetype
|
||||
import com.nextcloud.talk.utils.ParticipantPermissions
|
||||
@ -211,78 +206,56 @@ class ConversationsListController(bundle: Bundle) :
|
||||
prepareViews()
|
||||
}
|
||||
|
||||
private fun loadUserAvatar(button: MaterialButton) {
|
||||
if (activity != null) {
|
||||
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
currentUser!!.baseUrl,
|
||||
currentUser!!.userId,
|
||||
true
|
||||
),
|
||||
currentUser
|
||||
)
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
||||
dataSource.subscribe(
|
||||
object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(bitmap: Bitmap?) {
|
||||
if (bitmap != null && resources != null) {
|
||||
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(
|
||||
resources!!,
|
||||
bitmap
|
||||
)
|
||||
roundedBitmapDrawable.isCircular = true
|
||||
roundedBitmapDrawable.setAntiAlias(true)
|
||||
button.icon = roundedBitmapDrawable
|
||||
}
|
||||
}
|
||||
private fun loadUserAvatar(
|
||||
target: Target
|
||||
) {
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage?>>) {
|
||||
if (resources != null) {
|
||||
button.icon = ResourcesCompat.getDrawable(resources!!, R.drawable.ic_user, null)
|
||||
}
|
||||
}
|
||||
},
|
||||
UiThreadImmediateExecutorService.getInstance()
|
||||
if (activity != null) {
|
||||
val url = ApiUtils.getUrlForAvatar(
|
||||
currentUser!!.baseUrl,
|
||||
currentUser!!.userId,
|
||||
true
|
||||
)
|
||||
|
||||
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
|
||||
|
||||
context.imageLoader.enqueue(
|
||||
ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.addHeader("Authorization", credentials)
|
||||
.placeholder(R.drawable.ic_user)
|
||||
.transformations(CircleCropTransformation())
|
||||
.crossfade(true)
|
||||
.target(target)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadUserAvatar(menuItem: MenuItem) {
|
||||
if (activity != null) {
|
||||
val imageRequest = DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
currentUser!!.baseUrl,
|
||||
currentUser!!.userId,
|
||||
true
|
||||
),
|
||||
currentUser
|
||||
)
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
|
||||
dataSource.subscribe(
|
||||
object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(bitmap: Bitmap?) {
|
||||
if (bitmap != null && resources != null) {
|
||||
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(
|
||||
resources!!,
|
||||
bitmap
|
||||
)
|
||||
roundedBitmapDrawable.isCircular = true
|
||||
roundedBitmapDrawable.setAntiAlias(true)
|
||||
menuItem.icon = roundedBitmapDrawable
|
||||
}
|
||||
}
|
||||
private fun loadUserAvatar(button: MaterialButton) {
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage?>>) {
|
||||
if (resources != null) {
|
||||
menuItem.icon = ResourcesCompat.getDrawable(resources!!, R.drawable.ic_user, null)
|
||||
}
|
||||
}
|
||||
},
|
||||
UiThreadImmediateExecutorService.getInstance()
|
||||
)
|
||||
val target = object : Target {
|
||||
override fun onStart(placeholder: Drawable?) {
|
||||
button.icon = placeholder
|
||||
}
|
||||
override fun onSuccess(result: Drawable) {
|
||||
button.icon = result
|
||||
}
|
||||
}
|
||||
|
||||
loadUserAvatar(target)
|
||||
}
|
||||
|
||||
private fun loadUserAvatar(menuItem: MenuItem) {
|
||||
val target = object : Target {
|
||||
override fun onStart(placeholder: Drawable?) {
|
||||
menuItem.icon = placeholder
|
||||
}
|
||||
override fun onSuccess(result: Drawable) {
|
||||
menuItem.icon = result
|
||||
}
|
||||
}
|
||||
loadUserAvatar(target)
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
@ -585,15 +558,15 @@ class ConversationsListController(bundle: Bundle) :
|
||||
if (activity != null) {
|
||||
val conversationItem = ConversationItem(
|
||||
conversation,
|
||||
currentUser,
|
||||
activity,
|
||||
currentUser!!,
|
||||
activity!!,
|
||||
viewThemeUtils
|
||||
)
|
||||
conversationItems.add(conversationItem)
|
||||
val conversationItemWithHeader = ConversationItem(
|
||||
conversation,
|
||||
currentUser,
|
||||
activity,
|
||||
currentUser!!,
|
||||
activity!!,
|
||||
callHeaderItems[headerTitle],
|
||||
viewThemeUtils
|
||||
)
|
||||
@ -651,8 +624,8 @@ class ConversationsListController(bundle: Bundle) :
|
||||
}
|
||||
val conversationItem = ConversationItem(
|
||||
conversation,
|
||||
currentUser,
|
||||
activity,
|
||||
currentUser!!,
|
||||
activity!!,
|
||||
callHeaderItems[headerTitle],
|
||||
viewThemeUtils
|
||||
)
|
||||
|
@ -442,23 +442,19 @@ class LocationPickerController(args: Bundle) :
|
||||
private fun isLocationPermissionsGranted(): Boolean {
|
||||
fun isCoarseLocationGranted(): Boolean {
|
||||
return PermissionChecker.checkSelfPermission(
|
||||
context!!,
|
||||
context,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
) == PermissionChecker.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun isFineLocationGranted(): Boolean {
|
||||
return PermissionChecker.checkSelfPermission(
|
||||
context!!,
|
||||
context,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) == PermissionChecker.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
isCoarseLocationGranted() && isFineLocationGranted()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
return isCoarseLocationGranted() && isFineLocationGranted()
|
||||
}
|
||||
|
||||
private fun requestLocationPermissions() {
|
||||
|
@ -25,12 +25,10 @@ import android.app.Activity
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.BiometricPrompt.PromptInfo
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
@ -62,15 +60,11 @@ class LockedController : BaseController(R.layout.controller_locked) {
|
||||
override fun onViewBound(view: View) {
|
||||
super.onViewBound(view)
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
binding.unlockContainer.setOnClickListener {
|
||||
unlock()
|
||||
}
|
||||
binding.unlockContainer.setOnClickListener {
|
||||
unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
Log.d(TAG, "onAttach")
|
||||
@ -92,12 +86,10 @@ class LockedController : BaseController(R.layout.controller_locked) {
|
||||
Log.d(TAG, "onDetach")
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
fun unlock() {
|
||||
checkIfWeAreSecure()
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private fun showBiometricDialog() {
|
||||
val context: Context? = activity
|
||||
if (context != null) {
|
||||
@ -140,11 +132,10 @@ class LockedController : BaseController(R.layout.controller_locked) {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private fun checkIfWeAreSecure() {
|
||||
val keyguardManager = activity?.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
|
||||
if (keyguardManager?.isKeyguardSecure == true && appPreferences!!.isScreenLocked) {
|
||||
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences!!.screenLockTimeout)) {
|
||||
if (keyguardManager?.isKeyguardSecure == true && appPreferences.isScreenLocked) {
|
||||
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) {
|
||||
Log.d(TAG, "showBiometricDialog because 'we are NOT authenticated'...")
|
||||
showBiometricDialog()
|
||||
} else {
|
||||
@ -172,8 +163,7 @@ class LockedController : BaseController(R.layout.controller_locked) {
|
||||
if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
SecurityUtils.checkIfWeAreAuthenticated(appPreferences!!.screenLockTimeout)
|
||||
SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)
|
||||
) {
|
||||
Log.d(TAG, "All went well, dismiss locked controller")
|
||||
router.popCurrentController()
|
||||
|
@ -157,18 +157,13 @@ class SettingsController : BaseController(R.layout.controller_settings) {
|
||||
binding.settingsIncognitoKeyboard.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
binding.settingsScreenLock.visibility = View.GONE
|
||||
binding.settingsScreenLockTimeout.visibility = View.GONE
|
||||
} else {
|
||||
binding.settingsScreenLock.setSummary(
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
resources!!.getString(R.string.nc_settings_screen_lock_desc),
|
||||
resources!!.getString(R.string.nc_app_product_name)
|
||||
)
|
||||
binding.settingsScreenLock.setSummary(
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
resources!!.getString(R.string.nc_settings_screen_lock_desc),
|
||||
resources!!.getString(R.string.nc_app_product_name)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
setupPrivacyUrl()
|
||||
setupSourceCodeUrl()
|
||||
@ -662,10 +657,8 @@ class SettingsController : BaseController(R.layout.controller_settings) {
|
||||
appPreferences.isKeyboardIncognito
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
(binding.settingsIncognitoKeyboard.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
|
||||
appPreferences.isKeyboardIncognito
|
||||
}
|
||||
(binding.settingsIncognitoKeyboard.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
|
||||
appPreferences.isKeyboardIncognito
|
||||
|
||||
if (CapabilitiesUtilNew.isReadStatusAvailable(userManager.currentUser.blockingGet())) {
|
||||
(binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
|
||||
@ -679,29 +672,27 @@ class SettingsController : BaseController(R.layout.controller_settings) {
|
||||
}
|
||||
|
||||
private fun setupScreenLockSetting() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
if (keyguardManager.isKeyguardSecure) {
|
||||
binding.settingsScreenLock.isEnabled = true
|
||||
binding.settingsScreenLockTimeout.isEnabled = true
|
||||
(binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
|
||||
appPreferences.isScreenLocked
|
||||
binding.settingsScreenLockTimeout.isEnabled = appPreferences.isScreenLocked
|
||||
if (appPreferences.isScreenLocked) {
|
||||
binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
|
||||
} else {
|
||||
binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
|
||||
}
|
||||
binding.settingsScreenLock.alpha = ENABLED_ALPHA
|
||||
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
if (keyguardManager.isKeyguardSecure) {
|
||||
binding.settingsScreenLock.isEnabled = true
|
||||
binding.settingsScreenLockTimeout.isEnabled = true
|
||||
(binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
|
||||
appPreferences.isScreenLocked
|
||||
binding.settingsScreenLockTimeout.isEnabled = appPreferences.isScreenLocked
|
||||
if (appPreferences.isScreenLocked) {
|
||||
binding.settingsScreenLockTimeout.alpha = ENABLED_ALPHA
|
||||
} else {
|
||||
binding.settingsScreenLock.isEnabled = false
|
||||
binding.settingsScreenLockTimeout.isEnabled = false
|
||||
appPreferences.removeScreenLock()
|
||||
appPreferences.removeScreenLockTimeout()
|
||||
(binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked = false
|
||||
binding.settingsScreenLock.alpha = DISABLED_ALPHA
|
||||
binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
|
||||
}
|
||||
binding.settingsScreenLock.alpha = ENABLED_ALPHA
|
||||
} else {
|
||||
binding.settingsScreenLock.isEnabled = false
|
||||
binding.settingsScreenLockTimeout.isEnabled = false
|
||||
appPreferences.removeScreenLock()
|
||||
appPreferences.removeScreenLockTimeout()
|
||||
(binding.settingsScreenLock.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked = false
|
||||
binding.settingsScreenLock.alpha = DISABLED_ALPHA
|
||||
binding.settingsScreenLockTimeout.alpha = DISABLED_ALPHA
|
||||
}
|
||||
}
|
||||
|
||||
@ -805,9 +796,7 @@ class SettingsController : BaseController(R.layout.controller_settings) {
|
||||
|
||||
private inner class ScreenLockTimeoutListener : OnPreferenceValueChangedListener<String?> {
|
||||
override fun onChanged(newValue: String?) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
SecurityUtils.createKey(appPreferences.screenLockTimeout)
|
||||
}
|
||||
SecurityUtils.createKey(appPreferences.screenLockTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2022 Nextcloud GmbH
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package com.nextcloud.talk.extensions
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import coil.annotation.ExperimentalCoilApi
|
||||
import coil.imageLoader
|
||||
import coil.load
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.SuccessResult
|
||||
import coil.result
|
||||
import coil.transform.CircleCropTransformation
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
|
||||
private const val ROUNDING_PIXEL = 16f
|
||||
private const val TAG = "ImageViewExtensions"
|
||||
|
||||
fun ImageView.loadAvatar(
|
||||
user: User,
|
||||
avatar: String,
|
||||
requestBigSize: Boolean = true
|
||||
): io.reactivex.disposables
|
||||
.Disposable {
|
||||
|
||||
val imageRequestUri = ApiUtils.getUrlForAvatar(
|
||||
user.baseUrl,
|
||||
avatar,
|
||||
requestBigSize
|
||||
)
|
||||
|
||||
return loadAvatarInternal(user, imageRequestUri, false)
|
||||
}
|
||||
|
||||
fun ImageView.replaceAvatar(
|
||||
user: User,
|
||||
avatar: String,
|
||||
requestBigSize: Boolean = true
|
||||
): io.reactivex.disposables
|
||||
.Disposable {
|
||||
|
||||
val imageRequestUri = ApiUtils.getUrlForAvatar(
|
||||
user.baseUrl,
|
||||
avatar,
|
||||
requestBigSize
|
||||
)
|
||||
|
||||
return loadAvatarInternal(user, imageRequestUri, true)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoilApi::class)
|
||||
private fun ImageView.loadAvatarInternal(
|
||||
user: User?,
|
||||
url: String,
|
||||
replace: Boolean
|
||||
): io.reactivex.disposables
|
||||
.Disposable {
|
||||
|
||||
if (replace && this.result is SuccessResult) {
|
||||
val result = this.result as SuccessResult
|
||||
val memoryCacheKey = result.memoryCacheKey
|
||||
val memoryCache = context.imageLoader.memoryCache
|
||||
memoryCacheKey?.let { memoryCache?.remove(it) }
|
||||
|
||||
val diskCacheKey = result.diskCacheKey
|
||||
val diskCache = context.imageLoader.diskCache
|
||||
diskCacheKey?.let { diskCache?.remove(it) }
|
||||
}
|
||||
|
||||
return DisposableWrapper(
|
||||
load(url) {
|
||||
user?.let {
|
||||
addHeader(
|
||||
"Authorization",
|
||||
ApiUtils.getCredentials(user.username, user.token)
|
||||
)
|
||||
}
|
||||
transformations(CircleCropTransformation())
|
||||
placeholder(R.drawable.account_circle_96dp)
|
||||
listener(onError = { _, result ->
|
||||
Log.w(TAG, "Can't load avatar with URL: $url", result.throwable)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("Use function loadAvatar", level = DeprecationLevel.WARNING)
|
||||
fun ImageView.loadAvatarWithUrl(user: User? = null, url: String): io.reactivex.disposables.Disposable {
|
||||
return loadAvatarInternal(user, url, false)
|
||||
}
|
||||
|
||||
fun ImageView.loadThumbnail(url: String, user: User): io.reactivex.disposables.Disposable {
|
||||
val requestBuilder = ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.target(this)
|
||||
.transformations(CircleCropTransformation())
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
|
||||
requestBuilder.placeholder(LayerDrawable(layers))
|
||||
} else {
|
||||
requestBuilder.placeholder(R.mipmap.ic_launcher)
|
||||
}
|
||||
|
||||
if (url.startsWith(user.baseUrl!!) &&
|
||||
(url.contains("index.php/core/preview?fileId=") || url.contains("/avatar/"))
|
||||
) {
|
||||
requestBuilder.addHeader(
|
||||
"Authorization",
|
||||
ApiUtils.getCredentials(user.username, user.token)
|
||||
)
|
||||
}
|
||||
|
||||
return DisposableWrapper(context.imageLoader.enqueue(requestBuilder.build()))
|
||||
}
|
||||
|
||||
fun ImageView.loadImage(url: String, user: User, placeholder: Drawable? = null): io.reactivex.disposables.Disposable {
|
||||
|
||||
val requestBuilder = ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.target(this)
|
||||
.placeholder(placeholder)
|
||||
.error(placeholder)
|
||||
.transformations(RoundedCornersTransformation(ROUNDING_PIXEL, ROUNDING_PIXEL, ROUNDING_PIXEL, ROUNDING_PIXEL))
|
||||
|
||||
if (url.startsWith(user.baseUrl!!) &&
|
||||
(url.contains("index.php/core/preview?fileId=") || url.contains("/avatar/"))
|
||||
) {
|
||||
requestBuilder.addHeader(
|
||||
"Authorization",
|
||||
ApiUtils.getCredentials(user.username, user.token)
|
||||
)
|
||||
}
|
||||
|
||||
return DisposableWrapper(context.imageLoader.enqueue(requestBuilder.build()))
|
||||
}
|
||||
|
||||
fun ImageView.loadAvatarOrImagePreview(url: String, user: User, placeholder: Drawable? = null): io.reactivex
|
||||
.disposables.Disposable {
|
||||
return if (url.contains("/avatar/")) {
|
||||
loadAvatarInternal(user, url, false)
|
||||
} else {
|
||||
loadImage(url, user, placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
fun ImageView.loadAvatar(any: Any?): io.reactivex.disposables.Disposable {
|
||||
return DisposableWrapper(
|
||||
load(any) {
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun ImageView.loadSystemAvatar(): io.reactivex.disposables.Disposable {
|
||||
val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
|
||||
val layerDrawable = LayerDrawable(layers)
|
||||
layerDrawable
|
||||
} else {
|
||||
R.mipmap.ic_launcher
|
||||
}
|
||||
|
||||
return DisposableWrapper(
|
||||
load(data) {
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun ImageView.loadChangelogBotAvatar(): io.reactivex.disposables.Disposable {
|
||||
return loadSystemAvatar()
|
||||
}
|
||||
|
||||
fun ImageView.loadBotsAvatar(): io.reactivex.disposables.Disposable {
|
||||
return loadAvatar(
|
||||
TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(
|
||||
">",
|
||||
ResourcesCompat.getColor(context.resources, R.color.black, null)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun ImageView.loadGroupCallAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable {
|
||||
|
||||
val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_group) as Any
|
||||
} else {
|
||||
R.drawable.ic_circular_group
|
||||
}
|
||||
return loadAvatar(data)
|
||||
}
|
||||
|
||||
fun ImageView.loadPublicCallAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable {
|
||||
val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_link) as Any
|
||||
} else {
|
||||
R.drawable.ic_circular_link
|
||||
}
|
||||
return loadAvatar(data)
|
||||
}
|
||||
|
||||
fun ImageView.loadMailAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable {
|
||||
val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_mail) as Any
|
||||
} else {
|
||||
R.drawable.ic_circular_mail
|
||||
}
|
||||
return loadAvatar(data)
|
||||
}
|
||||
|
||||
fun ImageView.loadGuestAvatar(user: User, name: String, big: Boolean): io.reactivex.disposables.Disposable {
|
||||
return loadGuestAvatar(user.baseUrl!!, name, big)
|
||||
}
|
||||
|
||||
fun ImageView.loadGuestAvatar(baseUrl: String, name: String, big: Boolean): io.reactivex.disposables.Disposable {
|
||||
val imageRequestUri = ApiUtils.getUrlForGuestAvatar(
|
||||
baseUrl,
|
||||
name,
|
||||
big
|
||||
)
|
||||
return DisposableWrapper(
|
||||
load(imageRequestUri) {
|
||||
transformations(CircleCropTransformation())
|
||||
listener(onError = { _, result ->
|
||||
Log.w(TAG, "Can't load guest avatar with URL: $imageRequestUri", result.throwable)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private class DisposableWrapper(private val disposable: coil.request.Disposable) : io.reactivex.disposables
|
||||
.Disposable {
|
||||
|
||||
override fun dispose() {
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
override fun isDisposed(): Boolean {
|
||||
return disposable.isDisposed
|
||||
}
|
||||
}
|
@ -447,11 +447,9 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
EmojiCompat.get().process(pushMessage.text!!)
|
||||
)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
// This method should exist since API 21, but some phones don't have it
|
||||
// So as a safeguard, we don't use it until 23
|
||||
notificationBuilder.color = context!!.resources.getColor(R.color.colorPrimary)
|
||||
}
|
||||
|
||||
notificationBuilder.color = context!!.resources.getColor(R.color.colorPrimary)
|
||||
|
||||
val notificationInfoBundle = Bundle()
|
||||
notificationInfoBundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!)
|
||||
// could be an ID or a TOKEN
|
||||
@ -526,7 +524,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
notificationUser.id,
|
||||
false
|
||||
) else ApiUtils.getUrlForGuestAvatar(baseUrl, notificationUser.name, false)
|
||||
person.setIcon(loadAvatarSync(avatarUrl))
|
||||
person.setIcon(loadAvatarSync(avatarUrl, context!!))
|
||||
}
|
||||
notificationBuilder.setStyle(getStyle(person.build(), style))
|
||||
}
|
||||
@ -694,7 +692,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
.subscribe(object : Observer<ParticipantsOverall> {
|
||||
override fun onSubscribe(d: Disposable) = Unit
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun onNext(participantsOverall: ParticipantsOverall) {
|
||||
val participantList: List<Participant> = participantsOverall.ocs!!.data!!
|
||||
hasParticipantsInCall = participantList.isNotEmpty()
|
||||
@ -726,7 +723,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
Log.e(TAG, "Error in getPeersForCall", e)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun onComplete() {
|
||||
|
||||
if (isCallNotificationVisible) {
|
||||
@ -821,7 +817,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
return PendingIntent.getActivity(context, requestCode, intent, intentFlag)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
private fun isCallNotificationVisible(decryptedPushMessage: DecryptedPushMessage): Boolean {
|
||||
var isVisible = false
|
||||
|
||||
|
@ -295,7 +295,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
false
|
||||
}
|
||||
}
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
|
||||
else -> {
|
||||
if (PermissionChecker.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
@ -308,10 +308,6 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
false
|
||||
}
|
||||
}
|
||||
else -> { // permission is automatically granted on sdk<23 upon installation
|
||||
Log.d(TAG, "Permission is granted")
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,7 +321,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
REQUEST_PERMISSION
|
||||
)
|
||||
}
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
|
||||
else -> {
|
||||
controller.requestPermissions(
|
||||
arrayOf(
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
@ -333,8 +329,6 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
REQUEST_PERMISSION
|
||||
)
|
||||
}
|
||||
else -> { // permission is automatically granted on sdk<23 upon installation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,16 +22,15 @@ package com.nextcloud.talk.polls.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.TextUtils
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import android.widget.ImageView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.PollResultVoterItemBinding
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.extensions.loadGuestAvatar
|
||||
import com.nextcloud.talk.polls.model.PollDetails
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
|
||||
class PollResultVoterViewHolder(
|
||||
private val user: User,
|
||||
@ -46,45 +45,19 @@ class PollResultVoterViewHolder(
|
||||
binding.root.setOnClickListener { clickListener.onClick() }
|
||||
|
||||
binding.pollVoterName.text = item.details.actorDisplayName
|
||||
binding.pollVoterAvatar.controller = getAvatarDraweeController(item.details)
|
||||
loadAvatar(item.details, binding.pollVoterAvatar)
|
||||
viewThemeUtils.dialog.colorDialogSupportingText(binding.pollVoterName)
|
||||
}
|
||||
|
||||
private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? {
|
||||
var draweeController: DraweeController? = null
|
||||
private fun loadAvatar(pollDetail: PollDetails, avatar: ImageView) {
|
||||
if (pollDetail.actorType == "guests") {
|
||||
var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest)
|
||||
if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) {
|
||||
displayName = pollDetail.actorDisplayName!!
|
||||
}
|
||||
draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForGuestAvatar(
|
||||
user.baseUrl,
|
||||
displayName,
|
||||
false
|
||||
),
|
||||
user
|
||||
)
|
||||
)
|
||||
.build()
|
||||
avatar.loadGuestAvatar(user, displayName!!, false)
|
||||
} else if (pollDetail.actorType == "users") {
|
||||
draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
user.baseUrl,
|
||||
pollDetail.actorId,
|
||||
false
|
||||
),
|
||||
user
|
||||
)
|
||||
)
|
||||
.build()
|
||||
avatar.loadAvatar(user, pollDetail.actorId!!, false)
|
||||
}
|
||||
return draweeController
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -22,20 +24,16 @@ package com.nextcloud.talk.polls.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.TextUtils
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.generic.RoundingParams
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.PollResultVotersOverviewItemBinding
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.extensions.loadGuestAvatar
|
||||
import com.nextcloud.talk.polls.model.PollDetails
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
|
||||
class PollResultVotersOverviewViewHolder(
|
||||
private val user: User,
|
||||
@ -61,24 +59,14 @@ class PollResultVotersOverviewViewHolder(
|
||||
|
||||
for (i in 0 until avatarsToDisplay) {
|
||||
val pollDetails = item.detailsList[i]
|
||||
val avatar = SimpleDraweeView(binding.root.context)
|
||||
val avatar = ImageView(binding.root.context)
|
||||
|
||||
layoutParams.marginStart = i * AVATAR_OFFSET
|
||||
avatar.layoutParams = layoutParams
|
||||
|
||||
avatar.translationZ = i.toFloat() * -1
|
||||
|
||||
val roundingParams = RoundingParams.fromCornersRadius(AVATAR_RADIUS)
|
||||
roundingParams.roundAsCircle = true
|
||||
roundingParams.borderColor = ResourcesCompat.getColor(
|
||||
itemView.context.resources!!,
|
||||
R.color.vote_dialog_background,
|
||||
null
|
||||
)
|
||||
roundingParams.borderWidth = DisplayUtils.convertDpToPixel(2.0f, itemView.context)
|
||||
|
||||
avatar.hierarchy.roundingParams = roundingParams
|
||||
avatar.controller = getAvatarDraweeController(pollDetails)
|
||||
loadAvatar(pollDetails, avatar)
|
||||
|
||||
binding.votersAvatarsOverviewWrapper.addView(avatar)
|
||||
|
||||
@ -92,47 +80,20 @@ class PollResultVotersOverviewViewHolder(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? {
|
||||
var draweeController: DraweeController? = null
|
||||
private fun loadAvatar(pollDetail: PollDetails, avatar: ImageView) {
|
||||
if (pollDetail.actorType == "guests") {
|
||||
var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest)
|
||||
if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) {
|
||||
displayName = pollDetail.actorDisplayName!!
|
||||
}
|
||||
draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForGuestAvatar(
|
||||
user.baseUrl,
|
||||
displayName,
|
||||
false
|
||||
),
|
||||
user
|
||||
)
|
||||
)
|
||||
.build()
|
||||
avatar.loadGuestAvatar(user, displayName!!, false)
|
||||
} else if (pollDetail.actorType == "users") {
|
||||
draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
user.baseUrl,
|
||||
pollDetail.actorId,
|
||||
false
|
||||
),
|
||||
user
|
||||
)
|
||||
)
|
||||
.build()
|
||||
avatar.loadAvatar(user, pollDetail.actorId!!, false)
|
||||
}
|
||||
return draweeController
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val AVATAR_SIZE = 60
|
||||
const val AVATAR_RADIUS = 5f
|
||||
const val MAX_AVATARS = 10
|
||||
const val AVATAR_OFFSET = AVATAR_SIZE - 20
|
||||
const val DOTS_OFFSET = 70
|
||||
|
@ -47,6 +47,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
@ -159,19 +160,25 @@ class DirectReplyReceiver : BroadcastReceiver() {
|
||||
.extractMessagingStyleFromNotification(previousNotification)
|
||||
|
||||
// Add reply
|
||||
val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false)
|
||||
val me = Person.Builder()
|
||||
.setName(currentUser.displayName)
|
||||
.setIcon(NotificationUtils.loadAvatarSync(avatarUrl))
|
||||
.build()
|
||||
val message = NotificationCompat.MessagingStyle.Message(reply, System.currentTimeMillis(), me)
|
||||
previousStyle?.addMessage(message)
|
||||
Single.fromCallable {
|
||||
val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false)
|
||||
val me = Person.Builder()
|
||||
.setName(currentUser.displayName)
|
||||
.setIcon(NotificationUtils.loadAvatarSync(avatarUrl, context))
|
||||
.build()
|
||||
val message = NotificationCompat.MessagingStyle.Message(reply, System.currentTimeMillis(), me)
|
||||
previousStyle?.addMessage(message)
|
||||
|
||||
// Set the updated style
|
||||
previousBuilder.setStyle(previousStyle)
|
||||
// Set the updated style
|
||||
previousBuilder.setStyle(previousStyle)
|
||||
|
||||
// Update the active notification.
|
||||
NotificationManagerCompat.from(context).notify(systemNotificationId!!, previousBuilder.build())
|
||||
// Check if notification still exists
|
||||
if (findActiveNotification(systemNotificationId!!) != null) {
|
||||
NotificationManagerCompat.from(context).notify(systemNotificationId!!, previousBuilder.build())
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -22,20 +22,18 @@ package com.nextcloud.talk.remotefilebrowser.adapters
|
||||
|
||||
import android.text.format.Formatter
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import autodagger.AutoInjector
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.RvItemBrowserFileBinding
|
||||
import com.nextcloud.talk.extensions.loadImage
|
||||
import com.nextcloud.talk.remotefilebrowser.SelectionInterface
|
||||
import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DateUtils.getLocalDateTimeStringFromTimestamp
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.Mimetype.FOLDER
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
@ -48,7 +46,7 @@ class RemoteFileBrowserItemsListViewHolder(
|
||||
onItemClicked: (Int) -> Unit
|
||||
) : RemoteFileBrowserItemsViewHolder(binding, mimeTypeSelectionFilter, currentUser, selectionInterface) {
|
||||
|
||||
override val fileIcon: SimpleDraweeView
|
||||
override val fileIcon: ImageView
|
||||
get() = binding.fileIcon
|
||||
|
||||
private var selectable: Boolean = true
|
||||
@ -68,7 +66,6 @@ class RemoteFileBrowserItemsListViewHolder(
|
||||
override fun onBind(item: RemoteFileBrowserItem) {
|
||||
super.onBind(item)
|
||||
|
||||
binding.fileIcon.controller = null
|
||||
if (!item.isAllowedToReShare || item.isEncrypted) {
|
||||
binding.root.isEnabled = false
|
||||
binding.root.alpha = DISABLED_ALPHA
|
||||
@ -95,11 +92,7 @@ class RemoteFileBrowserItemsListViewHolder(
|
||||
calculateClickability(item, selectable)
|
||||
setSelectability()
|
||||
|
||||
binding.fileIcon
|
||||
.hierarchy
|
||||
.setPlaceholderImage(
|
||||
viewThemeUtils.talk.getPlaceholderImage(binding.root.context, item.mimeType)
|
||||
)
|
||||
val placeholder = viewThemeUtils.talk.getPlaceholderImage(binding.root.context, item.mimeType)
|
||||
|
||||
if (item.hasPreview) {
|
||||
val path = ApiUtils.getUrlForFilePreviewWithRemotePath(
|
||||
@ -108,12 +101,10 @@ class RemoteFileBrowserItemsListViewHolder(
|
||||
binding.fileIcon.context.resources.getDimensionPixelSize(R.dimen.small_item_height)
|
||||
)
|
||||
if (path.isNotEmpty()) {
|
||||
val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(path))
|
||||
.build()
|
||||
binding.fileIcon.controller = draweeController
|
||||
binding.fileIcon.loadImage(path, currentUser, placeholder)
|
||||
}
|
||||
} else {
|
||||
binding.fileIcon.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
binding.filenameTextView.text = item.displayName
|
||||
|
@ -20,11 +20,9 @@
|
||||
|
||||
package com.nextcloud.talk.remotefilebrowser.adapters
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat
|
||||
import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.remotefilebrowser.SelectionInterface
|
||||
import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem
|
||||
@ -37,17 +35,9 @@ abstract class RemoteFileBrowserItemsViewHolder(
|
||||
val selectionInterface: SelectionInterface,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
abstract val fileIcon: SimpleDraweeView
|
||||
abstract val fileIcon: ImageView
|
||||
|
||||
open fun onBind(item: RemoteFileBrowserItem) {
|
||||
fileIcon.hierarchy.setPlaceholderImage(staticImage(item.mimeType, fileIcon))
|
||||
}
|
||||
|
||||
private fun staticImage(
|
||||
mimeType: String?,
|
||||
image: SimpleDraweeView
|
||||
): Drawable {
|
||||
val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimeType)
|
||||
return ContextCompat.getDrawable(image.context, drawableResourceId)!!
|
||||
fileIcon.setImageResource(DrawableUtils.getDrawableResourceIdForMimeType(item.mimeType))
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
package com.nextcloud.talk.shareditems.adapters
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.SharedItemGridBinding
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
@ -35,7 +35,7 @@ class SharedItemsGridViewHolder(
|
||||
viewThemeUtils: ViewThemeUtils
|
||||
) : SharedItemsViewHolder(binding, user, viewThemeUtils) {
|
||||
|
||||
override val image: SimpleDraweeView
|
||||
override val image: ImageView
|
||||
get() = binding.image
|
||||
override val clickTarget: View
|
||||
get() = binding.image
|
||||
|
@ -26,9 +26,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.format.Formatter
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import coil.load
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.SharedItemListBinding
|
||||
@ -46,7 +47,7 @@ class SharedItemsListViewHolder(
|
||||
viewThemeUtils: ViewThemeUtils
|
||||
) : SharedItemsViewHolder(binding, user, viewThemeUtils) {
|
||||
|
||||
override val image: SimpleDraweeView
|
||||
override val image: ImageView
|
||||
get() = binding.fileImage
|
||||
override val clickTarget: View
|
||||
get() = binding.fileItem
|
||||
@ -75,7 +76,7 @@ class SharedItemsListViewHolder(
|
||||
binding.separator1.visibility = View.GONE
|
||||
binding.fileDate.text = item.dateTime
|
||||
binding.actor.text = item.actorName
|
||||
image.hierarchy.setPlaceholderImage(R.drawable.ic_baseline_bar_chart_24)
|
||||
image.load(R.drawable.ic_baseline_bar_chart_24)
|
||||
image.setColorFilter(
|
||||
ContextCompat.getColor(image.context, R.color.high_emphasis_menu_icon),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
@ -93,7 +94,7 @@ class SharedItemsListViewHolder(
|
||||
binding.separator1.visibility = View.GONE
|
||||
binding.fileDate.text = item.dateTime
|
||||
binding.actor.text = item.actorName
|
||||
image.hierarchy.setPlaceholderImage(R.drawable.ic_baseline_location_on_24)
|
||||
image.load(R.drawable.ic_baseline_location_on_24)
|
||||
image.setColorFilter(
|
||||
ContextCompat.getColor(image.context, R.color.high_emphasis_menu_icon),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
@ -114,7 +115,7 @@ class SharedItemsListViewHolder(
|
||||
binding.separator1.visibility = View.GONE
|
||||
binding.fileDate.text = item.dateTime
|
||||
binding.actor.text = item.actorName
|
||||
image.hierarchy.setPlaceholderImage(R.drawable.ic_mimetype_file)
|
||||
image.load(R.drawable.ic_mimetype_file)
|
||||
image.setColorFilter(
|
||||
ContextCompat.getColor(image.context, R.color.high_emphasis_menu_icon),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
@ -129,7 +130,7 @@ class SharedItemsListViewHolder(
|
||||
binding.separator1.visibility = View.GONE
|
||||
binding.fileDate.text = item.dateTime
|
||||
binding.actor.text = item.actorName
|
||||
image.hierarchy.setPlaceholderImage(R.drawable.ic_baseline_deck_24)
|
||||
image.load(R.drawable.ic_baseline_deck_24)
|
||||
image.setColorFilter(
|
||||
ContextCompat.getColor(image.context, R.color.high_emphasis_menu_icon),
|
||||
android.graphics.PorterDuff.Mode.SRC_IN
|
||||
|
@ -23,29 +23,20 @@
|
||||
package com.nextcloud.talk.shareditems.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.controller.BaseControllerListener
|
||||
import com.facebook.drawee.controller.ControllerListener
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.facebook.imagepipeline.common.RotationOptions
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.extensions.loadImage
|
||||
import com.nextcloud.talk.shareditems.model.SharedDeckCardItem
|
||||
import com.nextcloud.talk.shareditems.model.SharedFileItem
|
||||
import com.nextcloud.talk.shareditems.model.SharedItem
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.shareditems.model.SharedLocationItem
|
||||
import com.nextcloud.talk.shareditems.model.SharedOtherItem
|
||||
import com.nextcloud.talk.shareditems.model.SharedPollItem
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.FileViewerUtils
|
||||
|
||||
abstract class SharedItemsViewHolder(
|
||||
@ -58,21 +49,21 @@ abstract class SharedItemsViewHolder(
|
||||
private val TAG = SharedItemsViewHolder::class.simpleName
|
||||
}
|
||||
|
||||
abstract val image: SimpleDraweeView
|
||||
abstract val image: ImageView
|
||||
abstract val clickTarget: View
|
||||
abstract val progressBar: ProgressBar
|
||||
|
||||
private val authHeader = mapOf(
|
||||
Pair(
|
||||
"Authorization",
|
||||
ApiUtils.getCredentials(user.username, user.token)
|
||||
)
|
||||
)
|
||||
|
||||
open fun onBind(item: SharedFileItem) {
|
||||
image.hierarchy.setPlaceholderImage(viewThemeUtils.talk.getPlaceholderImage(image.context, item.mimeType))
|
||||
|
||||
val placeholder = viewThemeUtils.talk.getPlaceholderImage(image.context, item.mimeType)
|
||||
if (item.previewAvailable) {
|
||||
image.controller = configurePreview(item)
|
||||
image.loadImage(
|
||||
item.previewLink,
|
||||
user,
|
||||
placeholder
|
||||
)
|
||||
} else {
|
||||
image.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -105,28 +96,6 @@ abstract class SharedItemsViewHolder(
|
||||
)
|
||||
}
|
||||
|
||||
private fun configurePreview(item: SharedFileItem): DraweeController {
|
||||
val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.previewLink))
|
||||
.setProgressiveRenderingEnabled(true)
|
||||
.setRotationOptions(RotationOptions.autoRotate())
|
||||
.disableDiskCache()
|
||||
.setHeaders(authHeader)
|
||||
.build()
|
||||
|
||||
val listener: ControllerListener<ImageInfo?> = object : BaseControllerListener<ImageInfo?>() {
|
||||
override fun onFailure(id: String, e: Throwable) {
|
||||
Log.w(TAG, "Failed to load image. A static mimetype image will be used", e)
|
||||
}
|
||||
}
|
||||
|
||||
return Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(image.controller)
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(imageRequest)
|
||||
.setControllerListener(listener)
|
||||
.build()
|
||||
}
|
||||
|
||||
open fun onBind(item: SharedPollItem, showPoll: (item: SharedItem, context: Context) -> Unit) {}
|
||||
|
||||
open fun onBind(item: SharedLocationItem) {}
|
||||
|
@ -33,8 +33,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.nextcloud.talk.activities.MainActivity;
|
||||
import com.nextcloud.talk.adapters.items.AdvancedUserItem;
|
||||
@ -42,6 +40,7 @@ import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.databinding.DialogChooseAccountBinding;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.status.Status;
|
||||
import com.nextcloud.talk.models.json.status.StatusOverall;
|
||||
@ -132,22 +131,10 @@ public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
if (user.getBaseUrl() != null &&
|
||||
(user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) {
|
||||
binding.currentAccount.userIcon.setVisibility(View.VISIBLE);
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(binding.currentAccount.userIcon.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
user.getBaseUrl(),
|
||||
user.getUserId(),
|
||||
false)))
|
||||
.build();
|
||||
binding.currentAccount.userIcon.setController(draweeController);
|
||||
|
||||
ImageViewExtensionsKt.loadAvatar(binding.currentAccount.userIcon, user, user.getUserId(), true);
|
||||
} else {
|
||||
binding.currentAccount.userIcon.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
loadCurrentStatus(user);
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,6 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import autodagger.AutoInjector
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.nextcloud.talk.activities.MainActivity
|
||||
import com.nextcloud.talk.adapters.items.AdvancedUserItem
|
||||
@ -42,11 +40,10 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.DialogChooseAccountShareToBinding
|
||||
import com.nextcloud.talk.extensions.loadAvatar
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import java.net.CookieManager
|
||||
@ -97,21 +94,7 @@ class ChooseAccountShareToDialogFragment : DialogFragment() {
|
||||
if (user.baseUrl != null &&
|
||||
(user.baseUrl!!.startsWith("http://") || user.baseUrl!!.startsWith("https://"))
|
||||
) {
|
||||
binding!!.currentAccount.userIcon.visibility = View.VISIBLE
|
||||
val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(binding!!.currentAccount.userIcon.controller)
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatar(
|
||||
user.baseUrl,
|
||||
user.userId,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
binding!!.currentAccount.userIcon.controller = draweeController
|
||||
binding!!.currentAccount.userIcon.loadAvatar(user, user.userId!!)
|
||||
} else {
|
||||
binding!!.currentAccount.userIcon.visibility = View.INVISIBLE
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
@ -22,7 +24,6 @@
|
||||
|
||||
package com.nextcloud.talk.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -33,11 +34,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
@ -51,48 +48,26 @@ 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.view.Window;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.interfaces.DraweeController;
|
||||
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.data.user.model.User;
|
||||
import com.nextcloud.talk.events.UserMentionClickEvent;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
import com.nextcloud.talk.utils.text.Spans;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -103,12 +78,16 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.XmlRes;
|
||||
import androidx.appcompat.widget.AppCompatDrawableManager;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.emoji.text.EmojiCompat;
|
||||
import coil.Coil;
|
||||
import coil.request.ImageRequest;
|
||||
import coil.target.Target;
|
||||
import coil.transform.CircleCropTransformation;
|
||||
import third.parties.fresco.BetterImageSpan;
|
||||
|
||||
import static com.nextcloud.talk.utils.FileSortOrder.sort_a_to_z_id;
|
||||
import static com.nextcloud.talk.utils.FileSortOrder.sort_big_to_small_id;
|
||||
@ -119,8 +98,6 @@ import static com.nextcloud.talk.utils.FileSortOrder.sort_z_to_a_id;
|
||||
|
||||
public class DisplayUtils {
|
||||
|
||||
private static final String TAG = "DisplayUtils";
|
||||
|
||||
private static final int INDEX_LUMINATION = 2;
|
||||
private static final double MAX_LIGHTNESS = 0.92;
|
||||
|
||||
@ -154,33 +131,6 @@ public class DisplayUtils {
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private static void updateViewSize(@Nullable ImageInfo imageInfo, SimpleDraweeView draweeView) {
|
||||
if (imageInfo != null && draweeView.getId() != R.id.messageUserAvatar) {
|
||||
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) ResourcesCompat.getDrawable(resources, resource, null);
|
||||
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));
|
||||
}
|
||||
|
||||
public static Bitmap getBitmap(Drawable drawable) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||
drawable.getIntrinsicHeight(),
|
||||
@ -191,60 +141,6 @@ public class DisplayUtils {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static ImageRequest getImageRequestForUrl(String url) {
|
||||
return getImageRequestForUrl(url, (User) null);
|
||||
}
|
||||
|
||||
public static ImageRequest getImageRequestForUrl(String url, @Nullable User user) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
if (user != null &&
|
||||
url.startsWith(user.getBaseUrl()) &&
|
||||
(url.contains("index.php/core/preview?fileId=") || url.contains("/avatar/"))) {
|
||||
headers.put("Authorization", ApiUtils.getCredentials(user.getUsername(), user.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) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinalImageSet(String id, @Nullable Object imageInfo, @Nullable Animatable animatable) {
|
||||
updateViewSize((ImageInfo) imageInfo, draweeView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIntermediateImageSet(String id, @Nullable Object imageInfo) {
|
||||
updateViewSize((ImageInfo) imageInfo, draweeView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIntermediateImageFailed(String id, Throwable throwable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String id, Throwable throwable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease(String id) {
|
||||
// unused atm
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static float convertDpToPixel(float dp, Context context) {
|
||||
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
|
||||
context.getResources().getDisplayMetrics()) + 0.5f);
|
||||
@ -254,31 +150,6 @@ public class DisplayUtils {
|
||||
return px / context.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
// 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 = ResourcesCompat.getDrawable(res, drawableResId, null);
|
||||
|
||||
@ -305,10 +176,8 @@ public class DisplayUtils {
|
||||
viewThemeUtils.material.colorChipDrawable(context, chip);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
chip.setLayoutDirection(config.getLayoutDirection());
|
||||
}
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
chip.setLayoutDirection(config.getLayoutDirection());
|
||||
|
||||
int drawable;
|
||||
|
||||
@ -335,33 +204,37 @@ public class DisplayUtils {
|
||||
conversationUser.getBaseUrl(),
|
||||
String.valueOf(label), true);
|
||||
}
|
||||
ImageRequest imageRequest = getImageRequestForUrl(url);
|
||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(
|
||||
imageRequest,
|
||||
context);
|
||||
|
||||
dataSource.subscribe(
|
||||
new BaseBitmapDataSubscriber() {
|
||||
ImageRequest imageRequest = new ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.transformations(new CircleCropTransformation())
|
||||
.target(new Target() {
|
||||
@Override
|
||||
protected void onNewResultImpl(Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
chip.setChipIcon(getRoundedDrawable(new BitmapDrawable(bitmap)));
|
||||
public void onStart(@Nullable Drawable drawable) {
|
||||
|
||||
// A hack to refresh the chip icon
|
||||
if (emojiEditText != null) {
|
||||
emojiEditText.post(() -> emojiEditText.setTextKeepState(
|
||||
emojiEditText.getText(),
|
||||
TextView.BufferType.SPANNABLE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable Drawable drawable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull Drawable drawable) {
|
||||
chip.setChipIcon(drawable);
|
||||
|
||||
// A hack to refresh the chip icon
|
||||
if (emojiEditText != null) {
|
||||
emojiEditText.post(() -> emojiEditText.setTextKeepState(
|
||||
emojiEditText.getText(),
|
||||
TextView.BufferType.SPANNABLE));
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
@Override
|
||||
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
|
||||
}
|
||||
},
|
||||
UiThreadImmediateExecutorService.getInstance());
|
||||
Coil.imageLoader(context).enqueue(imageRequest);
|
||||
}
|
||||
|
||||
return chip;
|
||||
@ -481,24 +354,23 @@ public class DisplayUtils {
|
||||
Window window = activity.getWindow();
|
||||
boolean isLightTheme = lightTheme(color);
|
||||
if (window != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
View decor = window.getDecorView();
|
||||
if (isLightTheme) {
|
||||
int systemUiFlagLightStatusBar;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
systemUiFlagLightStatusBar = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR |
|
||||
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
|
||||
} else {
|
||||
systemUiFlagLightStatusBar = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
||||
}
|
||||
decor.setSystemUiVisibility(systemUiFlagLightStatusBar);
|
||||
|
||||
View decor = window.getDecorView();
|
||||
if (isLightTheme) {
|
||||
int systemUiFlagLightStatusBar;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
systemUiFlagLightStatusBar = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR |
|
||||
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
|
||||
} else {
|
||||
decor.setSystemUiVisibility(0);
|
||||
systemUiFlagLightStatusBar = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
||||
}
|
||||
window.setStatusBarColor(color);
|
||||
} else if (isLightTheme) {
|
||||
window.setStatusBarColor(Color.BLACK);
|
||||
decor.setSystemUiVisibility(systemUiFlagLightStatusBar);
|
||||
} else {
|
||||
decor.setSystemUiVisibility(0);
|
||||
}
|
||||
window.setStatusBarColor(color);
|
||||
} else if (isLightTheme) {
|
||||
window.setStatusBarColor(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -575,7 +447,7 @@ public class DisplayUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadAvatarImage(User user, SimpleDraweeView avatarImageView, boolean deleteCache) {
|
||||
public static void loadAvatarImage(User user, ImageView avatarImageView, boolean deleteCache) {
|
||||
String avatarId;
|
||||
if (!TextUtils.isEmpty(user.getUserId())) {
|
||||
avatarId = user.getUserId();
|
||||
@ -583,50 +455,13 @@ public class DisplayUtils {
|
||||
avatarId = user.getUsername();
|
||||
}
|
||||
|
||||
String avatarString = ApiUtils.getUrlForAvatar(user.getBaseUrl(), avatarId, true);
|
||||
|
||||
// clear cache
|
||||
if (deleteCache) {
|
||||
Uri avatarUri = Uri.parse(avatarString);
|
||||
|
||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
imagePipeline.evictFromMemoryCache(avatarUri);
|
||||
imagePipeline.evictFromDiskCache(avatarUri);
|
||||
imagePipeline.evictFromCache(avatarUri);
|
||||
}
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(avatarImageView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(avatarString))
|
||||
.build();
|
||||
avatarImageView.setController(draweeController);
|
||||
}
|
||||
|
||||
public static void loadAvatarPlaceholder(final SimpleDraweeView targetView) {
|
||||
final Context context = targetView.getContext();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Drawable[] layers = new Drawable[2];
|
||||
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background);
|
||||
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground);
|
||||
LayerDrawable layerDrawable = new LayerDrawable(layers);
|
||||
|
||||
targetView.getHierarchy().setPlaceholderImage(
|
||||
DisplayUtils.getRoundedDrawable(layerDrawable));
|
||||
ImageViewExtensionsKt.replaceAvatar(avatarImageView, user, avatarId, true);
|
||||
} else {
|
||||
targetView.getHierarchy().setPlaceholderImage(R.mipmap.ic_launcher);
|
||||
ImageViewExtensionsKt.loadAvatar(avatarImageView, user, avatarId, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadImage(final SimpleDraweeView targetView, final ImageRequest imageRequest) {
|
||||
final DraweeController newController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(targetView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(imageRequest)
|
||||
.build();
|
||||
targetView.setController(newController);
|
||||
}
|
||||
|
||||
public static @StringRes
|
||||
int getSortOrderStringId(FileSortOrder sortOrder) {
|
||||
switch (sortOrder.getName()) {
|
||||
@ -649,7 +484,7 @@ public class DisplayUtils {
|
||||
/**
|
||||
* calculates the relative time string based on the given modification timestamp.
|
||||
*
|
||||
* @param context the app's context
|
||||
* @param context the app's context
|
||||
* @param modificationTimestamp the UNIX timestamp of the file modification time in milliseconds.
|
||||
* @return a relative time string
|
||||
*/
|
||||
|
@ -22,7 +22,7 @@
|
||||
package com.nextcloud.talk.utils
|
||||
|
||||
import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem
|
||||
import third_parties.daveKoeller.AlphanumComparator
|
||||
import third.parties.daveKoeller.AlphanumComparator
|
||||
import java.util.Collections
|
||||
|
||||
class FileSortOrderByName internal constructor(name: String, ascending: Boolean) : FileSortOrder(name, ascending) {
|
||||
@ -40,7 +40,8 @@ class FileSortOrderByName internal constructor(name: String, ascending: Boolean)
|
||||
* Comparator for RemoteFileBrowserItems, sorts by name.
|
||||
*/
|
||||
class RemoteFileBrowserItemNameComparator(private val multiplier: Int) : Comparator<RemoteFileBrowserItem> {
|
||||
private val alphanumComparator = AlphanumComparator<RemoteFileBrowserItem>()
|
||||
private val alphanumComparator =
|
||||
AlphanumComparator<RemoteFileBrowserItem>()
|
||||
|
||||
override fun compare(left: RemoteFileBrowserItem, right: RemoteFileBrowserItem): Int {
|
||||
return if (!left.isFile && !right.isFile) {
|
||||
|
@ -28,6 +28,7 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
@ -36,7 +37,6 @@ import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.FullScreenImageActivity
|
||||
import com.nextcloud.talk.activities.FullScreenMediaActivity
|
||||
@ -412,7 +412,7 @@ class FileViewerUtils(private val context: Context, private val user: User) {
|
||||
data class ProgressUi(
|
||||
val progressBar: ProgressBar?,
|
||||
val messageText: EmojiTextView?,
|
||||
val previewImage: SimpleDraweeView
|
||||
val previewImage: ImageView
|
||||
)
|
||||
|
||||
data class FileInfo(
|
||||
|
@ -27,18 +27,19 @@ import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.media.AudioAttributes
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import coil.executeBlocking
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.facebook.common.references.CloseableReference
|
||||
import com.facebook.datasource.DataSources
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.imagepipeline.image.CloseableBitmap
|
||||
import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
@ -50,6 +51,8 @@ import java.io.IOException
|
||||
@Suppress("TooManyFunctions")
|
||||
object NotificationUtils {
|
||||
|
||||
const val TAG = "NotificationUtils"
|
||||
|
||||
enum class NotificationChannels {
|
||||
NOTIFICATION_CHANNEL_MESSAGES_V4,
|
||||
NOTIFICATION_CHANNEL_CALLS_V4,
|
||||
@ -215,7 +218,7 @@ object NotificationUtils {
|
||||
notification: Notification
|
||||
) -> Unit
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || conversationUser.id == -1L || context == null) {
|
||||
if (conversationUser.id == -1L || context == null) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -320,28 +323,30 @@ object NotificationUtils {
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Load user avatar synchronously.
|
||||
* Inspired by:
|
||||
* https://frescolib.org/docs/using-image-pipeline.html
|
||||
* https://github.com/facebook/fresco/issues/830
|
||||
* https://localcoder.org/using-facebooks-fresco-to-load-a-bitmap
|
||||
*/
|
||||
fun loadAvatarSync(avatarUrl: String): IconCompat? {
|
||||
// TODO - how to handle errors here?
|
||||
fun loadAvatarSync(avatarUrl: String, context: Context): IconCompat? {
|
||||
var avatarIcon: IconCompat? = null
|
||||
val imageRequest = DisplayUtils.getImageRequestForUrl(avatarUrl)
|
||||
val dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, null)
|
||||
val closeableImageRef = DataSources.waitForFinalResult(dataSource) as CloseableReference<CloseableBitmap>?
|
||||
val bitmap = closeableImageRef?.get()?.underlyingBitmap
|
||||
if (bitmap != null) {
|
||||
// According to Fresco documentation a copy of the bitmap should be made before closing the references.
|
||||
// However, it seems to work without making a copy... ;-)
|
||||
RoundAsCirclePostprocessor(true).process(bitmap)
|
||||
avatarIcon = IconCompat.createWithBitmap(bitmap)
|
||||
}
|
||||
CloseableReference.closeSafely(closeableImageRef)
|
||||
dataSource.close()
|
||||
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(avatarUrl)
|
||||
.transformations(CircleCropTransformation())
|
||||
.placeholder(R.drawable.account_circle_96dp)
|
||||
.target(
|
||||
onSuccess = { result ->
|
||||
val bitmap = (result as BitmapDrawable).bitmap
|
||||
avatarIcon = IconCompat.createWithBitmap(bitmap)
|
||||
},
|
||||
onError = { error ->
|
||||
error?.let {
|
||||
val bitmap = (error as BitmapDrawable).bitmap
|
||||
avatarIcon = IconCompat.createWithBitmap(bitmap)
|
||||
}
|
||||
Log.w(TAG, "Can't load avatar for URL: $avatarUrl")
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
context.imageLoader.executeBlocking(request)
|
||||
|
||||
return avatarIcon
|
||||
}
|
||||
|
||||
|
@ -1,41 +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;
|
||||
|
||||
import com.facebook.imagepipeline.backends.okhttp3.OkHttpNetworkFetcher;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class OkHttpNetworkFetcherWithCache extends OkHttpNetworkFetcher {
|
||||
public OkHttpNetworkFetcherWithCache(OkHttpClient okHttpClient) {
|
||||
super(okHttpClient);
|
||||
}
|
||||
|
||||
public OkHttpNetworkFetcherWithCache(Call.Factory callFactory, Executor cancellationExecutor) {
|
||||
super(callFactory, cancellationExecutor);
|
||||
}
|
||||
|
||||
public OkHttpNetworkFetcherWithCache(Call.Factory callFactory, Executor cancellationExecutor, boolean disableOkHttpCache) {
|
||||
super(callFactory, cancellationExecutor, true);
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
package com.nextcloud.talk.utils;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
import android.security.keystore.KeyProperties;
|
||||
@ -50,7 +49,6 @@ import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
|
||||
public class SecurityUtils {
|
||||
@ -60,7 +58,6 @@ public class SecurityUtils {
|
||||
|
||||
private static BiometricPrompt.CryptoObject cryptoObject;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static boolean checkIfWeAreAuthenticated(String screenLockTimeout) {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||
@ -95,12 +92,10 @@ public class SecurityUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static BiometricPrompt.CryptoObject getCryptoObject() {
|
||||
return cryptoObject;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static void createKey(String validity) {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||
|
@ -23,7 +23,6 @@ package com.nextcloud.talk.utils.permissions
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.core.content.PermissionChecker
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
|
||||
@ -32,13 +31,9 @@ class PlatformPermissionUtilImpl(private val context: Context) : PlatformPermiss
|
||||
"${BuildConfig.APPLICATION_ID}.${BuildConfig.PERMISSION_LOCAL_BROADCAST}"
|
||||
|
||||
override fun isCameraPermissionGranted(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return PermissionChecker.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.CAMERA
|
||||
) == PermissionChecker.PERMISSION_GRANTED
|
||||
} else {
|
||||
true
|
||||
}
|
||||
return PermissionChecker.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.CAMERA
|
||||
) == PermissionChecker.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,9 @@
|
||||
|
||||
package com.nextcloud.talk.utils.ssl
|
||||
|
||||
import android.os.Build
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
||||
import java.security.GeneralSecurityException
|
||||
import java.util.LinkedList
|
||||
import javax.net.ssl.KeyManager
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.SSLSocket
|
||||
@ -35,69 +32,12 @@ class SSLSocketFactoryCompat(
|
||||
var cipherSuites: Array<String>? = null
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Since Android 6.0 (API level 23),
|
||||
// - TLSv1.1 and TLSv1.2 is enabled by default
|
||||
// - SSLv3 is disabled by default
|
||||
// - all modern ciphers are activated by default
|
||||
protocols = null
|
||||
cipherSuites = null
|
||||
} else {
|
||||
val socket = SSLSocketFactory.getDefault().createSocket() as SSLSocket?
|
||||
try {
|
||||
socket?.let {
|
||||
/* set reasonable protocol versions */
|
||||
// - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
|
||||
// - remove all SSL versions (especially SSLv3) because they're insecure now
|
||||
val _protocols = LinkedList<String>()
|
||||
for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) })
|
||||
_protocols += protocol
|
||||
protocols = _protocols.toTypedArray()
|
||||
|
||||
/* set up reasonable cipher suites */
|
||||
val knownCiphers = arrayOf<String>(
|
||||
// TLS 1.2
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
// maximum interoperability
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
// additionally
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
|
||||
)
|
||||
val availableCiphers = socket.supportedCipherSuites
|
||||
|
||||
/* For maximum security, preferredCiphers should *replace* enabled ciphers (thus
|
||||
* disabling ciphers which are enabled by default, but have become unsecure), but for
|
||||
* the security level of DAVdroid and maximum compatibility, disabling of insecure
|
||||
* ciphers should be a server-side task */
|
||||
|
||||
// for the final set of enabled ciphers, take the ciphers enabled by default, ...
|
||||
val _cipherSuites = LinkedList<String>()
|
||||
_cipherSuites.addAll(socket.enabledCipherSuites)
|
||||
// ... add explicitly allowed ciphers ...
|
||||
_cipherSuites.addAll(knownCiphers)
|
||||
// ... and keep only those which are actually available
|
||||
_cipherSuites.retainAll(availableCiphers)
|
||||
|
||||
cipherSuites = _cipherSuites.toTypedArray()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// Exception is to be ignored
|
||||
} finally {
|
||||
socket?.close() // doesn't implement Closeable on all supported Android versions
|
||||
}
|
||||
}
|
||||
// Since Android 6.0 (API level 23),
|
||||
// - TLSv1.1 and TLSv1.2 is enabled by default
|
||||
// - SSLv3 is disabled by default
|
||||
// - all modern ciphers are activated by default
|
||||
protocols = null
|
||||
cipherSuites = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,10 @@ package com.nextcloud.talk.utils.text;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.facebook.widget.text.span.BetterImageSpan;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import third.parties.fresco.BetterImageSpan;
|
||||
|
||||
public class Spans {
|
||||
|
||||
|
@ -41,7 +41,6 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nextcloud.talk.events.PeerConnectionEvent;
|
||||
@ -388,22 +387,18 @@ public class WebRtcAudioManager {
|
||||
*/
|
||||
@Deprecated
|
||||
private boolean hasWiredHeadset() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return audioManager.isWiredHeadsetOn();
|
||||
} else {
|
||||
@SuppressLint("WrongConstant") final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
|
||||
for (AudioDeviceInfo device : devices) {
|
||||
final int type = device.getType();
|
||||
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
|
||||
Log.d(TAG, "hasWiredHeadset: found wired headset");
|
||||
return true;
|
||||
} else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) {
|
||||
Log.d(TAG, "hasWiredHeadset: found USB audio device");
|
||||
return true;
|
||||
}
|
||||
@SuppressLint("WrongConstant") final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
|
||||
for (AudioDeviceInfo device : devices) {
|
||||
final int type = device.getType();
|
||||
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
|
||||
Log.d(TAG, "hasWiredHeadset: found wired headset");
|
||||
return true;
|
||||
} else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) {
|
||||
Log.d(TAG, "hasWiredHeadset: found USB audio device");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateAudioDeviceState() {
|
||||
|
@ -22,7 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package third_parties.daveKoeller;
|
||||
package third.parties.daveKoeller;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
138
app/src/main/java/third/parties/fresco/BetterImageSpan.kt
Normal file
138
app/src/main/java/third/parties/fresco/BetterImageSpan.kt
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package third.parties.fresco
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Paint.FontMetricsInt
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.style.ReplacementSpan
|
||||
import androidx.annotation.IntDef
|
||||
|
||||
/**
|
||||
* A better implementation of image spans that also supports centering images against the text.
|
||||
*
|
||||
* In order to migrate from ImageSpan, replace `new ImageSpan(drawable, alignment)` with
|
||||
* `new BetterImageSpan(drawable, BetterImageSpan.normalizeAlignment(alignment))`.
|
||||
*
|
||||
* There are 2 main differences between BetterImageSpan and ImageSpan:
|
||||
* 1. Pass in ALIGN_CENTER to center images against the text.
|
||||
* 2. ALIGN_BOTTOM no longer unnecessarily increases the size of the text:
|
||||
* DynamicDrawableSpan (ImageSpan's parent) adjusts sizes as if alignment was ALIGN_BASELINE
|
||||
* which can lead to unnecessary whitespace.
|
||||
*/
|
||||
open class BetterImageSpan @JvmOverloads constructor(
|
||||
val drawable: Drawable,
|
||||
@param:BetterImageSpanAlignment private val mAlignment: Int = ALIGN_BASELINE
|
||||
) : ReplacementSpan() {
|
||||
@IntDef(*[ALIGN_BASELINE, ALIGN_BOTTOM, ALIGN_CENTER])
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class BetterImageSpanAlignment
|
||||
|
||||
private var mWidth = 0
|
||||
private var mHeight = 0
|
||||
private var mBounds: Rect? = null
|
||||
private val mFontMetricsInt = FontMetricsInt()
|
||||
|
||||
init {
|
||||
updateBounds()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the image span and increases the height if font metrics are available.
|
||||
*/
|
||||
override fun getSize(
|
||||
paint: Paint,
|
||||
text: CharSequence,
|
||||
start: Int,
|
||||
end: Int,
|
||||
fontMetrics: FontMetricsInt?
|
||||
): Int {
|
||||
updateBounds()
|
||||
if (fontMetrics == null) {
|
||||
return mWidth
|
||||
}
|
||||
val offsetAbove = getOffsetAboveBaseline(fontMetrics)
|
||||
val offsetBelow = mHeight + offsetAbove
|
||||
if (offsetAbove < fontMetrics.ascent) {
|
||||
fontMetrics.ascent = offsetAbove
|
||||
}
|
||||
if (offsetAbove < fontMetrics.top) {
|
||||
fontMetrics.top = offsetAbove
|
||||
}
|
||||
if (offsetBelow > fontMetrics.descent) {
|
||||
fontMetrics.descent = offsetBelow
|
||||
}
|
||||
if (offsetBelow > fontMetrics.bottom) {
|
||||
fontMetrics.bottom = offsetBelow
|
||||
}
|
||||
return mWidth
|
||||
}
|
||||
|
||||
override fun draw(
|
||||
canvas: Canvas,
|
||||
text: CharSequence,
|
||||
start: Int,
|
||||
end: Int,
|
||||
x: Float,
|
||||
top: Int,
|
||||
y: Int,
|
||||
bottom: Int,
|
||||
paint: Paint
|
||||
) {
|
||||
paint.getFontMetricsInt(mFontMetricsInt)
|
||||
val iconTop = y + getOffsetAboveBaseline(mFontMetricsInt)
|
||||
canvas.translate(x, iconTop.toFloat())
|
||||
drawable.draw(canvas)
|
||||
canvas.translate(-x, -iconTop.toFloat())
|
||||
}
|
||||
|
||||
private fun updateBounds() {
|
||||
mBounds = drawable.bounds
|
||||
mWidth = mBounds!!.width()
|
||||
mHeight = mBounds!!.height()
|
||||
}
|
||||
|
||||
private fun getOffsetAboveBaseline(fm: FontMetricsInt): Int {
|
||||
return when (mAlignment) {
|
||||
ALIGN_BOTTOM -> fm.descent - mHeight
|
||||
ALIGN_CENTER -> {
|
||||
val textHeight = fm.descent - fm.ascent
|
||||
val offset = (textHeight - mHeight) / 2
|
||||
fm.ascent + offset
|
||||
}
|
||||
ALIGN_BASELINE -> -mHeight
|
||||
else -> -mHeight
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ALIGN_BOTTOM = 0
|
||||
const val ALIGN_BASELINE = 1
|
||||
const val ALIGN_CENTER = 2
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/shape_oval.xml
Normal file
5
app/src/main/res/drawable/shape_oval.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="#000000" />
|
||||
</shape>
|
@ -21,7 +21,6 @@
|
||||
-->
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
@ -44,7 +43,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/user_icon"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
@ -54,10 +53,7 @@
|
||||
android:layout_marginEnd="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:src="@drawable/account_circle_48dp"
|
||||
fresco:placeholderImage="@drawable/account_circle_48dp"
|
||||
fresco:failureImage="@drawable/account_circle_48dp"
|
||||
app:roundAsCircle="true"/>
|
||||
android:src="@drawable/account_circle_48dp" />
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
@ -113,7 +113,8 @@
|
||||
android:transitionName="userAvatar.transitionTag"
|
||||
app:cornerRadius="@dimen/button_corner_radius"
|
||||
app:iconSize="@dimen/avatar_size_app_bar"
|
||||
tools:visibility="gone" />
|
||||
app:iconTint="@null"
|
||||
tools:icon="@drawable/ic_user" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
@ -2,9 +2,11 @@
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
~ @author Marcel Hibbe
|
||||
~ @author Tim Krüger
|
||||
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
|
||||
~ 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
|
||||
@ -69,14 +71,14 @@
|
||||
android:visibility="invisible"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/switchSelfVideoButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:placeholderImage="@drawable/ic_switch_video_white_24px"
|
||||
app:roundAsCircle="true" />
|
||||
app:srcCompat="@drawable/ic_switch_video_white_24px"
|
||||
android:contentDescription="@string/nc_call_button_content_description_switch_to_self_vide"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/selfVideoViewProgressBar"
|
||||
@ -144,6 +146,7 @@
|
||||
android:id="@+id/callControls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/call_controls_height"
|
||||
android:paddingHorizontal="@dimen/call_controls_padding_horizontal"
|
||||
android:layout_alignBottom="@id/linearWrapperLayout"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@android:color/transparent"
|
||||
@ -151,94 +154,98 @@
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="5">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/pictureInPictureButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:elevation="10dp"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_baseline_picture_in_picture_alt_24"
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginHorizontal="@dimen/call_controls_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/call_buttons_background"
|
||||
app:srcCompat="@drawable/ic_baseline_picture_in_picture_alt_24"
|
||||
android:contentDescription="@string/nc_call_button_content_description_pip" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/audioOutputButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_volume_mute_white_24dp"
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginHorizontal="@dimen/call_controls_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/call_buttons_background"
|
||||
app:srcCompat="@drawable/ic_volume_mute_white_24dp"
|
||||
android:contentDescription="@string/nc_call_button_content_description_audio_output" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/cameraButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginHorizontal="@dimen/call_controls_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:alpha="0.7"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_videocam_white_24px"
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/call_buttons_background"
|
||||
app:srcCompat="@drawable/ic_videocam_white_24px"
|
||||
android:contentDescription="@string/nc_call_button_content_description_camera" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/microphoneButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginHorizontal="@dimen/call_controls_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:alpha="0.7"
|
||||
app:backgroundImage="@color/call_buttons_background"
|
||||
app:placeholderImage="@drawable/ic_mic_off_white_24px"
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/call_buttons_background"
|
||||
app:srcCompat="@drawable/ic_mic_off_white_24px"
|
||||
android:contentDescription="@string/nc_call_button_content_description_microphone" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/hangupButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:backgroundImage="@color/nc_darkRed"
|
||||
app:placeholderImage="@drawable/ic_call_end_white_24px"
|
||||
app:roundAsCircle="true"
|
||||
android:layout_weight="1"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginHorizontal="@dimen/call_controls_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/nc_darkRed"
|
||||
app:srcCompat="@drawable/ic_call_end_white_24px"
|
||||
android:contentDescription="@string/nc_call_button_content_description_hangup" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/pipGroupCallOverlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/black"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pipCallConversationNameTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-30dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="-30dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:textAlignment="center"
|
||||
android:maxLines="3"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
tools:text="our group call" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
app:backgroundImage="@drawable/ic_circular_group"
|
||||
app:roundAsCircle="true" />
|
||||
app:srcCompat="@drawable/ic_circular_group"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -31,12 +31,12 @@
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/avatarImageView"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_centerInParent="true"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar"/>
|
||||
|
||||
<org.webrtc.SurfaceViewRenderer
|
||||
android:id="@+id/surface_view"
|
||||
|
@ -3,6 +3,8 @@
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ @author Tim Krüger
|
||||
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
@ -36,36 +38,37 @@
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/callAnswerVoiceOnlyView"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_margin="24dp"
|
||||
android:visibility="gone"
|
||||
app:backgroundImage="@color/nc_darkGreen"
|
||||
app:placeholderImage="@drawable/ic_call_white_24dp"
|
||||
app:roundAsCircle="true"
|
||||
tools:visibility="visible" />
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/nc_darkGreen"
|
||||
android:src="@drawable/ic_call_white_24dp"
|
||||
android:contentDescription="@string/nc_call_button_content_description_answer_voice_only" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/hangupButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_margin="24dp"
|
||||
app:backgroundImage="@color/nc_darkRed"
|
||||
app:placeholderImage="@drawable/ic_call_end_white_24px"
|
||||
app:roundAsCircle="true" />
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/nc_darkRed"
|
||||
android:src="@drawable/ic_call_end_white_24px"
|
||||
android:contentDescription="@string/nc_call_button_content_description_hangup" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageButton
|
||||
android:id="@+id/callAnswerCameraView"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_margin="24dp"
|
||||
android:background="@drawable/shape_oval"
|
||||
android:backgroundTint="@color/nc_darkGreen"
|
||||
android:src="@drawable/ic_videocam_white_24px"
|
||||
android:visibility="gone"
|
||||
app:backgroundImage="@color/nc_darkGreen"
|
||||
app:placeholderImage="@drawable/ic_videocam_white_24px"
|
||||
app:roundAsCircle="true"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="visible"
|
||||
android:contentDescription="@string/nc_call_button_content_description_answer_video_call" />
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
@ -111,11 +114,11 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/avatarImageView"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
android:layout_centerInParent="true"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -77,13 +77,13 @@
|
||||
android:layout_marginTop="@dimen/margin_between_elements"
|
||||
tools:text="Jane Doe" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
android:layout_centerHorizontal="true"
|
||||
apc:roundAsCircle="true"
|
||||
tools:background="@color/hwSecurityRed" />
|
||||
tools:background="@color/hwSecurityRed"
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
</RelativeLayout>
|
||||
</com.yarolegovich.mp.MaterialPreferenceCategory>
|
||||
|
@ -19,7 +19,6 @@
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -31,7 +30,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
@ -39,9 +38,7 @@
|
||||
android:layout_marginTop="@dimen/standard_margin"
|
||||
android:src="@drawable/account_circle_96dp"
|
||||
android:transitionName="userAvatar.transitionTag"
|
||||
app:roundAsCircle="true"
|
||||
fresco:failureImage="@drawable/account_circle_96dp"
|
||||
fresco:placeholderImage="@drawable/account_circle_96dp" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/userinfo_fullName"
|
||||
|
@ -25,7 +25,6 @@
|
||||
<com.yarolegovich.mp.MaterialPreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:apc="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:background="@color/white"
|
||||
android:id="@+id/settings_screen"
|
||||
@ -119,16 +118,14 @@
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:src="@drawable/account_circle_96dp"
|
||||
android:transitionName="userAvatar.transitionTag"
|
||||
apc:roundAsCircle="true"
|
||||
fresco:failureImage="@drawable/account_circle_96dp"
|
||||
fresco:placeholderImage="@drawable/account_circle_96dp" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.yarolegovich.mp.MaterialStandardPreference
|
||||
android:id="@+id/settings_remove_account"
|
||||
|
@ -21,7 +21,6 @@
|
||||
-->
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
@ -45,7 +44,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/user_icon"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
@ -55,10 +54,7 @@
|
||||
android:layout_marginEnd="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:src="@drawable/account_circle_48dp"
|
||||
fresco:placeholderImage="@drawable/account_circle_48dp"
|
||||
fresco:failureImage="@drawable/account_circle_48dp"
|
||||
app:roundAsCircle="true"/>
|
||||
android:src="@drawable/account_circle_48dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ticker"
|
||||
|
@ -32,13 +32,13 @@
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/messageUserAvatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@id/bubble"
|
||||
|
@ -31,13 +31,13 @@
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/messageUserAvatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@id/bubble"
|
||||
|
@ -28,13 +28,13 @@
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/messageUserAvatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@id/bubble"
|
||||
|
@ -32,13 +32,13 @@
|
||||
android:layout_marginEnd="14dp"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/messageUserAvatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="6dp"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:layout_width="wrap_content"
|
||||
@ -64,14 +64,13 @@
|
||||
app:layout_wrapBefore="true"
|
||||
tools:visibility="gone">
|
||||
|
||||
<!-- SimpleDraweeView does not support wrap_content for layout_width or layout_height attributes! -->
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/image"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitStart"
|
||||
app:roundedCornerRadius="6dp"
|
||||
tools:src="@drawable/ic_call_black_24dp" />
|
||||
tools:src="@drawable/ic_call_black_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
@ -105,14 +104,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true">
|
||||
|
||||
<!-- SimpleDraweeView does not support wrap_content for layout_width or layout_height attributes! -->
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/contact_photo"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:scaleType="fitStart"
|
||||
app:roundAsCircle="true"
|
||||
tools:src="@drawable/ic_call_black_24dp" />
|
||||
tools:src="@drawable/ic_call_black_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/contact_progress_bar"
|
||||
|
@ -30,13 +30,13 @@
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/messageUserAvatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@id/bubble"
|
||||
|
@ -32,13 +32,13 @@
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="2dp">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/messageUserAvatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@id/bubble"
|
||||
|
@ -55,14 +55,13 @@
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_wrapBefore="true">
|
||||
|
||||
<!-- SimpleDraweeView does not support wrap_content for layout_width or layout_height attributes! -->
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/image"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:scaleType="fitEnd"
|
||||
app:roundedCornerRadius="6dp"
|
||||
tools:src="@drawable/ic_call_black_24dp" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitStart"
|
||||
tools:src="@drawable/ic_call_black_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
@ -95,14 +94,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true">
|
||||
|
||||
<!-- SimpleDraweeView does not support wrap_content for layout_width or layout_height attributes! -->
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/contact_photo"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:scaleType="fitStart"
|
||||
app:roundAsCircle="true"
|
||||
tools:src="@drawable/ic_call_black_24dp" />
|
||||
tools:src="@drawable/ic_call_black_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/contact_progress_bar"
|
||||
|
@ -17,22 +17,21 @@
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="4dp"
|
||||
tools:background="@color/white">
|
||||
tools:background="@color/white"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/poll_voter_avatar"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_gravity="center"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/poll_voter_name"
|
||||
|
@ -19,19 +19,18 @@
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/item_height"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="@dimen/standard_margin"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
|
@ -19,7 +19,6 @@
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/referenceWrapper"
|
||||
android:layout_width="match_parent"
|
||||
@ -32,64 +31,64 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@color/low_emphasis_text"
|
||||
tools:layout_height="100dp"/>
|
||||
tools:layout_height="100dp" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/referenceName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:maxLines="2"
|
||||
android:textAlignment="viewStart"
|
||||
android:textIsSelectable="false"
|
||||
android:layout_marginStart="10dp"
|
||||
android:visibility="gone"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="Name of Website"
|
||||
tools:visibility="visible"/>
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/referenceDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/referenceName"
|
||||
android:layout_marginStart="10dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:maxLines="2"
|
||||
android:textAlignment="viewStart"
|
||||
android:textIsSelectable="false"
|
||||
android:layout_marginStart="10dp"
|
||||
android:visibility="gone"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
tools:text="Description of Website"
|
||||
tools:visibility="visible"/>
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/referenceLink"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/referenceDescription"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:textAlignment="viewStart"
|
||||
android:textIsSelectable="false"
|
||||
android:layout_marginStart="10dp"
|
||||
android:textColor="@color/medium_emphasis_text"
|
||||
android:singleLine="true"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/medium_emphasis_text"
|
||||
android:textIsSelectable="false"
|
||||
android:visibility="gone"
|
||||
tools:text="http://nextcloud.com"
|
||||
tools:visibility="visible"/>
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/referenceThumbImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:scaleType="fitEnd"
|
||||
android:layout_below="@id/referenceLink"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:scaleType="fitEnd"
|
||||
android:visibility="gone"
|
||||
app:roundedCornerRadius="6dp"
|
||||
tools:visibility="visible"/>
|
||||
tools:visibility="visible"
|
||||
tools:ignore="ContentDescription" />
|
||||
</RelativeLayout>
|
@ -93,13 +93,12 @@
|
||||
android:textSize="@dimen/two_line_primary_text_size"
|
||||
tools:text="filename.md" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/file_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
app:actualImageScaleType="fitCenter"
|
||||
app:placeholderImageScaleType="fitCenter" />
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -48,19 +48,19 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toStartOf="@id/checkedImageView"
|
||||
android:layout_toEndOf="@id/avatar_drawee_view"
|
||||
android:layout_toEndOf="@id/avatar_view"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/ListItem"
|
||||
tools:text="Jane Doe" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/avatar_drawee_view"
|
||||
<ImageView
|
||||
android:id="@+id/avatar_view"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@string/avatar" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -29,7 +29,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:id="@+id/simple_drawee_view"
|
||||
android:id="@+id/loader_text_view"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_centerVertical="true"
|
||||
@ -42,7 +42,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/simple_drawee_view"
|
||||
android:layout_toEndOf="@id/loader_text_view"
|
||||
app:custom_color="@color/nc_shimmer_default_color" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -26,15 +26,14 @@
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:layout_marginTop="@dimen/standard_margin">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/avatar_drawee_view"
|
||||
<ImageView
|
||||
android:id="@+id/avatar_view"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:contentDescription="@null"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundAsCircle="true" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.vanniktech.emoji.EmojiEditText
|
||||
android:id="@+id/participant_status_emoji"
|
||||
@ -54,8 +53,8 @@
|
||||
android:layout_height="18dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:contentDescription="@string/nc_account_chooser_active_user"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/avatar_drawee_view"
|
||||
app:layout_constraintEnd_toEndOf="@+id/avatar_drawee_view"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/avatar_view"
|
||||
app:layout_constraintEnd_toEndOf="@+id/avatar_view"
|
||||
tools:src="@drawable/emoji_one_category_smileysandpeople"/>
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
@ -68,8 +67,8 @@
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="@color/conversation_item_header"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar_drawee_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/avatar_drawee_view"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/avatar_view"
|
||||
tools:text="Jane Doe" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
|
@ -3,6 +3,8 @@
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ @author Tim Krüger
|
||||
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
@ -38,12 +40,11 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/double_margin_between_elements">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@id/dialogAvatar"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:contentDescription="@null"
|
||||
app:roundAsCircle="true" />
|
||||
android:contentDescription="@null" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/favoriteConversationImageView"
|
||||
|
@ -41,8 +41,7 @@
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundAsCircle="true" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
|
@ -3,6 +3,8 @@
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ @author Tim Krüger
|
||||
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
@ -35,14 +37,13 @@
|
||||
android:layout_margin="@dimen/double_margin_between_elements"
|
||||
tools:background="@color/white">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundAsCircle="true" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/conversation_title"
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@ -40,14 +40,12 @@
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_wrapBefore="true">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="4dp"
|
||||
app:placeholderImageScaleType="fitCenter"
|
||||
fresco:actualImageScaleType="centerCrop"
|
||||
fresco:roundedCornerRadius="4dp" />
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
|
@ -39,17 +39,14 @@
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_wrapBefore="true">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
<ImageView
|
||||
android:id="@+id/file_image"
|
||||
android:layout_width="@dimen/mediatab_file_icon_size"
|
||||
android:layout_height="@dimen/mediatab_file_icon_size"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:placeholderImageScaleType="fitCenter"
|
||||
fresco:actualImageScaleType="centerCrop"
|
||||
fresco:roundedCornerRadius="4dp"
|
||||
tools:src="@drawable/ic_call_black_24dp"/>
|
||||
tools:src="@drawable/ic_call_black_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
|
@ -75,7 +75,5 @@
|
||||
<color name="grey_200">#818181</color>
|
||||
|
||||
<color name="dialog_background">#353535</color>
|
||||
<color name="vote_dialog_background">#424242</color>
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -105,8 +105,5 @@
|
||||
<!-- this is just a helper for status icon background because getting the background color of a dialog is not
|
||||
possible?! don't use this to set the background of dialogs -->
|
||||
<color name="dialog_background">#FFFFFF</color>
|
||||
<color name="vote_dialog_background">#FFFFFF</color>
|
||||
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -2,6 +2,8 @@
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Tim Krüger
|
||||
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
~ Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
@ -67,6 +69,8 @@
|
||||
<dimen name="call_self_video_short_side_length">80dp</dimen>
|
||||
<dimen name="call_grid_item_min_height">180dp</dimen>
|
||||
<dimen name="call_controls_height">110dp</dimen>
|
||||
<dimen name="call_controls_padding_horizontal">10dp</dimen>
|
||||
<dimen name="call_controls_margin_horizontal">10dp</dimen>
|
||||
<dimen name="call_participant_progress_bar_size">48dp</dimen>
|
||||
<dimen name="call_self_participant_progress_bar_size">48dp</dimen>
|
||||
<dimen name="zero">0dp</dimen>
|
||||
|
@ -4,6 +4,8 @@
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ @author Marcel Hibbe
|
||||
~ @author Tim Krüger
|
||||
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
@ -211,6 +213,14 @@
|
||||
<string name="nc_call_state_with_phone">%1$s with phone</string>
|
||||
<string name="nc_call_state_with_video">%1$s with video</string>
|
||||
<string name="nc_missed_call">You missed a call from %s</string>
|
||||
<string name="nc_call_button_content_description_pip">Open picture in picture mode</string>
|
||||
<string name="nc_call_button_content_description_audio_output">Change audio output</string>
|
||||
<string name="nc_call_button_content_description_camera">Toggle camera</string>
|
||||
<string name="nc_call_button_content_description_microphone">Toggle microphone</string>
|
||||
<string name="nc_call_button_content_description_hangup">Hangup</string>
|
||||
<string name="nc_call_button_content_description_answer_voice_only">Answer as voice call only</string>
|
||||
<string name="nc_call_button_content_description_answer_video_call">Answer as video call</string>
|
||||
<string name="nc_call_button_content_description_switch_to_self_vide">Switch to self video</string>
|
||||
|
||||
<!-- Picture in Picture -->
|
||||
<string name="nc_pip_microphone_mute">Mute microphone</string>
|
||||
|
@ -1,2 +1,2 @@
|
||||
DO NOT TOUCH; GENERATED BY DRONE
|
||||
<span class="mdl-layout-title">Lint Report: 1 error and 112 warnings</span>
|
||||
<span class="mdl-layout-title">Lint Report: 112 warnings</span>
|
||||
|
Loading…
Reference in New Issue
Block a user