mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-28 16:09:39 +01:00
Merge pull request #1668 from nextcloud/feature/898/userStatus
add user status
This commit is contained in:
commit
2ccbbc5969
@ -194,7 +194,7 @@ dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'com.github.vanniktech:Emoji:0.6.0' // 0.7.0 has display issue - don't update to 0.7.0
|
||||
implementation "com.vanniktech:emoji-google:0.8.0"
|
||||
implementation group: 'androidx.emoji', name: 'emoji-bundled', version: '1.1.0'
|
||||
implementation 'org.michaelevans.colorart:library:0.0.3'
|
||||
implementation "androidx.work:work-runtime:${workVersion}"
|
||||
|
@ -272,7 +272,8 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
|
||||
apiVersion,
|
||||
signatureVerification.userEntity.baseUrl,
|
||||
decryptedPushMessage.id
|
||||
)
|
||||
),
|
||||
null
|
||||
)
|
||||
.repeatWhen { completed ->
|
||||
completed.zipWith(Observable.range(1, 12), { _, i -> i })
|
||||
|
@ -160,6 +160,9 @@ import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class CallActivity extends CallBaseActivity {
|
||||
|
||||
public static final String VIDEO_STREAM_TYPE_SCREEN = "screen";
|
||||
public static final String VIDEO_STREAM_TYPE_VIDEO = "video";
|
||||
|
||||
@Inject
|
||||
NcApi ncApi;
|
||||
@Inject
|
||||
@ -396,7 +399,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
||||
DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(
|
||||
rootEglBase.getEglBaseContext(), true, true);
|
||||
DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
|
||||
DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(
|
||||
rootEglBase.getEglBaseContext());
|
||||
|
||||
peerConnectionFactory = PeerConnectionFactory.builder()
|
||||
.setOptions(options)
|
||||
@ -436,7 +440,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
offerToReceiveVideoString = "false";
|
||||
}
|
||||
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", offerToReceiveVideoString));
|
||||
sdpConstraints.mandatory.add(
|
||||
new MediaConstraints.KeyValuePair("OfferToReceiveVideo", offerToReceiveVideoString));
|
||||
|
||||
sdpConstraintsForMCU.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false"));
|
||||
sdpConstraintsForMCU.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false"));
|
||||
@ -600,7 +605,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
Log.d(TAG, "initGridAdapter");
|
||||
int columns;
|
||||
int participantsInGrid = participantDisplayItems.size();
|
||||
if (getResources() != null && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
if (getResources() != null
|
||||
&& getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
if (participantsInGrid > 2) {
|
||||
columns = 2;
|
||||
} else {
|
||||
@ -618,7 +624,9 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
binding.gridview.setNumColumns(columns);
|
||||
|
||||
binding.conversationRelativeLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
binding.conversationRelativeLayout
|
||||
.getViewTreeObserver()
|
||||
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
binding.conversationRelativeLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
@ -627,7 +635,10 @@ public class CallActivity extends CallBaseActivity {
|
||||
}
|
||||
});
|
||||
|
||||
binding.callInfosLinearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
binding
|
||||
.callInfosLinearLayout
|
||||
.getViewTreeObserver()
|
||||
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
binding.callInfosLinearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
@ -776,7 +787,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
//Create a VideoSource instance
|
||||
if (videoCapturer != null) {
|
||||
SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase.getEglBaseContext());
|
||||
SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread",
|
||||
rootEglBase.getEglBaseContext());
|
||||
videoSource = peerConnectionFactory.createVideoSource(false);
|
||||
videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
|
||||
}
|
||||
@ -1141,14 +1153,19 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
@Override
|
||||
public void onNext(@io.reactivex.annotations.NonNull SignalingSettingsOverall signalingSettingsOverall) {
|
||||
if (signalingSettingsOverall.getOcs() != null && signalingSettingsOverall.getOcs().getSettings() != null) {
|
||||
if (signalingSettingsOverall.getOcs() != null
|
||||
&& signalingSettingsOverall.getOcs().getSettings() != null) {
|
||||
externalSignalingServer = new ExternalSignalingServer();
|
||||
|
||||
if (!TextUtils.isEmpty(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()) &&
|
||||
!TextUtils.isEmpty(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket())) {
|
||||
if (!TextUtils.isEmpty(
|
||||
signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer()) &&
|
||||
!TextUtils.isEmpty(
|
||||
signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket())) {
|
||||
externalSignalingServer = new ExternalSignalingServer();
|
||||
externalSignalingServer.setExternalSignalingServer(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer());
|
||||
externalSignalingServer.setExternalSignalingTicket(signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket());
|
||||
externalSignalingServer.setExternalSignalingServer(
|
||||
signalingSettingsOverall.getOcs().getSettings().getExternalSignalingServer());
|
||||
externalSignalingServer.setExternalSignalingTicket(
|
||||
signalingSettingsOverall.getOcs().getSettings().getExternalSignalingTicket());
|
||||
hasExternalSignalingServer = true;
|
||||
} else {
|
||||
hasExternalSignalingServer = false;
|
||||
@ -1157,8 +1174,17 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
if (!conversationUser.getUserId().equals("?")) {
|
||||
try {
|
||||
userUtils.createOrUpdateUser(null, null, null, null, null, null, null,
|
||||
conversationUser.getId(), null, null, LoganSquare.serialize(externalSignalingServer))
|
||||
userUtils.createOrUpdateUser(null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
conversationUser.getId(),
|
||||
null,
|
||||
null,
|
||||
LoganSquare.serialize(externalSignalingServer))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe();
|
||||
} catch (IOException exception) {
|
||||
@ -1547,14 +1573,16 @@ public class CallActivity extends CallBaseActivity {
|
||||
sessionDescriptionStringWithPreferredCodec);
|
||||
|
||||
if (peerConnectionWrapper.getPeerConnection() != null) {
|
||||
peerConnectionWrapper.getPeerConnection().setRemoteDescription(peerConnectionWrapper
|
||||
.getMagicSdpObserver(), sessionDescriptionWithPreferredCodec);
|
||||
peerConnectionWrapper.getPeerConnection().setRemoteDescription(
|
||||
peerConnectionWrapper.getMagicSdpObserver(),
|
||||
sessionDescriptionWithPreferredCodec);
|
||||
}
|
||||
break;
|
||||
case "candidate":
|
||||
NCIceCandidate ncIceCandidate = ncSignalingMessage.getPayload().getIceCandidate();
|
||||
IceCandidate iceCandidate = new IceCandidate(ncIceCandidate.getSdpMid(),
|
||||
ncIceCandidate.getSdpMLineIndex(), ncIceCandidate.getCandidate());
|
||||
ncIceCandidate.getSdpMLineIndex(),
|
||||
ncIceCandidate.getCandidate());
|
||||
peerConnectionWrapper.addCandidate(iceCandidate);
|
||||
break;
|
||||
case "endOfCandidates":
|
||||
@ -1651,7 +1679,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) {
|
||||
if (shutDownView) {
|
||||
finish();
|
||||
} else if (currentCallStatus == CallStatus.RECONNECTING || currentCallStatus == CallStatus.PUBLISHER_FAILED) {
|
||||
} else if (currentCallStatus == CallStatus.RECONNECTING
|
||||
|| currentCallStatus == CallStatus.PUBLISHER_FAILED) {
|
||||
initiateCall();
|
||||
}
|
||||
}
|
||||
@ -1694,7 +1723,10 @@ public class CallActivity extends CallBaseActivity {
|
||||
long inCallFlag = (long) participant.get("inCall");
|
||||
if (!participant.get("sessionId").equals(currentSessionId)) {
|
||||
boolean isNewSession;
|
||||
Log.d(TAG, " inCallFlag of participant " + participant.get("sessionId").toString().substring(0, 4) + " : " + inCallFlag);
|
||||
Log.d(TAG, " inCallFlag of participant "
|
||||
+ participant.get("sessionId").toString().substring(0, 4)
|
||||
+ " : "
|
||||
+ inCallFlag);
|
||||
isNewSession = inCallFlag != 0;
|
||||
|
||||
if (isNewSession) {
|
||||
@ -1733,12 +1765,12 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
if (hasMCU) {
|
||||
// Ensure that own publishing peer is set up.
|
||||
getPeerConnectionWrapperForSessionIdAndType(webSocketClient.getSessionId(), "video", true);
|
||||
getPeerConnectionWrapperForSessionIdAndType(webSocketClient.getSessionId(), VIDEO_STREAM_TYPE_VIDEO, true);
|
||||
}
|
||||
|
||||
for (String sessionId : newSessions) {
|
||||
Log.d(TAG, " newSession joined: " + sessionId);
|
||||
getPeerConnectionWrapperForSessionIdAndType(sessionId, "video", false);
|
||||
getPeerConnectionWrapperForSessionIdAndType(sessionId, VIDEO_STREAM_TYPE_VIDEO, false);
|
||||
}
|
||||
|
||||
if (newSessions.size() > 0 && !currentCallStatus.equals(CallStatus.IN_CONVERSATION)) {
|
||||
@ -1755,7 +1787,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
Log.d(TAG, "getPeersForCall");
|
||||
int apiVersion = ApiUtils.getCallApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
|
||||
|
||||
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken))
|
||||
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken), null)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new Observer<ParticipantsOverall>() {
|
||||
@Override
|
||||
@ -1790,7 +1822,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
private PeerConnectionWrapper getPeerConnectionWrapperForSessionId(String sessionId, String type) {
|
||||
for (int i = 0; i < peerConnectionWrapperList.size(); i++) {
|
||||
if (peerConnectionWrapperList.get(i).getSessionId().equals(sessionId) && peerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) {
|
||||
if (peerConnectionWrapperList.get(i).getSessionId().equals(sessionId)
|
||||
&& peerConnectionWrapperList.get(i).getVideoStreamType().equals(type)) {
|
||||
return peerConnectionWrapperList.get(i);
|
||||
}
|
||||
}
|
||||
@ -1798,7 +1831,9 @@ public class CallActivity extends CallBaseActivity {
|
||||
return null;
|
||||
}
|
||||
|
||||
private PeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId, String type, boolean publisher) {
|
||||
private PeerConnectionWrapper getPeerConnectionWrapperForSessionIdAndType(String sessionId,
|
||||
String type,
|
||||
boolean publisher) {
|
||||
PeerConnectionWrapper peerConnectionWrapper;
|
||||
if ((peerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId, type)) != null) {
|
||||
return peerConnectionWrapper;
|
||||
@ -1876,7 +1911,7 @@ public class CallActivity extends CallBaseActivity {
|
||||
for (int i = 0; i < peerConnectionWrappers.size(); i++) {
|
||||
peerConnectionWrapper = peerConnectionWrappers.get(i);
|
||||
if (peerConnectionWrapper.getSessionId().equals(sessionId)) {
|
||||
if (peerConnectionWrapper.getVideoStreamType().equals("screen") || !justScreen) {
|
||||
if (VIDEO_STREAM_TYPE_SCREEN.equals(peerConnectionWrapper.getVideoStreamType()) || !justScreen) {
|
||||
runOnUiThread(() -> removeMediaStream(sessionId));
|
||||
deletePeerConnection(peerConnectionWrapper);
|
||||
}
|
||||
@ -1904,7 +1939,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
private void updateSelfVideoViewPosition() {
|
||||
Log.d(TAG, "updateSelfVideoViewPosition");
|
||||
if (!isInPipMode) {
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) binding.selfVideoRenderer.getLayoutParams();
|
||||
FrameLayout.LayoutParams layoutParams =
|
||||
(FrameLayout.LayoutParams) binding.selfVideoRenderer.getLayoutParams();
|
||||
|
||||
DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics();
|
||||
int screenWidthPx = displayMetrics.widthPixels;
|
||||
@ -1941,42 +1977,46 @@ public class CallActivity extends CallBaseActivity {
|
||||
public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) {
|
||||
String sessionId = peerConnectionEvent.getSessionId();
|
||||
|
||||
if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType
|
||||
.PEER_CLOSED)) {
|
||||
endPeerConnection(sessionId, peerConnectionEvent.getVideoStreamType().equals("screen"));
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
|
||||
.PeerConnectionEventType.SENSOR_FAR) ||
|
||||
peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
|
||||
.PeerConnectionEventType.SENSOR_NEAR)) {
|
||||
if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.PEER_CLOSED) {
|
||||
endPeerConnection(sessionId, VIDEO_STREAM_TYPE_SCREEN.equals(peerConnectionEvent.getVideoStreamType()));
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.SENSOR_FAR ||
|
||||
peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.SENSOR_NEAR) {
|
||||
|
||||
if (!isVoiceOnlyCall) {
|
||||
boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
|
||||
.PeerConnectionEventType.SENSOR_FAR) && videoOn;
|
||||
boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.SENSOR_FAR && videoOn;
|
||||
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA) &&
|
||||
(currentCallStatus.equals(CallStatus.CONNECTING) || isConnectionEstablished()) && videoOn
|
||||
&& enableVideo != localVideoTrack.enabled()) {
|
||||
toggleMedia(enableVideo, true);
|
||||
}
|
||||
}
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE)) {
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE) {
|
||||
if (participantDisplayItems.get(sessionId) != null) {
|
||||
participantDisplayItems.get(sessionId).setNick(peerConnectionEvent.getNick());
|
||||
}
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) {
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE && !isVoiceOnlyCall) {
|
||||
if (participantDisplayItems.get(sessionId) != null) {
|
||||
participantDisplayItems.get(sessionId).setStreamEnabled(peerConnectionEvent.getChangeValue());
|
||||
}
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE)) {
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE) {
|
||||
if (participantDisplayItems.get(sessionId) != null) {
|
||||
participantDisplayItems.get(sessionId).setAudioEnabled(peerConnectionEvent.getChangeValue());
|
||||
}
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) {
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED) {
|
||||
currentCallStatus = CallStatus.PUBLISHER_FAILED;
|
||||
webSocketClient.clearResumeId();
|
||||
hangup(false);
|
||||
@ -2074,7 +2114,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("{")
|
||||
.append("\"fn\":\"")
|
||||
.append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))).append("\"")
|
||||
.append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage())))
|
||||
.append("\"")
|
||||
.append(",")
|
||||
.append("\"sessionId\":")
|
||||
.append("\"").append(StringEscapeUtils.escapeJson(callSession)).append("\"")
|
||||
@ -2127,7 +2168,10 @@ public class CallActivity extends CallBaseActivity {
|
||||
this);
|
||||
}
|
||||
|
||||
private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, boolean videoStreamEnabled, String videoStreamType) {
|
||||
private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream,
|
||||
String session,
|
||||
boolean videoStreamEnabled,
|
||||
String videoStreamType) {
|
||||
String nick;
|
||||
if (hasExternalSignalingServer) {
|
||||
nick = webSocketClient.getDisplayNameForSession(session);
|
||||
@ -2416,13 +2460,12 @@ public class CallActivity extends CallBaseActivity {
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.BACKGROUND)
|
||||
public void onMessageEvent(NetworkEvent networkEvent) {
|
||||
if (networkEvent.getNetworkConnectionEvent()
|
||||
.equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) {
|
||||
if (networkEvent.getNetworkConnectionEvent() == NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED) {
|
||||
if (handler != null) {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
} else if (networkEvent.getNetworkConnectionEvent()
|
||||
.equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) {
|
||||
} else if (networkEvent.getNetworkConnectionEvent() ==
|
||||
NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED) {
|
||||
if (handler != null) {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
@ -2516,7 +2559,8 @@ public class CallActivity extends CallBaseActivity {
|
||||
if (isVoiceOnlyCall) {
|
||||
binding.callControls.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.callControls.setVisibility(View.INVISIBLE); // animateCallControls needs this to be invisible for a check.
|
||||
// animateCallControls needs this to be invisible for a check.
|
||||
binding.callControls.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
initViews();
|
||||
|
||||
|
@ -215,7 +215,7 @@ public class CallNotificationActivity extends CallBaseActivity {
|
||||
int apiVersion = ApiUtils.getCallApiVersion(userBeingCalled, new int[]{ApiUtils.APIv4, 1});
|
||||
|
||||
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(apiVersion, userBeingCalled.getBaseUrl(),
|
||||
currentConversation.getToken()))
|
||||
currentConversation.getToken()), null)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.repeatWhen(completed -> completed.zipWith(Observable.range(1, 12), (n, i) -> i)
|
||||
.flatMap(retryCount -> Observable.timer(5, TimeUnit.SECONDS))
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.adapters
|
||||
|
||||
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus
|
||||
|
||||
interface PredefinedStatusClickListener {
|
||||
fun onClick(predefinedStatus: PredefinedStatus)
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.nextcloud.talk.databinding.PredefinedStatusBinding
|
||||
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus
|
||||
|
||||
class PredefinedStatusListAdapter(
|
||||
private val clickListener: PredefinedStatusClickListener,
|
||||
val context: Context
|
||||
) : RecyclerView.Adapter<PredefinedStatusViewHolder>() {
|
||||
internal var list: List<PredefinedStatus> = emptyList()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PredefinedStatusViewHolder {
|
||||
val itemBinding = PredefinedStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return PredefinedStatusViewHolder(itemBinding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PredefinedStatusViewHolder, position: Int) {
|
||||
holder.bind(list[position], clickListener, context)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return list.size
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.adapters
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.databinding.PredefinedStatusBinding
|
||||
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
|
||||
private const val ONE_SECOND_IN_MILLIS = 1000
|
||||
|
||||
class PredefinedStatusViewHolder(private val binding: PredefinedStatusBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(status: PredefinedStatus, clickListener: PredefinedStatusClickListener, context: Context) {
|
||||
binding.root.setOnClickListener { clickListener.onClick(status) }
|
||||
binding.icon.text = status.icon
|
||||
binding.name.text = status.message
|
||||
|
||||
if (status.clearAt == null) {
|
||||
binding.clearAt.text = context.getString(R.string.dontClear)
|
||||
} else {
|
||||
val clearAt = status.clearAt!!
|
||||
if (clearAt.type.equals("period")) {
|
||||
binding.clearAt.text = DisplayUtils.getRelativeTimestamp(
|
||||
context,
|
||||
System.currentTimeMillis() + clearAt.time.toInt() * ONE_SECOND_IN_MILLIS,
|
||||
true
|
||||
)
|
||||
} else {
|
||||
// end-of
|
||||
if (clearAt.time.equals("day")) {
|
||||
binding.clearAt.text = context.getString(R.string.today)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -24,17 +26,12 @@ import android.accounts.Account;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
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.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.databinding.AccountItemBinding;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
@ -44,9 +41,6 @@ import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.emoji.widget.EmojiTextView;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import eu.davidea.flexibleadapter.items.IFilterable;
|
||||
@ -56,10 +50,10 @@ import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.UserItemViewHolder> implements
|
||||
IFilterable<String> {
|
||||
|
||||
private Participant participant;
|
||||
private UserEntity userEntity;
|
||||
private final Participant participant;
|
||||
private final UserEntity userEntity;
|
||||
@Nullable
|
||||
private Account account;
|
||||
private final Account account;
|
||||
|
||||
public AdvancedUserItem(Participant participant, UserEntity userEntity, @Nullable Account account) {
|
||||
this.participant = participant;
|
||||
@ -110,68 +104,70 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) {
|
||||
holder.avatarImageView.setController(null);
|
||||
holder.binding.userIcon.setController(null);
|
||||
|
||||
if (adapter.hasFilter()) {
|
||||
FlexibleUtils.highlightText(
|
||||
holder.contactDisplayName,
|
||||
holder.binding.userName,
|
||||
participant.getDisplayName(),
|
||||
String.valueOf(adapter.getFilter(String.class)),
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
.getResources()
|
||||
.getColor(R.color.colorPrimary));
|
||||
} else {
|
||||
holder.contactDisplayName.setText(participant.getDisplayName());
|
||||
holder.binding.userName.setText(participant.getDisplayName());
|
||||
}
|
||||
|
||||
if (userEntity != null && !TextUtils.isEmpty(userEntity.getBaseUrl())) {
|
||||
String host = Uri.parse(userEntity.getBaseUrl()).getHost();
|
||||
if (!TextUtils.isEmpty(host)) {
|
||||
holder.serverUrl.setText(Uri.parse(userEntity.getBaseUrl()).getHost());
|
||||
holder.binding.account.setText(Uri.parse(userEntity.getBaseUrl()).getHost());
|
||||
} else {
|
||||
holder.serverUrl.setText(userEntity.getBaseUrl());
|
||||
holder.binding.account.setText(userEntity.getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
||||
holder.avatarImageView.getHierarchy().setPlaceholderImage(R.drawable.account_circle_48dp);
|
||||
holder.avatarImageView.getHierarchy().setFailureImage(R.drawable.account_circle_48dp);
|
||||
holder.binding.userIcon.getHierarchy().setPlaceholderImage(R.drawable.account_circle_48dp);
|
||||
holder.binding.userIcon.getHierarchy().setFailureImage(R.drawable.account_circle_48dp);
|
||||
|
||||
if (userEntity != null && userEntity.getBaseUrl() != null &&
|
||||
userEntity.getBaseUrl().startsWith("http://") ||
|
||||
userEntity.getBaseUrl().startsWith("https://")) {
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.avatarImageView.getController())
|
||||
.setOldController(holder.binding.userIcon.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(),
|
||||
participant.getActorId(), R.dimen.small_item_height), null))
|
||||
.setImageRequest(
|
||||
DisplayUtils.getImageRequestForUrl(
|
||||
ApiUtils.getUrlForAvatarWithName(
|
||||
userEntity.getBaseUrl(),
|
||||
participant.getActorId(),
|
||||
R.dimen.small_item_height),
|
||||
null))
|
||||
.build();
|
||||
holder.avatarImageView.setController(draweeController);
|
||||
holder.binding.userIcon.setController(draweeController);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(String constraint) {
|
||||
return participant.getDisplayName() != null &&
|
||||
Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getDisplayName().trim()).find();
|
||||
Pattern
|
||||
.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
|
||||
.matcher(participant.getDisplayName().trim())
|
||||
.find();
|
||||
}
|
||||
|
||||
|
||||
static class UserItemViewHolder extends FlexibleViewHolder {
|
||||
|
||||
@BindView(R.id.user_name)
|
||||
public EmojiTextView contactDisplayName;
|
||||
@BindView(R.id.account)
|
||||
public TextView serverUrl;
|
||||
@BindView(R.id.user_icon)
|
||||
public SimpleDraweeView avatarImageView;
|
||||
public AccountItemBinding binding;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
UserItemViewHolder(View view, FlexibleAdapter adapter) {
|
||||
super(view, adapter);
|
||||
ButterKnife.bind(this, view);
|
||||
binding = AccountItemBinding.bind(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
*
|
||||
* @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>
|
||||
*
|
||||
@ -46,6 +48,9 @@ import com.nextcloud.talk.models.database.CapabilitiesUtil;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage;
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation;
|
||||
import com.nextcloud.talk.models.json.status.Status;
|
||||
import com.nextcloud.talk.models.json.status.StatusType;
|
||||
import com.nextcloud.talk.ui.StatusDrawable;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
@ -68,24 +73,28 @@ import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
public class ConversationItem extends AbstractFlexibleItem<ConversationItem.ConversationItemViewHolder> implements ISectionable<ConversationItem.ConversationItemViewHolder, GenericTextHeaderItem>,
|
||||
IFilterable<String> {
|
||||
|
||||
private static final float STATUS_SIZE_IN_DP = 9f;
|
||||
|
||||
private Conversation conversation;
|
||||
private UserEntity userEntity;
|
||||
private Context context;
|
||||
private GenericTextHeaderItem header;
|
||||
private Status status;
|
||||
|
||||
public ConversationItem(Conversation conversation, UserEntity userEntity, Context activityContext) {
|
||||
public ConversationItem(Conversation conversation, UserEntity userEntity, Context activityContext, Status status) {
|
||||
this.conversation = conversation;
|
||||
this.userEntity = userEntity;
|
||||
this.context = activityContext;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public ConversationItem(Conversation conversation, UserEntity userEntity,
|
||||
Context activityContext, GenericTextHeaderItem genericTextHeaderItem) {
|
||||
Context activityContext, GenericTextHeaderItem genericTextHeaderItem, Status status) {
|
||||
this.conversation = conversation;
|
||||
this.userEntity = userEntity;
|
||||
this.context = activityContext;
|
||||
this.header = genericTextHeaderItem;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -159,7 +168,7 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
|
||||
holder.dialogUnreadBubble.setChipBackgroundColorResource(R.color.colorPrimary);
|
||||
holder.dialogUnreadBubble.setTextColor(Color.WHITE);
|
||||
} else if (conversation.isUnreadMention()) {
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(userEntity, "direct-mention-flag")){
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(userEntity, "direct-mention-flag")) {
|
||||
if (conversation.getUnreadMentionDirect()) {
|
||||
holder.dialogUnreadBubble.setChipBackgroundColorResource(R.color.colorPrimary);
|
||||
holder.dialogUnreadBubble.setTextColor(Color.WHITE);
|
||||
@ -192,18 +201,28 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
|
||||
holder.pinnedConversationImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (Conversation.ConversationType.ROOM_SYSTEM != conversation.getType()) {
|
||||
float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, appContext);
|
||||
holder.userStatusImage.setImageDrawable(new StatusDrawable(
|
||||
status != null ? status.getStatus() : "",
|
||||
status != null ? status.getIcon() : "",
|
||||
size,
|
||||
context.getResources().getColor(R.color.bg_default),
|
||||
appContext));
|
||||
}
|
||||
|
||||
if (conversation.getLastMessage() != null) {
|
||||
holder.dialogDate.setVisibility(View.VISIBLE);
|
||||
holder.dialogDate.setText(DateUtils.getRelativeTimeSpanString(conversation.getLastActivity() * 1000L,
|
||||
System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE));
|
||||
|
||||
if (!TextUtils.isEmpty(conversation.getLastMessage().getSystemMessage()) || Conversation.ConversationType.ROOM_SYSTEM.equals(conversation.getType())) {
|
||||
if (!TextUtils.isEmpty(conversation.getLastMessage().getSystemMessage()) || Conversation.ConversationType.ROOM_SYSTEM == conversation.getType()) {
|
||||
holder.dialogLastMessage.setText(conversation.getLastMessage().getText());
|
||||
} else {
|
||||
String authorDisplayName = "";
|
||||
conversation.getLastMessage().setActiveUser(userEntity);
|
||||
String text;
|
||||
if (conversation.getLastMessage().getMessageType().equals(ChatMessage.MessageType.REGULAR_TEXT_MESSAGE)) {
|
||||
if (conversation.getLastMessage().getMessageType() == ChatMessage.MessageType.REGULAR_TEXT_MESSAGE) {
|
||||
if (conversation.getLastMessage().getActorId().equals(userEntity.getUserId())) {
|
||||
text = String.format(appContext.getString(R.string.nc_formatted_message_you),
|
||||
conversation.getLastMessage().getLastMessageDisplayText());
|
||||
@ -318,6 +337,8 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
|
||||
Chip dialogUnreadBubble;
|
||||
@BindView(R.id.favoriteConversationImageView)
|
||||
ImageView pinnedConversationImageView;
|
||||
@BindView(R.id.user_status_image)
|
||||
ImageView userStatusImage;
|
||||
|
||||
ConversationItemViewHolder(View view, FlexibleAdapter adapter) {
|
||||
super(view, adapter);
|
||||
|
@ -1,7 +1,9 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2022 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
|
||||
@ -29,12 +31,17 @@ import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
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.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
@ -45,23 +52,30 @@ import eu.davidea.flexibleadapter.utils.FlexibleUtils;
|
||||
public class MentionAutocompleteItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
implements IFilterable<String> {
|
||||
|
||||
private static final float STATUS_SIZE_IN_DP = 9f;
|
||||
private static final String NO_ICON = "";
|
||||
public static final String SOURCE_CALLS = "calls";
|
||||
public static final String SOURCE_GUESTS = "guests";
|
||||
private String objectId;
|
||||
private String displayName;
|
||||
|
||||
private String source;
|
||||
private UserEntity currentUser;
|
||||
private Context context;
|
||||
private final String objectId;
|
||||
private final String displayName;
|
||||
private final String status;
|
||||
private final String statusIcon;
|
||||
private final String statusMessage;
|
||||
private final UserEntity currentUser;
|
||||
private final Context context;
|
||||
|
||||
public MentionAutocompleteItem(
|
||||
String objectId,
|
||||
String displayName,
|
||||
String source,
|
||||
Mention mention,
|
||||
UserEntity currentUser,
|
||||
Context activityContext) {
|
||||
this.objectId = objectId;
|
||||
this.displayName = displayName;
|
||||
this.source = source;
|
||||
this.objectId = mention.getId();
|
||||
this.displayName = mention.getLabel();
|
||||
this.source = mention.getSource();
|
||||
this.status = mention.getStatus();
|
||||
this.statusIcon = mention.getStatusIcon();
|
||||
this.statusMessage = mention.getStatusMessage();
|
||||
this.currentUser = currentUser;
|
||||
this.context = activityContext;
|
||||
}
|
||||
@ -94,7 +108,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<UserItem.UserI
|
||||
|
||||
@Override
|
||||
public int getLayoutRes() {
|
||||
return R.layout.rv_item_mention;
|
||||
return R.layout.rv_item_conversation_info_participant;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -118,7 +132,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<UserItem.UserI
|
||||
FlexibleUtils.highlightText(holder.contactDisplayName,
|
||||
displayName,
|
||||
String.valueOf(adapter.getFilter(String.class)),
|
||||
NextcloudTalkApplication.Companion.getSharedApplication()
|
||||
Objects.requireNonNull(NextcloudTalkApplication.Companion.getSharedApplication())
|
||||
.getResources().getColor(R.color.colorPrimary));
|
||||
if (holder.contactMentionId != null) {
|
||||
FlexibleUtils.highlightText(holder.contactMentionId,
|
||||
@ -135,7 +149,9 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<UserItem.UserI
|
||||
}
|
||||
|
||||
if (SOURCE_CALLS.equals(source)) {
|
||||
holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_group);
|
||||
if (holder.participantAvatar != null){
|
||||
holder.participantAvatar.setImageResource(R.drawable.ic_circular_group);
|
||||
}
|
||||
} else {
|
||||
String avatarId = objectId;
|
||||
String avatarUrl = ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(),
|
||||
@ -149,14 +165,62 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<UserItem.UserI
|
||||
R.dimen.avatar_size_big);
|
||||
}
|
||||
|
||||
holder.simpleDraweeView.setController(null);
|
||||
if(holder.participantAvatar != null){
|
||||
holder.participantAvatar.setController(null);
|
||||
}
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.simpleDraweeView.getController())
|
||||
.setOldController(holder.participantAvatar.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(avatarUrl, null))
|
||||
.build();
|
||||
holder.simpleDraweeView.setController(draweeController);
|
||||
holder.participantAvatar.setController(draweeController);
|
||||
}
|
||||
|
||||
drawStatus(holder);
|
||||
}
|
||||
|
||||
private void drawStatus(UserItem.UserItemViewHolder holder) {
|
||||
if (holder.statusMessage != null && holder.participantEmoji != null && holder.userStatusImage != null) {
|
||||
float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context);
|
||||
holder.userStatusImage.setImageDrawable(new StatusDrawable(
|
||||
status,
|
||||
NO_ICON,
|
||||
size,
|
||||
context.getResources().getColor(R.color.bg_default),
|
||||
context));
|
||||
|
||||
if (statusMessage != null) {
|
||||
holder.statusMessage.setText(statusMessage);
|
||||
alignUsernameVertical(holder, 0);
|
||||
} else {
|
||||
holder.statusMessage.setText("");
|
||||
alignUsernameVertical(holder, 10);
|
||||
}
|
||||
|
||||
if (statusIcon != null && !statusIcon.isEmpty()) {
|
||||
holder.participantEmoji.setText(statusIcon);
|
||||
} else {
|
||||
holder.participantEmoji.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (status != null && status.equals(StatusType.DND.getString())) {
|
||||
if (statusMessage == null || statusMessage.isEmpty()) {
|
||||
holder.statusMessage.setText(R.string.dnd);
|
||||
}
|
||||
} else if (status != null && status.equals(StatusType.AWAY.getString())) {
|
||||
if (statusMessage == null || statusMessage.isEmpty()) {
|
||||
holder.statusMessage.setText(R.string.away);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void alignUsernameVertical(UserItem.UserItemViewHolder holder, float densityPixelsFromTop) {
|
||||
ConstraintLayout.LayoutParams layoutParams =
|
||||
(ConstraintLayout.LayoutParams) holder.contactDisplayName.getLayoutParams();
|
||||
layoutParams.topMargin = (int) DisplayUtils.convertDpToPixel(densityPixelsFromTop, context);
|
||||
holder.contactDisplayName.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,7 +2,9 @@
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
|
||||
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
|
||||
*
|
||||
* 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
|
||||
@ -20,15 +22,13 @@
|
||||
|
||||
package com.nextcloud.talk.adapters.items;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.emoji.widget.EmojiTextView;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
@ -38,12 +38,18 @@ import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.participants.Participant.InCallFlags;
|
||||
import com.nextcloud.talk.models.json.status.StatusType;
|
||||
import com.nextcloud.talk.ui.StatusDrawable;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.emoji.widget.EmojiTextView;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
@ -56,12 +62,20 @@ import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder> implements
|
||||
ISectionable<UserItem.UserItemViewHolder, GenericTextHeaderItem>, IFilterable<String> {
|
||||
|
||||
private static final float STATUS_SIZE_IN_DP = 9f;
|
||||
private static final String NO_ICON = "";
|
||||
|
||||
private Context context;
|
||||
private Participant participant;
|
||||
private UserEntity userEntity;
|
||||
private GenericTextHeaderItem header;
|
||||
public boolean isOnline = true;
|
||||
|
||||
public UserItem(Participant participant, UserEntity userEntity, GenericTextHeaderItem genericTextHeaderItem) {
|
||||
public UserItem(Context activityContext,
|
||||
Participant participant,
|
||||
UserEntity userEntity,
|
||||
GenericTextHeaderItem genericTextHeaderItem) {
|
||||
this.context = activityContext;
|
||||
this.participant = participant;
|
||||
this.userEntity = userEntity;
|
||||
this.header = genericTextHeaderItem;
|
||||
@ -109,10 +123,13 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
return new UserItemViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) {
|
||||
|
||||
holder.simpleDraweeView.setController(null);
|
||||
if (holder.participantAvatar != null) {
|
||||
holder.participantAvatar.setController(null);
|
||||
}
|
||||
|
||||
if (holder.checkedImageView != null) {
|
||||
if (participant.isSelected()) {
|
||||
@ -122,20 +139,22 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
}
|
||||
}
|
||||
|
||||
drawStatus(holder);
|
||||
|
||||
if (!isOnline) {
|
||||
holder.contactDisplayName.setTextColor(ResourcesCompat.getColor(
|
||||
holder.contactDisplayName.getContext().getResources(),
|
||||
R.color.medium_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.simpleDraweeView.setAlpha(0.38f);
|
||||
holder.participantAvatar.setAlpha(0.38f);
|
||||
} else {
|
||||
holder.contactDisplayName.setTextColor(ResourcesCompat.getColor(
|
||||
holder.contactDisplayName.getContext().getResources(),
|
||||
R.color.high_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.simpleDraweeView.setAlpha(1.0f);
|
||||
holder.participantAvatar.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
if (adapter.hasFilter()) {
|
||||
@ -155,9 +174,9 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
"groups".equals(participant.getSource()) ||
|
||||
participant.getActorType() == Participant.ActorType.CIRCLES ||
|
||||
"circles".equals(participant.getSource())) {
|
||||
holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_group);
|
||||
holder.participantAvatar.setImageResource(R.drawable.ic_circular_group);
|
||||
} else if (participant.getActorType() == Participant.ActorType.EMAILS) {
|
||||
holder.simpleDraweeView.setImageResource(R.drawable.ic_circular_mail);
|
||||
holder.participantAvatar.setImageResource(R.drawable.ic_circular_mail);
|
||||
} else if (participant.getActorType() == Participant.ActorType.GUESTS ||
|
||||
Participant.ParticipantType.GUEST.equals(participant.getType()) ||
|
||||
Participant.ParticipantType.GUEST_MODERATOR.equals(participant.getType())) {
|
||||
@ -170,21 +189,21 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
}
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.simpleDraweeView.getController())
|
||||
.setOldController(holder.participantAvatar.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameForGuests(userEntity.getBaseUrl(),
|
||||
displayName, R.dimen.avatar_size), null))
|
||||
.build();
|
||||
holder.simpleDraweeView.setController(draweeController);
|
||||
holder.participantAvatar.setController(draweeController);
|
||||
|
||||
} else if (participant.getActorType() == Participant.ActorType.USERS || participant.getSource().equals("users")) {
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(holder.simpleDraweeView.getController())
|
||||
.setOldController(holder.participantAvatar.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(),
|
||||
participant.getActorId(), R.dimen.avatar_size), null))
|
||||
.build();
|
||||
holder.simpleDraweeView.setController(draweeController);
|
||||
holder.participantAvatar.setController(draweeController);
|
||||
}
|
||||
|
||||
Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources();
|
||||
@ -243,13 +262,56 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
break;
|
||||
}
|
||||
|
||||
if (!holder.contactMentionId.getText().equals(userType)) {
|
||||
holder.contactMentionId.setText(userType);
|
||||
if (!userType.equals(NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_user))) {
|
||||
holder.contactMentionId.setText("(" + userType + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawStatus(UserItemViewHolder holder) {
|
||||
if (holder.statusMessage != null && holder.participantEmoji != null && holder.userStatusImage != null) {
|
||||
float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context);
|
||||
holder.userStatusImage.setImageDrawable(new StatusDrawable(
|
||||
participant.status,
|
||||
NO_ICON,
|
||||
size,
|
||||
context.getResources().getColor(R.color.bg_default),
|
||||
context));
|
||||
|
||||
if (participant.statusMessage != null) {
|
||||
holder.statusMessage.setText(participant.statusMessage);
|
||||
alignUsernameVertical(holder, 0);
|
||||
} else {
|
||||
holder.statusMessage.setText("");
|
||||
alignUsernameVertical(holder, 10);
|
||||
}
|
||||
|
||||
if (participant.statusIcon != null && !participant.statusIcon.isEmpty()) {
|
||||
holder.participantEmoji.setText(participant.statusIcon);
|
||||
} else {
|
||||
holder.participantEmoji.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (participant.status != null && participant.status.equals(StatusType.DND.getString())) {
|
||||
if (participant.statusMessage == null || participant.statusMessage.isEmpty()) {
|
||||
holder.statusMessage.setText(R.string.dnd);
|
||||
}
|
||||
} else if (participant.status != null && participant.status.equals(StatusType.AWAY.getString())) {
|
||||
if (participant.statusMessage == null || participant.statusMessage.isEmpty()) {
|
||||
holder.statusMessage.setText(R.string.away);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void alignUsernameVertical(UserItem.UserItemViewHolder holder, float densityPixelsFromTop) {
|
||||
ConstraintLayout.LayoutParams layoutParams =
|
||||
(ConstraintLayout.LayoutParams) holder.contactDisplayName.getLayoutParams();
|
||||
layoutParams.topMargin = (int) DisplayUtils.convertDpToPixel(densityPixelsFromTop, context);
|
||||
holder.contactDisplayName.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(String constraint) {
|
||||
return participant.getDisplayName() != null &&
|
||||
@ -271,8 +333,9 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
|
||||
@BindView(R.id.name_text)
|
||||
public EmojiTextView contactDisplayName;
|
||||
@BindView(R.id.simple_drawee_view)
|
||||
public SimpleDraweeView simpleDraweeView;
|
||||
@Nullable
|
||||
@BindView(R.id.avatar_drawee_view)
|
||||
public SimpleDraweeView participantAvatar;
|
||||
@Nullable
|
||||
@BindView(R.id.secondary_text)
|
||||
public EmojiTextView contactMentionId;
|
||||
@ -282,6 +345,15 @@ public class UserItem extends AbstractFlexibleItem<UserItem.UserItemViewHolder>
|
||||
@Nullable
|
||||
@BindView(R.id.checkedImageView)
|
||||
ImageView checkedImageView;
|
||||
@Nullable
|
||||
@BindView(R.id.participant_status_emoji)
|
||||
com.vanniktech.emoji.EmojiEditText participantEmoji;
|
||||
@Nullable
|
||||
@BindView(R.id.user_status_image)
|
||||
ImageView userStatusImage;
|
||||
@Nullable
|
||||
@BindView(R.id.conversation_info_status_message)
|
||||
EmojiTextView statusMessage;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
|
@ -40,6 +40,8 @@ import com.nextcloud.talk.models.json.push.PushRegistrationOverall;
|
||||
import com.nextcloud.talk.models.json.search.ContactsByNumberOverall;
|
||||
import com.nextcloud.talk.models.json.signaling.SignalingOverall;
|
||||
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall;
|
||||
import com.nextcloud.talk.models.json.status.StatusOverall;
|
||||
import com.nextcloud.talk.models.json.statuses.StatusesOverall;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
|
||||
|
||||
@ -185,7 +187,8 @@ public interface NcApi {
|
||||
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
|
||||
*/
|
||||
@GET
|
||||
Observable<ParticipantsOverall> getPeersForCall(@Header("Authorization") String authorization, @Url String url);
|
||||
Observable<ParticipantsOverall> getPeersForCall(@Header("Authorization") String authorization, @Url String url,
|
||||
@QueryMap Map<String, Boolean> fields);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST
|
||||
@ -333,7 +336,8 @@ public interface NcApi {
|
||||
@GET
|
||||
Observable<MentionOverall> getMentionAutocompleteSuggestions(@Header("Authorization") String authorization,
|
||||
@Url String url, @Query("search") String query,
|
||||
@Nullable @Query("limit") Integer limit);
|
||||
@Nullable @Query("limit") Integer limit,
|
||||
@QueryMap Map<String, String> fields);
|
||||
|
||||
// Url is: /api/{apiVersion}/room/{token}/pin
|
||||
@POST
|
||||
@ -443,4 +447,42 @@ public interface NcApi {
|
||||
@GET
|
||||
Observable<RoomsOverall> getOpenConversations(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
|
||||
/*
|
||||
* OCS Status API
|
||||
*/
|
||||
@GET
|
||||
Observable<StatusOverall> status(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
@GET
|
||||
Observable<ResponseBody> getPredefinedStatuses(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
@DELETE
|
||||
Observable<GenericOverall> statusDeleteMessage(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
|
||||
@FormUrlEncoded
|
||||
@PUT
|
||||
Observable<GenericOverall> setPredefinedStatusMessage(@Header("Authorization") String authorization,
|
||||
@Url String url,
|
||||
@Field("messageId") String selectedPredefinedMessageId,
|
||||
@Field("clearAt") Long clearAt);
|
||||
|
||||
@FormUrlEncoded
|
||||
@PUT
|
||||
Observable<GenericOverall> setCustomStatusMessage(@Header("Authorization") String authorization,
|
||||
@Url String url,
|
||||
@Field("statusIcon") String statusIcon,
|
||||
@Field("message") String message,
|
||||
@Field("clearAt") Long clearAt);
|
||||
|
||||
@FormUrlEncoded
|
||||
@PUT
|
||||
Observable<GenericOverall> setStatusType(@Header("Authorization") String authorization,
|
||||
@Url String url,
|
||||
@Field("statusType") String statusType);
|
||||
|
||||
@GET
|
||||
Observable<StatusesOverall> getUserStatuses(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ import com.nextcloud.talk.utils.database.user.UserModule
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import com.nextcloud.talk.webrtc.MagicWebRTCUtils
|
||||
import com.vanniktech.emoji.EmojiManager
|
||||
import com.vanniktech.emoji.googlecompat.GoogleCompatEmojiProvider
|
||||
import com.vanniktech.emoji.google.GoogleEmojiProvider
|
||||
import de.cotech.hw.SecurityKeyManager
|
||||
import de.cotech.hw.SecurityKeyManagerConfig
|
||||
import okhttp3.OkHttpClient
|
||||
@ -188,7 +188,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
|
||||
config.setReplaceAll(true)
|
||||
val emojiCompat = EmojiCompat.init(config)
|
||||
|
||||
EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat))
|
||||
EmojiManager.install(GoogleEmojiProvider())
|
||||
|
||||
NotificationUtils.registerNotificationChannels(applicationContext, appPreferences)
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2022 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
|
||||
@ -24,20 +26,14 @@ import android.content.Context;
|
||||
import android.text.format.Formatter;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
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.components.filebrowser.models.BrowserFile;
|
||||
import com.nextcloud.talk.databinding.RvItemBrowserFileBinding;
|
||||
import com.nextcloud.talk.interfaces.SelectionInterface;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
@ -49,9 +45,8 @@ import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import eu.davidea.flexibleadapter.items.IFilterable;
|
||||
@ -59,12 +54,12 @@ import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHolder> implements IFilterable<String> {
|
||||
public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.BrowserFileItemViewHolder> implements IFilterable<String> {
|
||||
@Inject
|
||||
Context context;
|
||||
private BrowserFile browserFile;
|
||||
private UserEntity activeUser;
|
||||
private SelectionInterface selectionInterface;
|
||||
private final BrowserFile browserFile;
|
||||
private final UserEntity activeUser;
|
||||
private final SelectionInterface selectionInterface;
|
||||
private boolean selected;
|
||||
|
||||
public BrowserFileItem(BrowserFile browserFile, UserEntity activeUser, SelectionInterface selectionInterface) {
|
||||
@ -94,9 +89,8 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new ViewHolder(view, adapter);
|
||||
|
||||
public BrowserFileItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new BrowserFileItemViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
private boolean isSelected() {
|
||||
@ -108,8 +102,11 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, ViewHolder holder, int position, List<Object> payloads) {
|
||||
holder.fileIconImageView.setController(null);
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter,
|
||||
BrowserFileItemViewHolder holder,
|
||||
int position,
|
||||
List<Object> payloads) {
|
||||
holder.binding.fileIcon.setController(null);
|
||||
if (!browserFile.isAllowedToReShare() || browserFile.isEncrypted()) {
|
||||
holder.itemView.setEnabled(false);
|
||||
holder.itemView.setAlpha(0.38f);
|
||||
@ -119,31 +116,32 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
||||
}
|
||||
|
||||
if (browserFile.isEncrypted()) {
|
||||
holder.fileEncryptedImageView.setVisibility(View.VISIBLE);
|
||||
holder.binding.fileEncryptedImageView.setVisibility(View.VISIBLE);
|
||||
|
||||
} else {
|
||||
holder.fileEncryptedImageView.setVisibility(View.GONE);
|
||||
holder.binding.fileEncryptedImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (browserFile.isFavorite()) {
|
||||
holder.fileFavoriteImageView.setVisibility(View.VISIBLE);
|
||||
holder.binding.fileFavoriteImageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.fileFavoriteImageView.setVisibility(View.GONE);
|
||||
holder.binding.fileFavoriteImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (selectionInterface.shouldOnlySelectOneImageFile()) {
|
||||
if (browserFile.isFile && browserFile.mimeType.startsWith("image/")) {
|
||||
holder.selectFileCheckbox.setVisibility(View.VISIBLE);
|
||||
holder.binding.selectFileCheckbox.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.selectFileCheckbox.setVisibility(View.GONE);
|
||||
holder.binding.selectFileCheckbox.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
holder.selectFileCheckbox.setVisibility(View.VISIBLE);
|
||||
holder.binding.selectFileCheckbox.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
holder
|
||||
.fileIconImageView
|
||||
.binding
|
||||
.fileIcon
|
||||
.getHierarchy()
|
||||
.setPlaceholderImage(
|
||||
AppCompatResources.getDrawable(
|
||||
@ -160,25 +158,28 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(path, null))
|
||||
.build();
|
||||
holder.fileIconImageView.setController(draweeController);
|
||||
holder.binding.fileIcon.setController(draweeController);
|
||||
}
|
||||
}
|
||||
|
||||
holder.filenameTextView.setText(browserFile.getDisplayName());
|
||||
holder.fileModifiedTextView.setText(String.format(context.getString(R.string.nc_last_modified),
|
||||
holder.binding.filenameTextView.setText(browserFile.getDisplayName());
|
||||
holder.binding.fileModifiedInfo.setText(String.format(context.getString(R.string.nc_last_modified),
|
||||
Formatter.formatShortFileSize(context, browserFile.getSize()),
|
||||
DateUtils.INSTANCE.getLocalDateTimeStringFromTimestamp(browserFile.getModifiedTimestamp())));
|
||||
setSelected(selectionInterface.isPathSelected(browserFile.getPath()));
|
||||
holder.selectFileCheckbox.setChecked(isSelected());
|
||||
holder.binding.selectFileCheckbox.setChecked(isSelected());
|
||||
|
||||
if (!browserFile.isEncrypted()) {
|
||||
holder.selectFileCheckbox.setOnClickListener(new View.OnClickListener() {
|
||||
holder.binding.selectFileCheckbox.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!browserFile.isAllowedToReShare()) {
|
||||
((CheckBox) v).setChecked(false);
|
||||
Toast.makeText(context, context.getResources().getString(R.string.nc_file_browser_reshare_forbidden),
|
||||
Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getResources().getString(R.string.nc_file_browser_reshare_forbidden),
|
||||
Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} else if (((CheckBox) v).isChecked() != isSelected()) {
|
||||
setSelected(((CheckBox) v).isChecked());
|
||||
selectionInterface.toggleBrowserItemSelection(browserFile.getPath());
|
||||
@ -187,8 +188,8 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
||||
});
|
||||
}
|
||||
|
||||
holder.filenameTextView.setSelected(true);
|
||||
holder.fileModifiedTextView.setSelected(true);
|
||||
holder.binding.filenameTextView.setSelected(true);
|
||||
holder.binding.fileModifiedInfo.setSelected(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -196,24 +197,13 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
||||
return false;
|
||||
}
|
||||
|
||||
static class ViewHolder extends FlexibleViewHolder {
|
||||
static class BrowserFileItemViewHolder extends FlexibleViewHolder {
|
||||
|
||||
@BindView(R.id.file_icon)
|
||||
public SimpleDraweeView fileIconImageView;
|
||||
@BindView(R.id.file_modified_info)
|
||||
public TextView fileModifiedTextView;
|
||||
@BindView(R.id.filename_text_view)
|
||||
public TextView filenameTextView;
|
||||
@BindView(R.id.select_file_checkbox)
|
||||
public CheckBox selectFileCheckbox;
|
||||
@BindView(R.id.fileEncryptedImageView)
|
||||
public ImageView fileEncryptedImageView;
|
||||
@BindView(R.id.fileFavoriteImageView)
|
||||
public ImageView fileFavoriteImageView;
|
||||
RvItemBrowserFileBinding binding;
|
||||
|
||||
ViewHolder(View view, FlexibleAdapter adapter) {
|
||||
BrowserFileItemViewHolder(View view, FlexibleAdapter adapter) {
|
||||
super(view, adapter);
|
||||
ButterKnife.bind(this, view);
|
||||
binding = RvItemBrowserFileBinding.bind(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,6 +551,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ
|
||||
}
|
||||
|
||||
UserItem newContactItem = new UserItem(
|
||||
getApplicationContext(),
|
||||
participant,
|
||||
currentUser,
|
||||
userHeaderItems.get(headerTitle)
|
||||
|
@ -4,6 +4,8 @@
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* @author Tim Krüger
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
@ -88,6 +90,7 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
import java.util.Calendar
|
||||
import java.util.Collections
|
||||
import java.util.Comparator
|
||||
import java.util.HashMap
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -120,7 +123,7 @@ class ConversationInfoController(args: Bundle) :
|
||||
private var conversation: Conversation? = null
|
||||
|
||||
private var adapter: FlexibleAdapter<UserItem>? = null
|
||||
private var recyclerViewItems: MutableList<UserItem> = ArrayList()
|
||||
private var userItems: MutableList<UserItem> = ArrayList()
|
||||
|
||||
private var saveStateHandler: LovelySaveStateHandler? = null
|
||||
|
||||
@ -362,7 +365,7 @@ class ConversationInfoController(args: Bundle) :
|
||||
private fun setupAdapter() {
|
||||
if (activity != null) {
|
||||
if (adapter == null) {
|
||||
adapter = FlexibleAdapter(recyclerViewItems, activity, true)
|
||||
adapter = FlexibleAdapter(userItems, activity, true)
|
||||
}
|
||||
|
||||
val layoutManager = SmoothScrollLinearLayoutManager(activity)
|
||||
@ -378,12 +381,12 @@ class ConversationInfoController(args: Bundle) :
|
||||
var userItem: UserItem
|
||||
var participant: Participant
|
||||
|
||||
recyclerViewItems = ArrayList()
|
||||
userItems = ArrayList()
|
||||
var ownUserItem: UserItem? = null
|
||||
|
||||
for (i in participants.indices) {
|
||||
participant = participants[i]
|
||||
userItem = UserItem(participant, conversationUser, null)
|
||||
userItem = UserItem(router.activity, participant, conversationUser, null)
|
||||
if (participant.sessionId != null) {
|
||||
userItem.isOnline = !participant.sessionId.equals("0")
|
||||
} else {
|
||||
@ -395,20 +398,20 @@ class ConversationInfoController(args: Bundle) :
|
||||
ownUserItem.model.sessionId = "-1"
|
||||
ownUserItem.isOnline = true
|
||||
} else {
|
||||
recyclerViewItems.add(userItem)
|
||||
userItems.add(userItem)
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(recyclerViewItems, UserItemComparator())
|
||||
Collections.sort(userItems, UserItemComparator())
|
||||
|
||||
if (ownUserItem != null) {
|
||||
recyclerViewItems.add(0, ownUserItem)
|
||||
userItems.add(0, ownUserItem)
|
||||
}
|
||||
|
||||
setupAdapter()
|
||||
|
||||
binding.participantsListCategory.visibility = View.VISIBLE
|
||||
adapter!!.updateDataSet(recyclerViewItems)
|
||||
adapter!!.updateDataSet(userItems)
|
||||
}
|
||||
|
||||
override val title: String
|
||||
@ -426,9 +429,12 @@ class ConversationInfoController(args: Bundle) :
|
||||
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
}
|
||||
|
||||
val fieldMap = HashMap<String, Boolean>()
|
||||
fieldMap["includeStatus"] = true
|
||||
|
||||
ncApi?.getPeersForCall(
|
||||
credentials,
|
||||
ApiUtils.getUrlForParticipants(apiVersion, conversationUser!!.baseUrl, conversationToken)
|
||||
ApiUtils.getUrlForParticipants(apiVersion, conversationUser!!.baseUrl, conversationToken), fieldMap
|
||||
)
|
||||
?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
@ -462,7 +468,7 @@ class ConversationInfoController(args: Bundle) :
|
||||
val bundle = Bundle()
|
||||
val existingParticipantsId = arrayListOf<String>()
|
||||
|
||||
for (userItem in recyclerViewItems) {
|
||||
for (userItem in userItems) {
|
||||
if (userItem.model.getActorType() == USERS) {
|
||||
existingParticipantsId.add(userItem.model.getActorId())
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.activities.MainActivity;
|
||||
import com.nextcloud.talk.adapters.items.ConversationItem;
|
||||
@ -72,8 +71,6 @@ import com.nextcloud.talk.adapters.items.GenericTextHeaderItem;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.controllers.base.BaseController;
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum;
|
||||
import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController;
|
||||
import com.nextcloud.talk.events.ConversationsListFetchDataEvent;
|
||||
import com.nextcloud.talk.events.EventStatus;
|
||||
import com.nextcloud.talk.interfaces.ConversationMenuInterface;
|
||||
@ -84,7 +81,8 @@ import com.nextcloud.talk.jobs.UploadAndShareFilesWorker;
|
||||
import com.nextcloud.talk.models.database.CapabilitiesUtil;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.status.Status;
|
||||
import com.nextcloud.talk.models.json.statuses.StatusesOverall;
|
||||
import com.nextcloud.talk.ui.dialog.ChooseAccountDialogFragment;
|
||||
import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
@ -132,6 +130,7 @@ import butterknife.BindView;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
@ -191,8 +190,6 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
private SearchView searchView;
|
||||
private String searchQuery;
|
||||
|
||||
private View view;
|
||||
|
||||
private String credentials;
|
||||
|
||||
private boolean adapterWasNull = true;
|
||||
@ -220,6 +217,8 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
|
||||
private ConversationsListBottomDialog conversationsListBottomDialog;
|
||||
|
||||
private HashMap<String, Status> userStatuses = new HashMap<>();
|
||||
|
||||
public ConversationsListController(Bundle bundle) {
|
||||
super();
|
||||
setHasOptionsMenu(true);
|
||||
@ -473,6 +472,37 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
|
||||
@SuppressLint("LongLogTag")
|
||||
public void fetchData() {
|
||||
fetchUserStatuses();
|
||||
}
|
||||
|
||||
private void fetchUserStatuses() {
|
||||
ncApi.getUserStatuses(credentials, ApiUtils.getUrlForUserStatuses(currentUser.getBaseUrl()))
|
||||
.subscribe(new Observer<StatusesOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull StatusesOverall statusesOverall) {
|
||||
for (Status status : statusesOverall.getOcs().getData()) {
|
||||
userStatuses.put(status.getUserId(), status);
|
||||
}
|
||||
fetchRooms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@io.reactivex.annotations.NonNull Throwable e) {
|
||||
Log.e(TAG, "failed to fetch user statuses", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void fetchRooms() {
|
||||
dispose(null);
|
||||
|
||||
isRefreshing = true;
|
||||
@ -531,14 +561,16 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
ConversationItem conversationItem = new ConversationItem(
|
||||
conversation,
|
||||
currentUser,
|
||||
getActivity());
|
||||
getActivity(),
|
||||
userStatuses.get(conversation.name));
|
||||
conversationItems.add(conversationItem);
|
||||
|
||||
ConversationItem conversationItemWithHeader = new ConversationItem(
|
||||
conversation,
|
||||
currentUser,
|
||||
getActivity(),
|
||||
callHeaderItems.get(headerTitle));
|
||||
callHeaderItems.get(headerTitle),
|
||||
userStatuses.get(conversation.name));
|
||||
conversationItemsWithHeader.add(conversationItemWithHeader);
|
||||
}
|
||||
}
|
||||
@ -610,7 +642,8 @@ public class ConversationsListController extends BaseController implements Searc
|
||||
conversation,
|
||||
currentUser,
|
||||
getActivity(),
|
||||
callHeaderItems.get(headerTitle));
|
||||
callHeaderItems.get(headerTitle),
|
||||
userStatuses.get(conversation.name));
|
||||
|
||||
openConversationItems.add(conversationItem);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public abstract class CapabilitiesUtil {
|
||||
Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class);
|
||||
if (capabilities.getExternalCapability() != null &&
|
||||
capabilities.getExternalCapability().containsKey("v1")) {
|
||||
return capabilities.getExternalCapability().get("v1").contains("capabilityName");
|
||||
return capabilities.getExternalCapability().get("v1").contains(capabilityName);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to get capabilities for the user");
|
||||
@ -175,6 +175,22 @@ public abstract class CapabilitiesUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isUserStatusAvailable(@Nullable UserEntity user) {
|
||||
if (user != null && user.getCapabilities() != null) {
|
||||
try {
|
||||
Capabilities capabilities = LoganSquare.parse(user.getCapabilities(), Capabilities.class);
|
||||
if (capabilities.getUserStatusCapability() != null &&
|
||||
capabilities.getUserStatusCapability().getEnabled() &&
|
||||
capabilities.getUserStatusCapability().getSupportsEmoji()) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to get capabilities for the user");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getAttachmentFolder(@Nullable UserEntity user) {
|
||||
if (user != null && user.getCapabilities() != null) {
|
||||
try {
|
||||
|
@ -38,8 +38,10 @@ data class Capabilities(
|
||||
@JsonField(name = ["external"])
|
||||
var externalCapability: HashMap<String, List<String>>?,
|
||||
@JsonField(name = ["provisioning_api"])
|
||||
var provisioningCapability: ProvisioningCapability?
|
||||
var provisioningCapability: ProvisioningCapability?,
|
||||
@JsonField(name = ["user_status"])
|
||||
var userStatusCapability: UserStatusCapability?
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null, null, null, null, null)
|
||||
constructor() : this(null, null, null, null, null, null)
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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
|
||||
* 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.models.json.capabilities
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class UserStatusCapability(
|
||||
@JsonField(name = ["enabled"])
|
||||
var enabled: Boolean,
|
||||
@JsonField(name = ["supports_emoji"])
|
||||
var supportsEmoji: Boolean
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(false, false)
|
||||
}
|
@ -1,110 +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.models.json.mention;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
@JsonObject
|
||||
public class Mention {
|
||||
@JsonField(name = "id")
|
||||
String id;
|
||||
|
||||
@JsonField(name = "label")
|
||||
String label;
|
||||
|
||||
// type of user (guests or users or calls)
|
||||
@JsonField(name = "source")
|
||||
String source;
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Mention)) {
|
||||
return false;
|
||||
}
|
||||
final Mention other = (Mention) o;
|
||||
if (!other.canEqual((Object) this)) {
|
||||
return false;
|
||||
}
|
||||
final Object this$id = this.getId();
|
||||
final Object other$id = other.getId();
|
||||
if (this$id == null ? other$id != null : !this$id.equals(other$id)) {
|
||||
return false;
|
||||
}
|
||||
final Object this$label = this.getLabel();
|
||||
final Object other$label = other.getLabel();
|
||||
if (this$label == null ? other$label != null : !this$label.equals(other$label)) {
|
||||
return false;
|
||||
}
|
||||
final Object this$source = this.getSource();
|
||||
final Object other$source = other.getSource();
|
||||
|
||||
return this$source == null ? other$source == null : this$source.equals(other$source);
|
||||
}
|
||||
|
||||
protected boolean canEqual(final Object other) {
|
||||
return other instanceof Mention;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
final int PRIME = 59;
|
||||
int result = 1;
|
||||
final Object $id = this.getId();
|
||||
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
|
||||
final Object $label = this.getLabel();
|
||||
result = result * PRIME + ($label == null ? 43 : $label.hashCode());
|
||||
final Object $source = this.getSource();
|
||||
result = result * PRIME + ($source == null ? 43 : $source.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Mention(id=" + this.getId() + ", label=" + this.getLabel() + ", source=" + this.getSource() + ")";
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.models.json.mention
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class Mention(
|
||||
@JsonField(name = ["id"])
|
||||
var id: String,
|
||||
|
||||
@JsonField(name = ["label"])
|
||||
var label: String,
|
||||
|
||||
// type of user (guests or users or calls)
|
||||
@JsonField(name = ["source"])
|
||||
var source: String,
|
||||
|
||||
@JsonField(name = ["status"])
|
||||
var status: String?,
|
||||
|
||||
@JsonField(name = ["statusIcon"])
|
||||
var statusIcon: String?,
|
||||
|
||||
@JsonField(name = ["statusMessage"])
|
||||
var statusMessage: String?
|
||||
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this("", "", "", "", "", "")
|
||||
}
|
@ -78,6 +78,15 @@ public class Participant {
|
||||
@JsonField(name = "inCall")
|
||||
public Object inCall;
|
||||
|
||||
@JsonField(name = "status")
|
||||
public String status;
|
||||
|
||||
@JsonField(name = "statusIcon")
|
||||
public String statusIcon;
|
||||
|
||||
@JsonField(name = "statusMessage")
|
||||
public String statusMessage;
|
||||
|
||||
public String source;
|
||||
|
||||
public boolean selected;
|
||||
|
@ -0,0 +1,18 @@
|
||||
package com.nextcloud.talk.models.json.status
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class ClearAt(
|
||||
@JsonField(name = ["type"])
|
||||
var type: String,
|
||||
@JsonField(name = ["time"])
|
||||
var time: String
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this("type", "time")
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
*
|
||||
* 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.models.json.status
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class Status(
|
||||
@JsonField(name = ["userId"])
|
||||
var userId: String?,
|
||||
@JsonField(name = ["message"])
|
||||
var message: String?,
|
||||
/* TODO: Change to enum */
|
||||
@JsonField(name = ["messageId"])
|
||||
var messageId: String?,
|
||||
@JsonField(name = ["messageIsPredefined"])
|
||||
var messageIsPredefined: Boolean,
|
||||
@JsonField(name = ["icon"])
|
||||
var icon: String?,
|
||||
@JsonField(name = ["clearAt"])
|
||||
var clearAt: Long = 0,
|
||||
/* TODO: Change to enum */
|
||||
@JsonField(name = ["status"])
|
||||
var status: String = "offline",
|
||||
@JsonField(name = ["statusIsUserDefined"])
|
||||
var statusIsUserDefined: Boolean
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null, null, null, false, null, 0, "offline", false)
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
*
|
||||
* 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.models.json.status;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOCS;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonObject
|
||||
public class StatusOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
public Status data;
|
||||
|
||||
public Status getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public void setData(Status data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
StatusOCS that = (StatusOCS) o;
|
||||
return Objects.equals(data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatusOCS{" +
|
||||
"data=" + data +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
*
|
||||
* 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.models.json.status;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonObject
|
||||
public class StatusOverall {
|
||||
@JsonField(name = "ocs")
|
||||
public StatusOCS ocs;
|
||||
|
||||
public StatusOCS getOcs() {
|
||||
return this.ocs;
|
||||
}
|
||||
|
||||
public void setOcs(StatusOCS ocs) {
|
||||
this.ocs = ocs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
StatusOverall that = (StatusOverall) o;
|
||||
return Objects.equals(ocs, that.ocs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(ocs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatusOverall{" +
|
||||
"ocs=" + ocs +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.nextcloud.talk.models.json.status
|
||||
|
||||
enum class StatusType(val string: String) {
|
||||
ONLINE("online"),
|
||||
OFFLINE("offline"),
|
||||
DND("dnd"),
|
||||
AWAY("away"),
|
||||
INVISIBLE("invisible");
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.nextcloud.talk.models.json.status.predefined
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import com.nextcloud.talk.models.json.status.ClearAt
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class PredefinedStatus(
|
||||
@JsonField(name = ["id"])
|
||||
var id: String,
|
||||
@JsonField(name = ["icon"])
|
||||
var icon: String,
|
||||
@JsonField(name = ["message"])
|
||||
var message: String,
|
||||
@JsonField(name = ["clearAt"])
|
||||
var clearAt: ClearAt?
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this("id", "icon", "message", null)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
|
||||
*
|
||||
* 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.models.json.status.predefined
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import com.nextcloud.talk.models.json.generic.GenericOCS
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class PredefinedStatusOCS(
|
||||
@JsonField(name = ["data"])
|
||||
var data: List<PredefinedStatus>?
|
||||
) : GenericOCS(), Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
|
||||
* 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.models.json.status.predefined
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class PredefinedStatusOverall(
|
||||
@JsonField(name = ["ocs"])
|
||||
var ocs: PredefinedStatusOCS? = null
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null)
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
*
|
||||
* 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.models.json.statuses;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOCS;
|
||||
import com.nextcloud.talk.models.json.status.Status;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonObject
|
||||
public class StatusesOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
public List<Status> data;
|
||||
|
||||
public List<Status> getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public void setData(List<Status> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
StatusesOCS that = (StatusesOCS) o;
|
||||
return Objects.equals(data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatusesOCS{" +
|
||||
"data=" + data +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tim Krüger
|
||||
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
|
||||
*
|
||||
* 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.models.json.statuses;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonObject
|
||||
public class StatusesOverall {
|
||||
@JsonField(name = "ocs")
|
||||
public StatusesOCS ocs;
|
||||
|
||||
public StatusesOCS getOcs() {
|
||||
return this.ocs;
|
||||
}
|
||||
|
||||
public void setOcs(StatusesOCS ocs) {
|
||||
this.ocs = ocs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
StatusesOverall that = (StatusesOverall) o;
|
||||
return Objects.equals(ocs, that.ocs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(ocs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatusesOverall{" +
|
||||
"ocs=" + ocs +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
*
|
||||
* @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>
|
||||
*
|
||||
@ -22,8 +24,11 @@
|
||||
|
||||
package com.nextcloud.talk.presenters;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.nextcloud.talk.adapters.items.MentionAutocompleteItem;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
@ -38,7 +43,9 @@ import com.otaliastudios.autocomplete.RecyclerViewPresenter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -54,6 +61,7 @@ import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention> implements FlexibleAdapter.OnItemClickListener {
|
||||
private static final String TAG = "MentionAutocompletePresenter";
|
||||
@Inject
|
||||
NcApi ncApi;
|
||||
@Inject
|
||||
@ -88,6 +96,14 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PopupDimensions getPopupDimensions() {
|
||||
PopupDimensions popupDimensions = new PopupDimensions();
|
||||
popupDimensions.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
popupDimensions.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
return popupDimensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onQuery(@Nullable CharSequence query) {
|
||||
|
||||
@ -101,10 +117,14 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
|
||||
int apiVersion = ApiUtils.getChatApiVersion(currentUser, new int[] {1});
|
||||
|
||||
adapter.setFilter(queryString);
|
||||
|
||||
Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("includeStatus", "true");
|
||||
|
||||
ncApi.getMentionAutocompleteSuggestions(
|
||||
ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
|
||||
ApiUtils.getUrlForMentionSuggestions(apiVersion, currentUser.getBaseUrl(), roomToken),
|
||||
queryString, 5)
|
||||
queryString, 5, queryMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(3)
|
||||
@ -125,9 +145,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
|
||||
for (Mention mention : mentionsList) {
|
||||
internalAbstractFlexibleItemList.add(
|
||||
new MentionAutocompleteItem(
|
||||
mention.getId(),
|
||||
mention.getLabel(),
|
||||
mention.getSource(),
|
||||
mention,
|
||||
currentUser,
|
||||
context));
|
||||
}
|
||||
@ -140,9 +158,11 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("LongLogTag")
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
adapter.clear();
|
||||
Log.e(TAG, "failed to get MentionAutocompleteSuggestions", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
133
app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java
Normal file
133
app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2020 Tobias Kaminsky
|
||||
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.nextcloud.talk.R;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
/**
|
||||
* A Drawable object that draws a status
|
||||
*/
|
||||
public class StatusDrawable extends Drawable {
|
||||
private String text;
|
||||
private @DrawableRes int icon = -1;
|
||||
private Paint textPaint;
|
||||
private int backgroundColor;
|
||||
private final float radius;
|
||||
private Context context;
|
||||
|
||||
public StatusDrawable(String status, String statusIcon, float statusSize, int backgroundColor, Context context) {
|
||||
radius = statusSize;
|
||||
this.backgroundColor = backgroundColor;
|
||||
|
||||
|
||||
if ("dnd".equals(status)) {
|
||||
icon = R.drawable.ic_user_status_dnd;
|
||||
this.context = context;
|
||||
} else if (TextUtils.isEmpty(statusIcon) && status != null) {
|
||||
switch (status) {
|
||||
case "online":
|
||||
icon = R.drawable.online_status;
|
||||
this.context = context;
|
||||
break;
|
||||
|
||||
case "away":
|
||||
icon = R.drawable.ic_user_status_away;
|
||||
this.context = context;
|
||||
break;
|
||||
|
||||
default:
|
||||
// do not show
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
text = statusIcon;
|
||||
|
||||
textPaint = new Paint();
|
||||
textPaint.setTextSize(statusSize);
|
||||
textPaint.setAntiAlias(true);
|
||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color
|
||||
* filter (set via setColorFilter) a circular background with a user's first character.
|
||||
*
|
||||
* @param canvas The canvas to draw into
|
||||
*/
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (text != null) {
|
||||
textPaint.setTextSize(1.6f * radius);
|
||||
canvas.drawText(text, radius, radius - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint);
|
||||
}
|
||||
|
||||
if (icon != -1) {
|
||||
|
||||
Paint backgroundPaint = new Paint();
|
||||
backgroundPaint.setStyle(Paint.Style.FILL);
|
||||
backgroundPaint.setAntiAlias(true);
|
||||
backgroundPaint.setColor(backgroundColor);
|
||||
|
||||
canvas.drawCircle(radius, radius, radius, backgroundPaint);
|
||||
|
||||
Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), icon, null);
|
||||
|
||||
if (drawable != null) {
|
||||
drawable.setBounds(0,
|
||||
0,
|
||||
(int) (2 * radius),
|
||||
(int) (2 * radius));
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
textPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
textPaint.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
}
|
@ -3,8 +3,10 @@
|
||||
*
|
||||
* @author Andy Scherzinger
|
||||
* @author Mario Danic
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
|
||||
*
|
||||
* 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
|
||||
@ -40,11 +42,16 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.activities.MainActivity;
|
||||
import com.nextcloud.talk.adapters.items.AdvancedUserItem;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.databinding.DialogChooseAccountBinding;
|
||||
import com.nextcloud.talk.models.database.CapabilitiesUtil;
|
||||
import com.nextcloud.talk.models.database.User;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.status.Status;
|
||||
import com.nextcloud.talk.models.json.status.StatusOverall;
|
||||
import com.nextcloud.talk.ui.StatusDrawable;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
@ -62,24 +69,33 @@ import autodagger.AutoInjector;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
private static final String TAG = ChooseAccountDialogFragment.class.getSimpleName();
|
||||
|
||||
private static final float STATUS_SIZE_IN_DP = 9f;
|
||||
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
|
||||
@Inject
|
||||
CookieManager cookieManager;
|
||||
|
||||
@Inject
|
||||
NcApi ncApi;
|
||||
|
||||
private DialogChooseAccountBinding binding;
|
||||
private View dialogView;
|
||||
|
||||
private FlexibleAdapter<AdvancedUserItem> adapter;
|
||||
private final List<AdvancedUserItem> userItems = new ArrayList<>();
|
||||
|
||||
private Status status;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@NonNull
|
||||
@Override
|
||||
@ -124,6 +140,8 @@ public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
} else {
|
||||
binding.currentAccount.userIcon.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
loadCurrentStatus(user);
|
||||
}
|
||||
|
||||
// Creating listeners for quick-actions
|
||||
@ -140,6 +158,17 @@ public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
});
|
||||
}
|
||||
|
||||
binding.setStatus.setOnClickListener(v -> {
|
||||
dismiss();
|
||||
|
||||
if (status != null) {
|
||||
SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(user, status);
|
||||
setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status");
|
||||
} else {
|
||||
Log.w(TAG, "status was null");
|
||||
}
|
||||
});
|
||||
|
||||
if (adapter == null) {
|
||||
adapter = new FlexibleAdapter<>(userItems, getActivity(), false);
|
||||
|
||||
@ -171,6 +200,41 @@ public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
prepareViews();
|
||||
}
|
||||
|
||||
private void loadCurrentStatus(User user) {
|
||||
String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken());
|
||||
|
||||
if (CapabilitiesUtil.isUserStatusAvailable(userUtils.getCurrentUser())) {
|
||||
binding.statusView.setVisibility(View.VISIBLE);
|
||||
|
||||
ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())).
|
||||
subscribeOn(Schedulers.io()).
|
||||
observeOn(AndroidSchedulers.mainThread()).
|
||||
subscribe(new Observer<StatusOverall>() {
|
||||
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull StatusOverall statusOverall) {
|
||||
status = statusOverall.ocs.data;
|
||||
|
||||
binding.setStatus.setEnabled(true);
|
||||
drawStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(TAG, "Can't receive user status from server. ", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareViews() {
|
||||
if (getActivity() != null) {
|
||||
LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity());
|
||||
@ -243,4 +307,25 @@ public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private void drawStatus() {
|
||||
float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, getContext());
|
||||
binding.currentAccount.ticker.setBackground(null);
|
||||
binding.currentAccount.ticker.setImageDrawable(new StatusDrawable(
|
||||
status.getStatus(),
|
||||
status.getIcon(),
|
||||
size,
|
||||
getContext().getResources().getColor(R.color.dialog_background),
|
||||
getContext()));
|
||||
binding.currentAccount.ticker.setVisibility(View.VISIBLE);
|
||||
|
||||
|
||||
if (status.getMessage() != null && !status.getMessage().isEmpty()) {
|
||||
binding.currentAccount.status.setText(status.getMessage());
|
||||
binding.currentAccount.status.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.currentAccount.status.setText("");
|
||||
binding.currentAccount.status.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2020 Nextcloud GmbH
|
||||
* Copyright (C) 2022 Marcel Hibbe (dev@mhibbe.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.ui.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemSelectedListener
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.adapters.PredefinedStatusClickListener
|
||||
import com.nextcloud.talk.adapters.PredefinedStatusListAdapter
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.databinding.DialogSetStatusBinding
|
||||
import com.nextcloud.talk.models.database.User
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.status.ClearAt
|
||||
import com.nextcloud.talk.models.json.status.Status
|
||||
import com.nextcloud.talk.models.json.status.StatusType
|
||||
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus
|
||||
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatusOverall
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val ARG_CURRENT_USER_PARAM = "currentUser"
|
||||
private const val ARG_CURRENT_STATUS_PARAM = "currentStatus"
|
||||
|
||||
private const val POS_DONT_CLEAR = 0
|
||||
private const val POS_HALF_AN_HOUR = 1
|
||||
private const val POS_AN_HOUR = 2
|
||||
private const val POS_FOUR_HOURS = 3
|
||||
private const val POS_TODAY = 4
|
||||
private const val POS_END_OF_WEEK = 5
|
||||
|
||||
private const val ONE_SECOND_IN_MILLIS = 1000
|
||||
private const val ONE_MINUTE_IN_SECONDS = 60
|
||||
private const val THIRTY_MINUTES = 30
|
||||
private const val FOUR_HOURS = 4
|
||||
private const val LAST_HOUR_OF_DAY = 23
|
||||
private const val LAST_MINUTE_OF_HOUR = 59
|
||||
private const val LAST_SECOND_OF_MINUTE = 59
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class SetStatusDialogFragment :
|
||||
DialogFragment(), PredefinedStatusClickListener {
|
||||
|
||||
private val logTag = SetStatusDialogFragment::class.java.simpleName
|
||||
|
||||
private lateinit var binding: DialogSetStatusBinding
|
||||
|
||||
private var currentUser: User? = null
|
||||
private var currentStatus: Status? = null
|
||||
|
||||
val predefinedStatusesList = ArrayList<PredefinedStatus>()
|
||||
|
||||
private lateinit var adapter: PredefinedStatusListAdapter
|
||||
private var clearAt: Long? = null
|
||||
private lateinit var popup: EmojiPopup
|
||||
|
||||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
lateinit var credentials: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
arguments?.let {
|
||||
currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM)
|
||||
currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM)
|
||||
|
||||
credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)
|
||||
ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<ResponseBody> {
|
||||
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
}
|
||||
|
||||
override fun onNext(responseBody: ResponseBody) {
|
||||
val predefinedStatusOverall: PredefinedStatusOverall = LoganSquare.parse(
|
||||
responseBody
|
||||
.string(),
|
||||
PredefinedStatusOverall::class.java
|
||||
)
|
||||
predefinedStatusOverall.ocs?.data?.let { it1 -> predefinedStatusesList.addAll(it1) }
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogSetStatusBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
return AlertDialog.Builder(requireContext())
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
currentStatus?.let {
|
||||
binding.emoji.setText(it.icon)
|
||||
binding.customStatusInput.text?.clear()
|
||||
binding.customStatusInput.setText(it.message)
|
||||
visualizeStatus(it.status)
|
||||
|
||||
if (it.clearAt > 0) {
|
||||
binding.clearStatusAfterSpinner.visibility = View.GONE
|
||||
binding.remainingClearTime.apply {
|
||||
binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message)
|
||||
visibility = View.VISIBLE
|
||||
text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true)
|
||||
.toString()
|
||||
.decapitalize(Locale.getDefault())
|
||||
setOnClickListener {
|
||||
visibility = View.GONE
|
||||
binding.clearStatusAfterSpinner.visibility = View.VISIBLE
|
||||
binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adapter = PredefinedStatusListAdapter(this, requireContext())
|
||||
adapter.list = predefinedStatusesList
|
||||
|
||||
binding.predefinedStatusList.adapter = adapter
|
||||
binding.predefinedStatusList.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) }
|
||||
binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) }
|
||||
binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) }
|
||||
binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) }
|
||||
|
||||
binding.clearStatus.setOnClickListener { clearStatus() }
|
||||
binding.setStatus.setOnClickListener { setStatusMessage() }
|
||||
binding.emoji.setOnClickListener { openEmojiPopup() }
|
||||
|
||||
popup = EmojiPopup.Builder
|
||||
.fromRootView(view)
|
||||
.setOnEmojiClickListener { _, _ ->
|
||||
popup.dismiss()
|
||||
binding.emoji.clearFocus()
|
||||
val imm: InputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as
|
||||
InputMethodManager
|
||||
imm.hideSoftInputFromWindow(binding.emoji.windowToken, 0)
|
||||
}
|
||||
.build(binding.emoji)
|
||||
binding.emoji.disableKeyboardInput(popup)
|
||||
binding.emoji.forceSingleEmoji()
|
||||
|
||||
val adapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_spinner_item)
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
adapter.add(getString(R.string.dontClear))
|
||||
adapter.add(getString(R.string.thirtyMinutes))
|
||||
adapter.add(getString(R.string.oneHour))
|
||||
adapter.add(getString(R.string.fourHours))
|
||||
adapter.add(getString(R.string.today))
|
||||
adapter.add(getString(R.string.thisWeek))
|
||||
|
||||
binding.clearStatusAfterSpinner.apply {
|
||||
this.adapter = adapter
|
||||
onItemSelectedListener = object : OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
||||
setClearStatusAfterValue(position)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.clearStatus.setTextColor(resources.getColor(R.color.colorPrimary))
|
||||
binding.setStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary))
|
||||
|
||||
binding.customStatusInput.highlightColor = resources.getColor(R.color.colorPrimary)
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun setClearStatusAfterValue(item: Int) {
|
||||
|
||||
val currentTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
|
||||
|
||||
when (item) {
|
||||
POS_DONT_CLEAR -> {
|
||||
// don't clear
|
||||
clearAt = null
|
||||
}
|
||||
|
||||
POS_HALF_AN_HOUR -> {
|
||||
// 30 minutes
|
||||
clearAt = currentTime + THIRTY_MINUTES * ONE_MINUTE_IN_SECONDS
|
||||
}
|
||||
|
||||
POS_AN_HOUR -> {
|
||||
// one hour
|
||||
clearAt = currentTime + ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS
|
||||
}
|
||||
|
||||
POS_FOUR_HOURS -> {
|
||||
// four hours
|
||||
clearAt = currentTime + FOUR_HOURS * ONE_MINUTE_IN_SECONDS * ONE_MINUTE_IN_SECONDS
|
||||
}
|
||||
|
||||
POS_TODAY -> {
|
||||
// today
|
||||
val date = Calendar.getInstance().apply {
|
||||
set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY)
|
||||
set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR)
|
||||
set(Calendar.SECOND, LAST_SECOND_OF_MINUTE)
|
||||
}
|
||||
clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS
|
||||
}
|
||||
|
||||
POS_END_OF_WEEK -> {
|
||||
// end of week
|
||||
val date = Calendar.getInstance().apply {
|
||||
set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY)
|
||||
set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR)
|
||||
set(Calendar.SECOND, LAST_SECOND_OF_MINUTE)
|
||||
}
|
||||
|
||||
while (date.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
|
||||
date.add(Calendar.DAY_OF_YEAR, 1)
|
||||
}
|
||||
|
||||
clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
private fun clearAtToUnixTime(clearAt: ClearAt?): Long {
|
||||
if (clearAt != null) {
|
||||
if (clearAt.type.equals("period")) {
|
||||
return System.currentTimeMillis() / ONE_SECOND_IN_MILLIS + clearAt.time.toLong()
|
||||
} else if (clearAt.type.equals("end-of")) {
|
||||
if (clearAt.time.equals("day")) {
|
||||
val date = Calendar.getInstance().apply {
|
||||
set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY)
|
||||
set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR)
|
||||
set(Calendar.SECOND, LAST_SECOND_OF_MINUTE)
|
||||
}
|
||||
return date.timeInMillis / ONE_SECOND_IN_MILLIS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun openEmojiPopup() {
|
||||
popup.show()
|
||||
}
|
||||
|
||||
private fun clearStatus() {
|
||||
val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)
|
||||
ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(statusOverall: GenericOverall) {}
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(logTag, "Failed to clear status", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
dismiss()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setStatus(statusType: StatusType) {
|
||||
visualizeStatus(statusType)
|
||||
|
||||
ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl), statusType.string)
|
||||
.subscribeOn(
|
||||
Schedulers
|
||||
.io()
|
||||
)
|
||||
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onNext(statusOverall: GenericOverall) {
|
||||
Log.d(logTag, "statusType successfully set")
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(logTag, "Failed to set statusType", e)
|
||||
clearTopStatus()
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun visualizeStatus(statusType: String) {
|
||||
StatusType.values().firstOrNull { it.name == statusType.uppercase(Locale.ROOT) }?.let { visualizeStatus(it) }
|
||||
}
|
||||
|
||||
private fun visualizeStatus(statusType: StatusType) {
|
||||
clearTopStatus()
|
||||
when (statusType) {
|
||||
StatusType.ONLINE -> {
|
||||
binding.onlineStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
|
||||
binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
|
||||
}
|
||||
StatusType.AWAY -> {
|
||||
binding.awayStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
|
||||
binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
|
||||
}
|
||||
StatusType.DND -> {
|
||||
binding.dndStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
|
||||
binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
|
||||
}
|
||||
StatusType.INVISIBLE -> {
|
||||
binding.invisibleStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
|
||||
binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
|
||||
}
|
||||
else -> Log.d(logTag, "unknown status")
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearTopStatus() {
|
||||
context?.let {
|
||||
val grey = it.resources.getColor(R.color.grey_200)
|
||||
binding.onlineStatus.setCardBackgroundColor(grey)
|
||||
binding.awayStatus.setCardBackgroundColor(grey)
|
||||
binding.dndStatus.setCardBackgroundColor(grey)
|
||||
binding.invisibleStatus.setCardBackgroundColor(grey)
|
||||
|
||||
binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
|
||||
binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
|
||||
binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
|
||||
binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setStatusMessage() {
|
||||
var inputText = binding.customStatusInput.text.toString()
|
||||
if (inputText.isEmpty()) {
|
||||
inputText = " "
|
||||
}
|
||||
|
||||
ncApi.setCustomStatusMessage(
|
||||
credentials,
|
||||
ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl),
|
||||
binding.emoji.text.toString(),
|
||||
inputText,
|
||||
clearAt
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<GenericOverall> {
|
||||
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
}
|
||||
|
||||
override fun onNext(t: GenericOverall) {
|
||||
Log.d(logTag, "CustomStatusMessage successfully set")
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(logTag, "failed to set CustomStatusMessage", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onClick(predefinedStatus: PredefinedStatus) {
|
||||
clearAt = clearAtToUnixTime(predefinedStatus.clearAt)
|
||||
binding.emoji.setText(predefinedStatus.icon)
|
||||
binding.customStatusInput.text?.clear()
|
||||
binding.customStatusInput.text?.append(predefinedStatus.message)
|
||||
|
||||
binding.remainingClearTime.visibility = View.GONE
|
||||
binding.clearStatusAfterSpinner.visibility = View.VISIBLE
|
||||
binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message_after)
|
||||
|
||||
if (predefinedStatus.clearAt == null) {
|
||||
binding.clearStatusAfterSpinner.setSelection(0)
|
||||
} else {
|
||||
val clearAt = predefinedStatus.clearAt!!
|
||||
if (clearAt.type.equals("period")) {
|
||||
when (clearAt.time) {
|
||||
"1800" -> binding.clearStatusAfterSpinner.setSelection(POS_HALF_AN_HOUR)
|
||||
"3600" -> binding.clearStatusAfterSpinner.setSelection(POS_AN_HOUR)
|
||||
"14400" -> binding.clearStatusAfterSpinner.setSelection(POS_FOUR_HOURS)
|
||||
else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR)
|
||||
}
|
||||
} else if (clearAt.type.equals("end-of")) {
|
||||
when (clearAt.time) {
|
||||
"day" -> binding.clearStatusAfterSpinner.setSelection(POS_TODAY)
|
||||
"week" -> binding.clearStatusAfterSpinner.setSelection(POS_END_OF_WEEK)
|
||||
else -> binding.clearStatusAfterSpinner.setSelection(POS_DONT_CLEAR)
|
||||
}
|
||||
}
|
||||
}
|
||||
setClearStatusAfterValue(binding.clearStatusAfterSpinner.selectedItemPosition)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment creator
|
||||
*/
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(user: User, status: Status): SetStatusDialogFragment {
|
||||
val args = Bundle()
|
||||
args.putParcelable(ARG_CURRENT_USER_PARAM, user)
|
||||
args.putParcelable(ARG_CURRENT_STATUS_PARAM, status)
|
||||
|
||||
val dialogFragment = SetStatusDialogFragment()
|
||||
dialogFragment.arguments = args
|
||||
return dialogFragment
|
||||
}
|
||||
}
|
||||
}
|
@ -416,4 +416,32 @@ public class ApiUtils {
|
||||
public static String getUrlForSetChatReadMarker(int version, String baseUrl, String roomToken) {
|
||||
return getUrlForChat(version, baseUrl, roomToken) + "/read";
|
||||
}
|
||||
|
||||
/*
|
||||
* OCS Status API
|
||||
*/
|
||||
|
||||
public static String getUrlForStatus(String baseUrl) {
|
||||
return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status";
|
||||
}
|
||||
|
||||
public static String getUrlForSetStatusType(String baseUrl) {
|
||||
return getUrlForStatus(baseUrl) + "/status";
|
||||
}
|
||||
|
||||
public static String getUrlForPredefinedStatuses(String baseUrl) {
|
||||
return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/predefined_statuses";
|
||||
}
|
||||
|
||||
public static String getUrlForStatusMessage(String baseUrl) {
|
||||
return getUrlForStatus(baseUrl) + "/message";
|
||||
}
|
||||
|
||||
public static String getUrlForSetCustomStatus(String baseUrl) {
|
||||
return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/custom";
|
||||
}
|
||||
|
||||
public static String getUrlForUserStatuses(String baseUrl) {
|
||||
return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/statuses";
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.ClickableSpan;
|
||||
@ -86,6 +87,8 @@ 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;
|
||||
@ -124,6 +127,8 @@ public class DisplayUtils {
|
||||
private static final String HTTP_PROTOCOL = "http://";
|
||||
private static final String HTTPS_PROTOCOL = "https://";
|
||||
|
||||
private static final int DATE_TIME_PARTS_SIZE = 2;
|
||||
|
||||
public static void setClickableString(String string, String url, TextView textView) {
|
||||
SpannableString spannableString = new SpannableString(string);
|
||||
spannableString.setSpan(new ClickableSpan() {
|
||||
@ -605,4 +610,66 @@ public class DisplayUtils {
|
||||
return R.string.menu_item_sort_by_name_a_z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates the relative time string based on the given modification timestamp.
|
||||
*
|
||||
* @param context the app's context
|
||||
* @param modificationTimestamp the UNIX timestamp of the file modification time in milliseconds.
|
||||
* @return a relative time string
|
||||
*/
|
||||
|
||||
public static CharSequence getRelativeTimestamp(Context context, long modificationTimestamp, boolean showFuture) {
|
||||
return getRelativeDateTimeString(context,
|
||||
modificationTimestamp,
|
||||
android.text.format.DateUtils.SECOND_IN_MILLIS,
|
||||
DateUtils.WEEK_IN_MILLIS,
|
||||
0,
|
||||
showFuture);
|
||||
}
|
||||
|
||||
public static CharSequence getRelativeDateTimeString(Context c,
|
||||
long time,
|
||||
long minResolution,
|
||||
long transitionResolution,
|
||||
int flags,
|
||||
boolean showFuture) {
|
||||
|
||||
CharSequence dateString = "";
|
||||
|
||||
// in Future
|
||||
if (!showFuture && time > System.currentTimeMillis()) {
|
||||
return DisplayUtils.unixTimeToHumanReadable(time);
|
||||
}
|
||||
// < 60 seconds -> seconds ago
|
||||
long diff = System.currentTimeMillis() - time;
|
||||
if (diff > 0 && diff < 60 * 1000 && minResolution == DateUtils.SECOND_IN_MILLIS) {
|
||||
return c.getString(R.string.secondsAgo);
|
||||
} else {
|
||||
dateString = DateUtils.getRelativeDateTimeString(c, time, minResolution, transitionResolution, flags);
|
||||
}
|
||||
|
||||
String[] parts = dateString.toString().split(",");
|
||||
if (parts.length == DATE_TIME_PARTS_SIZE) {
|
||||
if (parts[1].contains(":") && !parts[0].contains(":")) {
|
||||
return parts[0];
|
||||
} else if (parts[0].contains(":") && !parts[1].contains(":")) {
|
||||
return parts[1];
|
||||
}
|
||||
}
|
||||
// dateString contains unexpected format. fallback: use relative date time string from android api as is.
|
||||
return dateString.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Unix time to human readable format
|
||||
*
|
||||
* @param milliseconds that have passed since 01/01/1970
|
||||
* @return The human readable time for the users locale
|
||||
*/
|
||||
public static String unixTimeToHumanReadable(long milliseconds) {
|
||||
Date date = new Date(milliseconds);
|
||||
DateFormat df = DateFormat.getDateTimeInstance();
|
||||
return df.format(date);
|
||||
}
|
||||
}
|
||||
|
34
app/src/main/res/drawable/ic_edit.xml
Normal file
34
app/src/main/res/drawable/ic_edit.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
~
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Tobias Kaminsky
|
||||
~ Copyright (C) 2019 Tobias Kaminsky
|
||||
~ Copyright (C) 2019 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:tools="http://schemas.android.com/tools"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
|
||||
</vector>
|
32
app/src/main/res/drawable/ic_user_status_away.xml
Normal file
32
app/src/main/res/drawable/ic_user_status_away.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
@author Tobias Kaminsky
|
||||
Copyright (C) 2020 Tobias Kaminsky
|
||||
Copyright (C) 2020 Nextcloud GmbH
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector xmlns:tools="http://schemas.android.com/tools"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#f4a331"
|
||||
android:pathData="m10.615,2.1094c-4.8491,0.6811 -8.6152,4.8615 -8.6152,9.8906 0,5.5 4.5,10 10,10 5.0292,0 9.2096,-3.7661 9.8906,-8.6152 -1.4654,1.601 -3.5625,2.6152 -5.8906,2.6152 -4.4,0 -8,-3.6 -8,-8 0,-2.3281 1.0143,-4.4252 2.6152,-5.8906z" />
|
||||
</vector>
|
38
app/src/main/res/drawable/ic_user_status_dnd.xml
Normal file
38
app/src/main/res/drawable/ic_user_status_dnd.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
@author Tobias Kaminsky
|
||||
Copyright (C) 2020 Tobias Kaminsky
|
||||
Copyright (C) 2020 Nextcloud GmbH
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or 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 AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector xmlns:tools="http://schemas.android.com/tools"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#ed484c"
|
||||
android:pathData="m12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10 10,-4.48 10,-10 -4.48,-10 -10,-10z" />
|
||||
<path
|
||||
android:fillColor="#fdffff"
|
||||
android:pathData="m8,10h8c1.108,0 2,0.892 2,2s-0.892,2 -2,2h-8c-1.108,0 -2,-0.892 -2,-2s0.892,-2 2,-2z"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2" />
|
||||
</vector>
|
34
app/src/main/res/drawable/ic_user_status_invisible.xml
Normal file
34
app/src/main/res/drawable/ic_user_status_invisible.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
~
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Tobias Kaminsky
|
||||
~ Copyright (C) 2020 Tobias Kaminsky
|
||||
~ Copyright (C) 2020 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:tools="http://schemas.android.com/tools"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="m12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10 10,-4.48 10,-10 -4.48,-10 -10,-10zM12,6a6,6 0,0 1,6 6,6 6,0 0,1 -6,6 6,6 0,0 1,-6 -6,6 6,0 0,1 6,-6z" />
|
||||
</vector>
|
32
app/src/main/res/drawable/online_status.xml
Normal file
32
app/src/main/res/drawable/online_status.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Nextcloud Android client application
|
||||
|
||||
@author Andy Scherzinger
|
||||
Copyright (C) 2019 Andy Scherzinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector xmlns:tools="http://schemas.android.com/tools"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#49b382"
|
||||
android:pathData="m12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10 10,-4.48 10,-10 -4.48,-10 -10,-10z" />
|
||||
</vector>
|
||||
|
26
app/src/main/res/drawable/online_status_with_border.xml
Normal file
26
app/src/main/res/drawable/online_status_with_border.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Nextcloud Android client application
|
||||
|
||||
@author Andy Scherzinger
|
||||
Copyright (C) 2019 Andy Scherzinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="#00ff00" />
|
||||
|
||||
<stroke android:width="1.3dp"
|
||||
android:color="@color/bg_default"/>
|
||||
</shape>
|
@ -107,6 +107,7 @@
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="☁️ My custom status" />
|
||||
|
||||
<TextView
|
||||
|
@ -17,6 +17,7 @@
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
|
||||
android:orientation="vertical">
|
||||
@ -31,15 +32,52 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/statusView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/current_account"
|
||||
tools:visibility="visible">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@color/list_divider_background" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/set_status"
|
||||
style="@style/Nextcloud.Material.TextButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/set_status"
|
||||
android:textAlignment="textStart"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/fontAppbar"
|
||||
android:enabled="false"
|
||||
app:icon="@drawable/ic_edit"
|
||||
app:iconGravity="start"
|
||||
app:iconPadding="22dp"
|
||||
app:iconTint="@color/fontAppbar" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/separator_line"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@color/controller_chat_separator"
|
||||
android:background="@color/list_divider_background"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/current_account" />
|
||||
app:layout_constraintTop_toBottomOf="@id/statusView" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/accounts_list"
|
||||
@ -60,7 +98,7 @@
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:text="@string/nc_account_chooser_add_account"
|
||||
android:textAlignment="textStart"
|
||||
@ -82,7 +120,7 @@
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:text="@string/nc_settings"
|
||||
android:textAlignment="textStart"
|
||||
@ -90,7 +128,7 @@
|
||||
android:textColor="@color/fontAppbar"
|
||||
app:icon="@drawable/ic_settings"
|
||||
app:iconGravity="start"
|
||||
app:iconPadding="20dp"
|
||||
app:iconPadding="22dp"
|
||||
app:iconTint="@color/fontAppbar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
458
app/src/main/res/layout/dialog_set_status.xml
Normal file
458
app/src/main/res/layout/dialog_set_status.xml
Normal file
@ -0,0 +1,458 @@
|
||||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2020 Andy Scherzinger
|
||||
Copyright (C) 2020 Tobias Kaminsky
|
||||
Copyright (C) 2020 Nextcloud GmbH
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
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/>.
|
||||
-->
|
||||
<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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/onlineStatusView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:text="@string/online_status"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/activity_list_item_title_header_text_size"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/statusView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/onlineStatusView">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/standard_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/onlineStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/online_status_item_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
app:cardBackgroundColor="@color/grey_200"
|
||||
app:cardElevation="0dp"
|
||||
app:cardCornerRadius="@dimen/button_corner_radius">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/online_icon"
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/online_status"
|
||||
app:tint="@color/hwSecurityGreen" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_toEndOf="@id/online_icon"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/online_headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:text="@string/online"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/online_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_half_margin"
|
||||
android:layout_marginBottom="@dimen/standard_quarter_margin"
|
||||
android:ellipsize="end"
|
||||
android:gravity="top"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/awayStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/online_status_item_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_half_margin"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
app:cardBackgroundColor="@color/grey_200"
|
||||
app:cardElevation="0dp"
|
||||
app:cardCornerRadius="@dimen/button_corner_radius">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/away_icon"
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_user_status_away"
|
||||
app:tint="#f4a331" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_toEndOf="@id/away_icon"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/away_headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:text="@string/away"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/away_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_half_margin"
|
||||
android:layout_marginBottom="@dimen/standard_quarter_margin"
|
||||
android:ellipsize="end"
|
||||
android:gravity="top"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/standard_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/dndStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/online_status_item_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
app:cardBackgroundColor="@color/grey_200"
|
||||
app:cardElevation="0dp"
|
||||
app:cardCornerRadius="@dimen/button_corner_radius">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dnd_icon"
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_user_status_dnd" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_toEndOf="@id/dnd_icon"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dnd_headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:text="@string/dnd"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/invisibleStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/online_status_item_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_half_margin"
|
||||
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
app:cardBackgroundColor="@color/grey_200"
|
||||
app:cardElevation="0dp"
|
||||
app:cardCornerRadius="@dimen/button_corner_radius">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/invisible_icon"
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_user_status_invisible" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_toEndOf="@id/invisible_icon"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/invisible_headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:text="@string/invisible"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<View
|
||||
android:id="@+id/separator_line"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:background="@color/list_divider_background"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/statusView" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:text="@string/status_message"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/activity_list_item_title_header_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusView" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/emojiCard"
|
||||
android:layout_width="@dimen/activity_row_layout_height"
|
||||
android:layout_height="@dimen/activity_row_layout_height"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/standard_eighth_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:orientation="horizontal"
|
||||
app:cardBackgroundColor="@color/grey_200"
|
||||
app:cardCornerRadius="24dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<com.vanniktech.emoji.EmojiEditText
|
||||
android:id="@+id/emoji"
|
||||
android:layout_width="@dimen/activity_row_layout_height"
|
||||
android:layout_height="@dimen/activity_row_layout_height"
|
||||
android:background="@color/grey_200"
|
||||
android:cursorVisible="false"
|
||||
android:gravity="center"
|
||||
android:text="@string/default_emoji"
|
||||
android:textSize="24sp" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/customStatusInput_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/whats_your_status">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/customStatusInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textAutoCorrect"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
</com.google.android.material.textfield.TextInputEditText>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/predefinedStatusList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/predefined_status" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/clearStatusMessageTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/clear_status_message_after"
|
||||
android:textColor="@color/high_emphasis_text" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/clearStatusAfterSpinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remainingClearTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:layout_marginStart="4dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/clearStatus"
|
||||
style="@style/OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/clear_status_message"
|
||||
app:cornerRadius="@dimen/button_corner_radius" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/setStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/set_status_message"
|
||||
android:theme="@style/Button.Primary"
|
||||
app:cornerRadius="@dimen/button_corner_radius" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
62
app/src/main/res/layout/predefined_status.xml
Normal file
62
app/src/main/res/layout/predefined_status.xml
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2020 Andy Scherzinger
|
||||
Copyright (C) 2020 Tobias Kaminsky
|
||||
Copyright (C) 2020 Nextcloud GmbH
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:textSize="25sp"
|
||||
tools:text="📆" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
tools:text="In a meeting" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/standard_half_margin"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/divider"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/clearAt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
tools:text="an hour" />
|
||||
</LinearLayout>
|
@ -48,7 +48,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toStartOf="@id/checkedImageView"
|
||||
android:layout_toEndOf="@id/simple_drawee_view"
|
||||
android:layout_toEndOf="@id/avatar_drawee_view"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textAlignment="viewStart"
|
||||
@ -56,7 +56,7 @@
|
||||
tools:text="Jane Doe" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/simple_drawee_view"
|
||||
android:id="@+id/avatar_drawee_view"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_centerVertical="true"
|
||||
|
@ -18,67 +18,105 @@
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:orientation="vertical">
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:layout_marginTop="@dimen/standard_margin">
|
||||
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/simple_drawee_view"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:id="@+id/avatar_drawee_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" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/videoCallIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginEnd="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_videocam_grey_600_24dp"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
<com.vanniktech.emoji.EmojiEditText
|
||||
android:id="@+id/participant_status_emoji"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginStart="@dimen/margin_between_elements"
|
||||
android:layout_marginEnd="@dimen/margin_between_elements"
|
||||
android:layout_toEndOf="@id/simple_drawee_view"
|
||||
android:layout_toStartOf="@id/videoCallIcon"
|
||||
android:orientation="vertical">
|
||||
android:background="@color/transparent"
|
||||
android:cursorVisible="false"
|
||||
android:gravity="center|start"
|
||||
android:text="@string/default_emoji"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="@+id/name_text"
|
||||
app:layout_constraintTop_toBottomOf="@+id/name_text" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/user_status_image"
|
||||
android:layout_width="18dp"
|
||||
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"
|
||||
tools:src="@drawable/emoji_one_category_smileysandpeople"/>
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/name_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="2dp"
|
||||
android:singleLine="true"
|
||||
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"
|
||||
tools:text="Jane Doe" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/secondary_text"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/conversation_info_status_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginEnd="@dimen/side_margin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/participant_status_emoji"
|
||||
app:layout_constraintTop_toBottomOf="@+id/name_text"
|
||||
tools:text="this is a very long status message. server allows only 81 chars here. 0123456789" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/secondary_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
tools:text="@string/nc_moderator" />
|
||||
android:layout_marginEnd="@dimen/side_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/name_text"
|
||||
app:layout_constraintTop_toTopOf="@+id/name_text"
|
||||
tools:text="Moderator (or userid for autocomplete mention)" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
<ImageView
|
||||
android:id="@+id/videoCallIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_videocam_grey_600_24dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/secondary_text"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -43,8 +43,7 @@
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:contentDescription="@null"
|
||||
app:roundAsCircle="true"
|
||||
tools:src="@drawable/ic_call_black_24dp" />
|
||||
app:roundAsCircle="true" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/favoriteConversationImageView"
|
||||
@ -56,6 +55,13 @@
|
||||
app:tint="@color/favorite_icon_tint"
|
||||
app:tintMode="src_in" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/user_status_image"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:contentDescription="@string/nc_account_chooser_active_user"
|
||||
tools:src="@drawable/emoji_one_category_smileysandpeople"/>
|
||||
</FrameLayout>
|
||||
|
||||
<RelativeLayout
|
||||
|
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ Copyright (C) 2017-2018 Mario Danic
|
||||
~ Copyright (C) 2017 Andy Scherzinger
|
||||
~
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="@dimen/item_height"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/frame_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/simple_drawee_view"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
app:roundAsCircle="true" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginStart="@dimen/margin_between_elements"
|
||||
android:layout_marginEnd="@dimen/margin_between_elements"
|
||||
android:layout_toEndOf="@id/frame_layout"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/name_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/ListItem"
|
||||
tools:text="Call item text" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/secondary_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/textColorMaxContrast"
|
||||
tools:text="A week ago" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -38,6 +38,7 @@
|
||||
<color name="high_emphasis_text">#deffffff</color>
|
||||
<color name="medium_emphasis_text">#99ffffff</color>
|
||||
<color name="low_emphasis_text">#61ffffff</color>
|
||||
<color name="high_emphasis_text_inverse">#de000000</color>
|
||||
|
||||
<color name="bg_default">#121212</color>
|
||||
<color name="bg_default_semitransparent">#99121212</color>
|
||||
@ -65,4 +66,9 @@
|
||||
<!-- shimmer element colors -->
|
||||
<color name="nc_shimmer_default_color">#4B4B4B</color>
|
||||
<color name="nc_shimmer_darker_color">#282828</color>
|
||||
|
||||
<color name="list_divider_background">#222222</color>
|
||||
<color name="grey_200">#818181</color>
|
||||
|
||||
<color name="dialog_background">#353535</color>
|
||||
</resources>
|
||||
|
@ -39,6 +39,7 @@
|
||||
<color name="high_emphasis_text">#de000000</color>
|
||||
<color name="medium_emphasis_text">#99000000</color>
|
||||
<color name="low_emphasis_text">#61000000</color>
|
||||
<color name="high_emphasis_text_inverse">#deffffff</color>
|
||||
|
||||
<!-- general text colors for dark background -->
|
||||
<color name="high_emphasis_text_dark_background">#deffffff</color>
|
||||
@ -97,4 +98,11 @@
|
||||
|
||||
<color name="camera_bg_tint">#99121212</color>
|
||||
|
||||
<color name="list_divider_background">#eeeeee</color>
|
||||
<color name="grey_200">#EEEEEE</color>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
</resources>
|
||||
|
@ -63,4 +63,10 @@
|
||||
<dimen name="call_grid_item_min_height">180dp</dimen>
|
||||
<dimen name="call_controls_height">110dp</dimen>
|
||||
<dimen name="zero">0dp</dimen>
|
||||
|
||||
<dimen name="online_status_item_height">52dp</dimen>
|
||||
<dimen name="standard_quarter_margin">4dp</dimen>
|
||||
<dimen name="activity_list_item_title_header_text_size">16sp</dimen>
|
||||
<dimen name="activity_row_layout_height">48dp</dimen>
|
||||
<dimen name="standard_eighth_margin">2dp</dimen>
|
||||
</resources>
|
||||
|
@ -268,6 +268,28 @@
|
||||
<string name="nc_remove_group_and_members">Remove group and members</string>
|
||||
<string name="nc_attendee_pin">Pin: %1$s</string>
|
||||
|
||||
<!-- User Status -->
|
||||
<string name="set_status">Set status</string>
|
||||
<string name="online_status">Online status</string>
|
||||
<string name="status_message">Status message</string>
|
||||
<string name="whats_your_status">What is your status?</string>
|
||||
<string name="clear_status_message_after">Clear status message after</string>
|
||||
<string name="clear_status_message">Clear status message</string>
|
||||
<string name="set_status_message">Set status message</string>
|
||||
<string name="online">Online</string>
|
||||
<string name="dnd">Do not disturb</string>
|
||||
<string name="away">Away</string>
|
||||
<string name="invisible">Invisible</string>
|
||||
<string translatable="false" name="divider">—</string>
|
||||
<string translatable="false" name="default_emoji">😃</string>
|
||||
<string name="dontClear">Don\'t clear</string>
|
||||
<string name="today">Today</string>
|
||||
<string name="thirtyMinutes">30 minutes</string>
|
||||
<string name="oneHour">1 hour</string>
|
||||
<string name="fourHours">4 hours</string>
|
||||
<string name="thisWeek">This week</string>
|
||||
<string name="secondsAgo">seconds ago</string>
|
||||
|
||||
<!-- Conversations List-->
|
||||
<string name="nc_new_mention">Unread mentions</string>
|
||||
<string name="conversations">Conversations</string>
|
||||
@ -435,8 +457,8 @@
|
||||
<string name="nc_phone_book_integration_account_not_found">Account not found</string>
|
||||
|
||||
<string name="starred">Favorite</string>
|
||||
<string name="user_status">Status</string>
|
||||
<string name="encrypted">Encrypted</string>
|
||||
<string name="password_protected">Password protected</string>
|
||||
|
||||
<string name="avatar">Avatar</string>
|
||||
<string name="account_icon">Account icon</string>
|
||||
|
@ -240,4 +240,12 @@
|
||||
<item name="android:windowSoftInputMode">adjustResize</item>
|
||||
</style>
|
||||
|
||||
<style name="OutlinedButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||
<item name="colorAccent">@color/transparent</item>
|
||||
<item name="android:textColor">@color/colorPrimaryDark</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:typeface">sans</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -1 +1 @@
|
||||
497
|
||||
492
|
@ -1,2 +1,2 @@
|
||||
DO NOT TOUCH; GENERATED BY DRONE
|
||||
<span class="mdl-layout-title">Lint Report: 1 error and 208 warnings</span>
|
||||
<span class="mdl-layout-title">Lint Report: 1 error and 205 warnings</span>
|
||||
|
Loading…
Reference in New Issue
Block a user