Skip to content

Commit 6a6f7b0

Browse files
Merge pull request #5342 from nextcloud/feature/5311/busyStatus
Add busy status + split set status dialog
2 parents 75c5879 + 00200e0 commit 6a6f7b0

14 files changed

Lines changed: 642 additions & 342 deletions

app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ class MentionAutocompleteItem(
221221
if (statusMessage.isNullOrEmpty()) {
222222
holder.binding.conversationInfoStatusMessage.setText(R.string.dnd)
223223
}
224+
} else if (status != null && status == StatusType.BUSY.string) {
225+
if (statusMessage.isNullOrEmpty()) {
226+
holder.binding.conversationInfoStatusMessage.setText(R.string.busy)
227+
}
224228
} else if (status != null && status == StatusType.AWAY.string) {
225229
if (statusMessage.isNullOrEmpty()) {
226230
holder.binding.conversationInfoStatusMessage.setText(R.string.away)

app/src/main/java/com/nextcloud/talk/adapters/items/ParticipantItem.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ class ParticipantItem(
276276
if (model.statusMessage == null || model.statusMessage!!.isEmpty()) {
277277
holder.binding.conversationInfoStatusMessage.setText(R.string.dnd)
278278
}
279+
} else if (model.status != null && model.status == StatusType.BUSY.string) {
280+
if (model.statusMessage == null || model.statusMessage!!.isEmpty()) {
281+
holder.binding.conversationInfoStatusMessage.setText(R.string.busy)
282+
}
279283
} else if (model.status != null && model.status == StatusType.AWAY.string) {
280284
if (model.statusMessage == null || model.statusMessage!!.isEmpty()) {
281285
holder.binding.conversationInfoStatusMessage.setText(R.string.away)

app/src/main/java/com/nextcloud/talk/models/json/status/StatusType.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ enum class StatusType(val string: String) {
1111
OFFLINE("offline"),
1212
DND("dnd"),
1313
AWAY("away"),
14+
BUSY("busy"),
1415
INVISIBLE("invisible")
1516
}

app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,16 @@ public StatusDrawable(String status, String statusIcon, float statusSize, int ba
5454
icon = StatusDrawableType.ONLINE;
5555
this.context = context;
5656
}
57+
58+
case "busy" -> {
59+
icon = StatusDrawableType.BUSY;
60+
this.context = context;
61+
}
5762
case "away" -> {
5863
icon = StatusDrawableType.AWAY;
5964
this.context = context;
6065
}
66+
6167
default -> {
6268
}
6369
// do not show
@@ -124,6 +130,7 @@ public int getOpacity() {
124130
private enum StatusDrawableType {
125131
DND(R.drawable.ic_user_status_dnd),
126132
ONLINE(R.drawable.online_status),
133+
BUSY(R.drawable.ic_user_status_busy),
127134
AWAY(R.drawable.ic_user_status_away),
128135
UNDEFINED(-1);
129136

app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,25 @@ private void setupListeners() {
245245
});
246246

247247

248-
binding.setStatus.setOnClickListener(v -> {
248+
binding.onlineStatus.setOnClickListener(v -> {
249249
dismiss();
250+
if(status!= null && getActivity()!= null){
251+
OnlineStatusBottomDialogFragment bottomDialog =
252+
OnlineStatusBottomDialogFragment.newInstance(status);
253+
bottomDialog.show(requireActivity().getSupportFragmentManager(),
254+
"fragment_online_status_bottom_dialog");
255+
256+
}
257+
});
258+
binding.statusMessage.setOnClickListener(v -> {
259+
260+
dismiss();
261+
if(status!= null && getActivity()!= null){
262+
StatusMessageBottomDialogFragment bottomDialog =
263+
StatusMessageBottomDialogFragment.newInstance(status);
264+
bottomDialog.show(getActivity().getSupportFragmentManager(),
265+
"fragment_status_message_bottom_dialog");
250266

251-
if (status != null && getActivity() != null) {
252-
SetStatusDialogFragment setStatusDialog = SetStatusDialogFragment.newInstance(status);
253-
setStatusDialog.show(getActivity().getSupportFragmentManager(), "fragment_set_status");
254-
} else {
255-
Log.w(TAG, "status was null");
256267
}
257268
});
258269
}
@@ -261,8 +272,10 @@ private void themeViews() {
261272
viewThemeUtils.platform.themeDialog(binding.getRoot());
262273
viewThemeUtils.platform.themeDialogDivider(binding.divider);
263274

264-
viewThemeUtils.material.colorMaterialTextButton(binding.setStatus);
265-
viewThemeUtils.dialog.colorDialogMenuText(binding.setStatus);
275+
viewThemeUtils.material.colorMaterialTextButton(binding.onlineStatus);
276+
viewThemeUtils.dialog.colorDialogMenuText(binding.onlineStatus);
277+
viewThemeUtils.material.colorMaterialTextButton(binding.statusMessage);
278+
viewThemeUtils.dialog.colorDialogMenuText(binding.statusMessage);
266279
viewThemeUtils.material.colorMaterialTextButton(binding.addAccount);
267280
viewThemeUtils.dialog.colorDialogMenuText(binding.addAccount);
268281
viewThemeUtils.material.colorMaterialTextButton(binding.manageSettings);
@@ -292,7 +305,8 @@ public void onNext(@NonNull StatusOverall statusOverall) {
292305
}
293306

294307
try {
295-
binding.setStatus.setEnabled(true);
308+
binding.onlineStatus.setEnabled(true);
309+
binding.statusMessage.setEnabled(true);
296310
drawStatus();
297311
} catch (NullPointerException npe) {
298312
Log.i(TAG, "UI already teared down", npe);
@@ -391,7 +405,6 @@ private void drawStatus() {
391405
binding.currentAccount.ticker.setImageDrawable(drawable);
392406
binding.currentAccount.ticker.setVisibility(View.VISIBLE);
393407

394-
395408
if (status.getMessage() != null && !status.getMessage().isEmpty()) {
396409
binding.currentAccount.status.setText(status.getMessage());
397410
binding.currentAccount.status.setVisibility(View.VISIBLE);
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* Nextcloud Talk - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2025 Sowjanya Kota <sowjanya.kota@gmail.com>
5+
* SPDX-License-Identifier: GPL-3.0-or-later
6+
*/
7+
package com.nextcloud.talk.ui.dialog
8+
9+
import android.content.res.Configuration
10+
import android.os.Bundle
11+
import android.util.Log
12+
import android.view.LayoutInflater
13+
import android.view.View
14+
import android.view.ViewGroup
15+
import android.widget.ImageView
16+
import android.widget.TextView
17+
import androidx.core.content.ContextCompat
18+
import androidx.core.view.WindowInsetsControllerCompat
19+
import autodagger.AutoInjector
20+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
21+
import com.google.android.material.card.MaterialCardView
22+
import com.nextcloud.android.common.ui.theme.utils.ColorRole
23+
import com.nextcloud.talk.R
24+
import com.nextcloud.talk.api.NcApi
25+
import com.nextcloud.talk.application.NextcloudTalkApplication
26+
import com.nextcloud.talk.data.user.model.User
27+
import com.nextcloud.talk.databinding.DialogSetOnlineStatusBinding
28+
import com.nextcloud.talk.models.json.generic.GenericOverall
29+
import com.nextcloud.talk.models.json.status.Status
30+
import com.nextcloud.talk.models.json.status.StatusType
31+
import com.nextcloud.talk.ui.theme.ViewThemeUtils
32+
import com.nextcloud.talk.utils.ApiUtils
33+
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
34+
import io.reactivex.Observer
35+
import io.reactivex.android.schedulers.AndroidSchedulers
36+
import io.reactivex.disposables.Disposable
37+
import io.reactivex.schedulers.Schedulers
38+
import java.util.Locale
39+
import javax.inject.Inject
40+
41+
@AutoInjector(NextcloudTalkApplication::class)
42+
class OnlineStatusBottomDialogFragment : BottomSheetDialogFragment() {
43+
private lateinit var binding: DialogSetOnlineStatusBinding
44+
private var currentUser: User? = null
45+
private var currentStatus: Status? = null
46+
private val disposables: MutableList<Disposable> = ArrayList()
47+
48+
@Inject lateinit var ncApi: NcApi
49+
50+
@Inject lateinit var viewThemeUtils: ViewThemeUtils
51+
52+
var currentUserProvider: CurrentUserProviderNew? = null
53+
@Inject set
54+
55+
lateinit var credentials: String
56+
57+
override fun onCreate(savedInstanceState: Bundle?) {
58+
super.onCreate(savedInstanceState)
59+
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
60+
61+
arguments?.let {
62+
currentUser = currentUserProvider?.currentUser?.blockingGet()
63+
currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM)
64+
credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)!!
65+
}
66+
}
67+
68+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
69+
binding = DialogSetOnlineStatusBinding.inflate(inflater, container, false)
70+
viewThemeUtils.platform.themeDialog(binding.root)
71+
viewThemeUtils.material.themeDragHandleView(binding.dragHandle)
72+
73+
dialog?.window?.let { window ->
74+
window.navigationBarColor = ContextCompat.getColor(requireContext(), R.color.bg_default)
75+
val inLightMode = resources.configuration.uiMode and
76+
Configuration.UI_MODE_NIGHT_MASK != Configuration.UI_MODE_NIGHT_YES
77+
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars = inLightMode
78+
}
79+
return binding.root
80+
}
81+
82+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
83+
super.onViewCreated(view, savedInstanceState)
84+
setupGeneralStatusOptions()
85+
currentStatus?.let { visualizeStatus(it.status) }
86+
}
87+
88+
private fun setupGeneralStatusOptions() {
89+
binding.onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) }
90+
binding.dndStatus.setOnClickListener { setStatus(StatusType.DND) }
91+
binding.busyStatus.setOnClickListener { setStatus(StatusType.BUSY) }
92+
binding.awayStatus.setOnClickListener { setStatus(StatusType.AWAY) }
93+
binding.invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) }
94+
95+
viewThemeUtils.talk.themeStatusCardView(binding.onlineStatus)
96+
viewThemeUtils.talk.themeStatusCardView(binding.dndStatus)
97+
viewThemeUtils.talk.themeStatusCardView(binding.busyStatus)
98+
viewThemeUtils.talk.themeStatusCardView(binding.awayStatus)
99+
viewThemeUtils.talk.themeStatusCardView(binding.invisibleStatus)
100+
}
101+
102+
private fun setStatus(statusType: StatusType) {
103+
visualizeStatus(statusType)
104+
105+
ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl!!), statusType.string)
106+
.subscribeOn(Schedulers.io())
107+
.observeOn(AndroidSchedulers.mainThread())
108+
.subscribe(object : Observer<GenericOverall> {
109+
override fun onSubscribe(d: Disposable) {
110+
disposables.add(d)
111+
}
112+
override fun onNext(t: GenericOverall) {
113+
dismiss()
114+
}
115+
override fun onError(e: Throwable) {
116+
Log.e(TAG, "Failed to set statusType", e)
117+
}
118+
override fun onComplete() {
119+
// unused atm
120+
}
121+
})
122+
}
123+
124+
private fun visualizeStatus(statusType: String) {
125+
StatusType.entries.firstOrNull { it.name == statusType.uppercase(Locale.ROOT) }?.let { visualizeStatus(it) }
126+
}
127+
128+
private fun visualizeStatus(statusType: StatusType) {
129+
clearTopStatus()
130+
val views: Triple<MaterialCardView, TextView, ImageView> = when (statusType) {
131+
StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon)
132+
StatusType.BUSY -> Triple(binding.busyStatus, binding.busyHeadline, binding.busyIcon)
133+
StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon)
134+
StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon)
135+
StatusType.INVISIBLE -> Triple(binding.invisibleStatus, binding.invisibleHeadline, binding.invisibleIcon)
136+
else -> return
137+
}
138+
views.first.isChecked = true
139+
viewThemeUtils.platform.colorTextView(views.second, ColorRole.ON_SECONDARY_CONTAINER)
140+
}
141+
142+
private fun clearTopStatus() {
143+
context?.let {
144+
binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
145+
binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
146+
binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
147+
binding.busyHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
148+
binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
149+
150+
binding.onlineIcon.imageTintList = null
151+
binding.awayIcon.imageTintList = null
152+
binding.dndIcon.imageTintList = null
153+
binding.busyIcon.imageTintList = null
154+
binding.invisibleIcon.imageTintList = null
155+
156+
binding.onlineStatus.isChecked = false
157+
binding.awayStatus.isChecked = false
158+
binding.dndStatus.isChecked = false
159+
binding.busyStatus.isChecked = false
160+
binding.invisibleStatus.isChecked = false
161+
}
162+
}
163+
164+
override fun onDestroyView() {
165+
super.onDestroyView()
166+
disposables.forEach { if (!it.isDisposed) it.dispose() }
167+
disposables.clear()
168+
}
169+
170+
companion object {
171+
private const val ARG_CURRENT_STATUS_PARAM = "currentStatus"
172+
private val TAG = OnlineStatusBottomDialogFragment::class.simpleName
173+
174+
@JvmStatic
175+
fun newInstance(status: Status): OnlineStatusBottomDialogFragment {
176+
val args = Bundle()
177+
args.putParcelable(ARG_CURRENT_STATUS_PARAM, status)
178+
179+
val fragment = OnlineStatusBottomDialogFragment()
180+
fragment.arguments = args
181+
return fragment
182+
}
183+
}
184+
}

0 commit comments

Comments
 (0)