Skip to content

Commit 0e78de0

Browse files
committed
add SetOnlineStatusBottomSheet
Signed-off-by: alperozturk <[email protected]>
1 parent 956d4f5 commit 0e78de0

22 files changed

+771
-309
lines changed

app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,8 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
7676
AvatarLoader.INSTANCE.load(requireContext(), binding.currentAccountItemAvatar, currentLocalAccount);
7777
binding.accountLayout.setOnClickListener((v) -> dismiss());
7878
binding.onlineStatus.setOnClickListener(v -> {
79-
/*
80-
val setStatusDialog = SetOnlineStatusBottomSheet(currentStatus)
81-
setStatusDialog.show((activity as DrawerActivity).supportFragmentManager, "fragment_set_status")
82-
*/
83-
79+
final var setOnlineStatusBottomSheet = new SetOnlineStatusBottomSheet();
80+
setOnlineStatusBottomSheet.show(requireActivity().getSupportFragmentManager(), "fragment_set_status");
8481
dismiss();
8582
});
8683

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package it.niedermann.owncloud.notes.accountswitcher
2+
3+
4+
import android.annotation.SuppressLint
5+
import android.os.Bundle
6+
import android.util.Log
7+
import android.view.LayoutInflater
8+
import android.view.View
9+
import android.view.ViewGroup
10+
import android.widget.ImageView
11+
import android.widget.TextView
12+
import androidx.core.content.ContextCompat
13+
import androidx.lifecycle.lifecycleScope
14+
import com.google.android.material.card.MaterialCardView
15+
import com.nextcloud.android.common.ui.theme.utils.ColorRole
16+
import com.nextcloud.android.sso.helper.SingleAccountHelper
17+
import com.owncloud.android.lib.resources.users.Status
18+
import com.owncloud.android.lib.resources.users.StatusType
19+
import it.niedermann.owncloud.notes.R
20+
import it.niedermann.owncloud.notes.accountswitcher.repository.UserStatusRepository
21+
import it.niedermann.owncloud.notes.branding.BrandedBottomSheetDialogFragment
22+
import it.niedermann.owncloud.notes.branding.BrandingUtil
23+
import it.niedermann.owncloud.notes.databinding.SetOnlineStatusBottomSheetBinding
24+
import it.niedermann.owncloud.notes.shared.util.DisplayUtils
25+
import it.niedermann.owncloud.notes.shared.util.FilesSpecificViewThemeUtils
26+
import kotlinx.coroutines.Dispatchers
27+
import kotlinx.coroutines.launch
28+
import kotlinx.coroutines.withContext
29+
30+
class SetOnlineStatusBottomSheet :
31+
BrandedBottomSheetDialogFragment(R.layout.set_online_status_bottom_sheet) {
32+
33+
companion object {
34+
private val TAG = SetOnlineStatusBottomSheet::class.simpleName
35+
}
36+
37+
private lateinit var binding: SetOnlineStatusBottomSheetBinding
38+
private var cardViews: Triple<MaterialCardView, TextView, ImageView>? = null
39+
private var repository: UserStatusRepository? = null
40+
41+
@SuppressLint("DefaultLocale")
42+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
43+
super.onViewCreated(view, savedInstanceState)
44+
45+
initRepository()
46+
setupStatusClickListeners()
47+
}
48+
49+
private fun setupStatusClickListeners() {
50+
val statusMap = mapOf(
51+
binding.onlineStatus to StatusType.ONLINE,
52+
binding.awayStatus to StatusType.AWAY,
53+
binding.busyStatus to StatusType.BUSY,
54+
binding.dndStatus to StatusType.DND,
55+
binding.invisibleStatus to StatusType.INVISIBLE
56+
)
57+
58+
statusMap.forEach { (view, statusType) ->
59+
view.setOnClickListener { setStatus(statusType) }
60+
}
61+
}
62+
63+
private fun initRepository() {
64+
lifecycleScope.launch(Dispatchers.IO) {
65+
val ssoAccount =
66+
SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()) ?: return@launch
67+
repository = UserStatusRepository(requireContext(), ssoAccount)
68+
val currentStatus =
69+
repository?.fetchUserStatus() ?: Status(StatusType.OFFLINE, "", "", -1)
70+
71+
val capabilities = repository?.getCapabilities()
72+
73+
if (capabilities?.isUserStatusSupportsBusy == true) {
74+
binding.busyStatus.visibility = View.VISIBLE
75+
} else {
76+
binding.busyStatus.visibility = View.GONE
77+
}
78+
79+
withContext(Dispatchers.Main) {
80+
updateCurrentStatusViews(currentStatus)
81+
}
82+
}
83+
}
84+
85+
private fun updateCurrentStatusViews(it: Status) {
86+
visualizeStatus(it.status)
87+
}
88+
89+
private fun setStatus(statusType: StatusType) {
90+
lifecycleScope.launch(Dispatchers.IO) {
91+
val result = repository?.setStatusType(statusType)
92+
withContext(Dispatchers.Main) {
93+
if (result == true) {
94+
dismiss()
95+
} else {
96+
showErrorSnackbar()
97+
}
98+
}
99+
}
100+
}
101+
102+
private fun showErrorSnackbar() {
103+
DisplayUtils.showSnackMessage(view, R.string.status_set_fail_message)
104+
clearTopStatus()
105+
}
106+
107+
private fun visualizeStatus(statusType: StatusType) {
108+
clearTopStatus()
109+
cardViews = when (statusType) {
110+
StatusType.ONLINE -> Triple(
111+
binding.onlineStatus,
112+
binding.onlineHeadline,
113+
binding.onlineIcon
114+
)
115+
116+
StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon)
117+
StatusType.BUSY -> Triple(binding.busyStatus, binding.busyHeadline, binding.busyIcon)
118+
StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon)
119+
StatusType.INVISIBLE -> Triple(
120+
binding.invisibleStatus,
121+
binding.invisibleHeadline,
122+
binding.invisibleIcon
123+
)
124+
125+
else -> {
126+
Log.d(TAG, "unknown status")
127+
return
128+
}
129+
}
130+
cardViews?.first?.isChecked = true
131+
}
132+
133+
private fun clearTopStatus() {
134+
context?.let { ctx ->
135+
binding.run {
136+
val headlines = listOf(
137+
onlineHeadline,
138+
awayHeadline,
139+
busyHeadline,
140+
dndHeadline,
141+
invisibleHeadline
142+
)
143+
val color = ContextCompat.getColor(ctx, com.nextcloud.android.common.ui.R.color.high_emphasis_text)
144+
headlines.forEach { it.setTextColor(color) }
145+
listOf(awayIcon, dndIcon, invisibleIcon).forEach { it.imageTintList = null }
146+
listOf(onlineStatus, awayStatus, busyStatus, dndStatus, invisibleStatus).forEach { it.isChecked = false }
147+
}
148+
}
149+
}
150+
151+
override fun onCreateView(
152+
inflater: LayoutInflater,
153+
container: ViewGroup?,
154+
savedInstanceState: Bundle?
155+
): View {
156+
binding = SetOnlineStatusBottomSheetBinding.inflate(layoutInflater, container, false)
157+
return binding.root
158+
}
159+
160+
override fun applyBrand(color: Int) {
161+
BrandingUtil.of(color, requireContext()).run {
162+
platform.themeDialog(binding.root)
163+
164+
cardViews?.let {
165+
platform.colorTextView(it.second, ColorRole.ON_SECONDARY_CONTAINER)
166+
}
167+
}
168+
169+
FilesSpecificViewThemeUtils.run {
170+
themeStatusCardView(binding.onlineStatus, color)
171+
themeStatusCardView(binding.awayStatus, color)
172+
themeStatusCardView(binding.busyStatus, color)
173+
themeStatusCardView(binding.dndStatus, color)
174+
themeStatusCardView(binding.invisibleStatus, color)
175+
}
176+
}
177+
}

app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/repository/UserStatusRepository.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import com.owncloud.android.lib.resources.users.PredefinedStatus
77
import com.owncloud.android.lib.resources.users.Status
88
import com.owncloud.android.lib.resources.users.StatusType
99
import it.niedermann.owncloud.notes.persistence.ApiProvider
10+
import it.niedermann.owncloud.notes.persistence.NotesRepository
11+
import it.niedermann.owncloud.notes.shared.model.Capabilities
1012
import kotlinx.coroutines.Dispatchers
1113
import kotlinx.coroutines.withContext
1214

@@ -18,8 +20,11 @@ class UserStatusRepository(
1820
private const val TAG = "UserStatusRepository"
1921
}
2022

23+
private val notesRepository: NotesRepository by lazy { NotesRepository.getInstance(context) }
2124
private val api by lazy { ApiProvider.getInstance().getUserStatusAPI(context, ssoAccount) }
2225

26+
fun getCapabilities(): Capabilities = notesRepository.capabilities
27+
2328
suspend fun fetchPredefinedStatuses(): ArrayList<PredefinedStatus> = withContext(Dispatchers.IO) {
2429
try {
2530
val response = api.fetchPredefinedStatuses().execute()
@@ -118,4 +123,23 @@ class UserStatusRepository(
118123
offlineStatus
119124
}
120125
}
126+
127+
suspend fun setStatusType(statusType: StatusType): Boolean = withContext(Dispatchers.IO) {
128+
try {
129+
val body = mutableMapOf(
130+
"statusType" to statusType.string,
131+
)
132+
val response = api.setStatusType(body).execute()
133+
if (response.isSuccessful) {
134+
Log_OC.d(TAG, "✅ status type successfully set")
135+
true
136+
} else {
137+
Log_OC.e(TAG, "❌ setting status type failed")
138+
false
139+
}
140+
} catch (t: Throwable) {
141+
Log_OC.e(TAG, "❌ setting status type failed", t)
142+
false
143+
}
144+
}
121145
}

app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@
5858
NotesListWidgetData.class,
5959
ShareEntity.class,
6060
Capabilities.class
61-
}, version = 26,
62-
autoMigrations = { @AutoMigration(from = 25, to = 26) }
61+
}, version = 27,
62+
autoMigrations = {
63+
@AutoMigration(from = 25, to = 26),
64+
@AutoMigration(from = 26, to = 27),
65+
}
6366
)
6467
@TypeConverters({Converters.class})
6568
public abstract class NotesDatabase extends RoomDatabase {
@@ -77,9 +80,9 @@ public static NotesDatabase getInstance(@NonNull Context context) {
7780

7881
private static NotesDatabase create(final Context context) {
7982
return Room.databaseBuilder(
80-
context,
81-
NotesDatabase.class,
82-
NOTES_DB_NAME)
83+
context,
84+
NotesDatabase.class,
85+
NOTES_DB_NAME)
8386
.addMigrations(
8487
new Migration_9_10(), // v2.0.0
8588
new Migration_10_11(context),

app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
public class CapabilitiesDeserializer implements JsonDeserializer<Capabilities> {
3333

3434
private static final String CAPABILITIES = "capabilities";
35+
private static final String CAPABILITIES_USER_STATUS = "user_status";
3536
private static final String CAPABILITIES_NOTES = "notes";
3637
private static final String CAPABILITIES_NOTES_API_VERSION = "api_version";
3738
private static final String CAPABILITIES_THEMING = "theming";
@@ -42,6 +43,7 @@ public class CapabilitiesDeserializer implements JsonDeserializer<Capabilities>
4243
private static final String CAPABILITIES_FILES_DIRECT_EDITING_SUPPORTS_FILE_ID = "supportsFileId";
4344
private static final String CAPABILITIES_FILES_SHARING = "files_sharing";
4445
private static final String VERSION = "version";
46+
private static final String CAPABILITIES_SUPPORTS_BUSY = "supports_busy";
4547

4648
@Override
4749
public Capabilities deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
@@ -110,6 +112,16 @@ public Capabilities deserialize(JsonElement json, Type typeOfT, JsonDeserializat
110112
}
111113
}
112114
response.setDirectEditingAvailable(hasDirectEditingCapability(capabilities));
115+
116+
if (capabilities.has(CAPABILITIES_USER_STATUS)) {
117+
final var userStatus = capabilities.getAsJsonObject(CAPABILITIES_USER_STATUS);
118+
if (userStatus.has(CAPABILITIES_SUPPORTS_BUSY)) {
119+
final var userStatusSupportsBusy = userStatus.getAsJsonPrimitive(CAPABILITIES_SUPPORTS_BUSY);
120+
if (userStatusSupportsBusy != null) {
121+
response.setUserStatusSupportsBusy(userStatusSupportsBusy.getAsBoolean());
122+
}
123+
}
124+
}
113125
}
114126
return response;
115127
}

app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/UserStatusAPI.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ interface UserStatusAPI {
2727
@PUT("user_status/message/custom?format=json")
2828
@Headers("Content-Type: application/json")
2929
fun setUserDefinedStatusMessage(@Body body: Map<String, String>): Call<OcsResponse<Void>>
30+
31+
@PUT("user_status/status?format=json")
32+
@Headers("Content-Type: application/json")
33+
fun setStatusType(@Body body: Map<String, String>): Call<OcsResponse<Void>>
3034
}

app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class Capabilities implements Serializable {
4545
private boolean askForOptionalPassword;
4646
private boolean isReSharingAllowed;
4747
private int defaultPermission = OCShare.NO_PERMISSION;
48+
private Boolean userStatusSupportsBusy = null;
4849

4950
public boolean isReSharingAllowed() {
5051
return isReSharingAllowed;
@@ -146,7 +147,6 @@ public void setTextColor(@ColorInt int textColor) {
146147
this.textColor = textColor;
147148
}
148149

149-
150150
public boolean isDirectEditingAvailable() {
151151
return directEditingAvailable;
152152
}
@@ -155,6 +155,14 @@ public void setDirectEditingAvailable(boolean directEditingAvailable) {
155155
this.directEditingAvailable = directEditingAvailable;
156156
}
157157

158+
public boolean isUserStatusSupportsBusy() {
159+
return userStatusSupportsBusy;
160+
}
161+
162+
public void setUserStatusSupportsBusy(boolean value) {
163+
userStatusSupportsBusy = value;
164+
}
165+
158166
@Override
159167
public String toString() {
160168
return "Capabilities{" +
@@ -169,6 +177,7 @@ public String toString() {
169177
", textColor=" + textColor +
170178
", eTag='" + eTag + '\'' +
171179
", hasDirectEditing=" + directEditingAvailable +
180+
", userStatusSupportsBusy=" + userStatusSupportsBusy +
172181
'}';
173182
}
174183
}

0 commit comments

Comments
 (0)