diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java index f57ef9b98..c10babf09 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -39,8 +39,6 @@ import android.view.inputmethod.EditorInfo; import android.widget.ProgressBar; import android.widget.RelativeLayout; -import com.afollestad.materialdialogs.LayoutMode; -import com.afollestad.materialdialogs.MaterialDialog; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler; import com.bluelinelabs.logansquare.LoganSquare; @@ -57,7 +55,6 @@ import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController; import com.nextcloud.talk.controllers.bottomsheet.OperationsMenuController; import com.nextcloud.talk.events.BottomSheetLockEvent; import com.nextcloud.talk.jobs.AddParticipantsToConversation; -import com.nextcloud.talk.jobs.DeleteConversationWorker; import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall; diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 02d5daf5e..5857593d7 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -54,6 +54,9 @@ import com.nextcloud.talk.adapters.items.UserItem 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.items.BasicListItemWithImage +import com.nextcloud.talk.controllers.bottomsheet.items.ListItemWithImage +import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.events.BottomSheetLockEvent import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.jobs.DeleteConversationWorker @@ -593,9 +596,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA if (participant.userId != conversationUser!!.userId) { val items = mutableListOf( - context.getString(R.string.nc_promote), - context.getString(R.string.nc_demote), - context.getString(R.string.nc_remove_participant) + BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)), + BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)), + BasicListItemWithImage(R.drawable.ic_delete_grey600_24dp, context.getString(R.string.nc_remove_participant)) ) if (participant.type == Participant.ParticipantType.MODERATOR) { @@ -610,8 +613,9 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { cornerRadius(res = R.dimen.corner_radius) + title(text = participant.displayName) - listItems(items = items) { dialog, index, text -> + listItemsWithImage(items = items) { dialog, index, _ -> if (index == 0) { if (participant.type == Participant.ParticipantType.MODERATOR) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt new file mode 100644 index 000000000..8d75b08ba --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.controllers.bottomsheet.items + +import android.widget.ImageView +import androidx.annotation.DrawableRes + +interface ListItemWithImage { + val title: String + fun populateIcon(imageView: ImageView) +} + +data class BasicListItemWithImage( + @DrawableRes val iconRes: Int, + override val title: String) : ListItemWithImage { + + override fun populateIcon(imageView: ImageView) { + imageView.setImageResource(iconRes) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt new file mode 100644 index 000000000..43c66fd1a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt @@ -0,0 +1,151 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.controllers.bottomsheet.items + +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.WhichButton +import com.afollestad.materialdialogs.actions.hasActionButton +import com.afollestad.materialdialogs.actions.hasActionButtons +import com.afollestad.materialdialogs.internal.list.DialogAdapter +import com.afollestad.materialdialogs.internal.rtl.RtlTextView +import com.afollestad.materialdialogs.list.getItemSelector +import com.afollestad.materialdialogs.utils.MDUtil.inflate +import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor +import com.google.android.material.textview.MaterialTextView +import com.nextcloud.talk.R + +private const val KEY_ACTIVATED_INDEX = "activated_index" + +internal class ListItemViewHolder( + itemView: View, + private val adapter: ListIconDialogAdapter<*>) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + init { + itemView.setOnClickListener(this) + } + + val iconView: ImageView = itemView.findViewById(R.id.icon) + val titleView: RtlTextView = itemView.findViewById(R.id.title) + + override fun onClick(view: View) = adapter.itemClicked(adapterPosition) +} + +internal class ListIconDialogAdapter( + private var dialog: MaterialDialog, + private var items: List, + disabledItems: IntArray?, + private var waitForPositiveButton: Boolean, + private var selection: ListItemListener) : RecyclerView.Adapter(), DialogAdapter> { + + private var disabledIndices: IntArray = disabledItems ?: IntArray(0) + + fun itemClicked(index: Int) { + if (waitForPositiveButton && dialog.hasActionButton(WhichButton.POSITIVE)) { + // Wait for positive action button, mark clicked item as activated so that we can call the + // selection listener when the button is pressed. + val lastActivated = dialog.config[KEY_ACTIVATED_INDEX] as? Int + dialog.config[KEY_ACTIVATED_INDEX] = index + if (lastActivated != null) { + notifyItemChanged(lastActivated) + } + notifyItemChanged(index) + } else { + // Don't wait for action buttons, call listener and dismiss if auto dismiss is applicable + this.selection?.invoke(dialog, index, this.items[index]) + if (dialog.autoDismissEnabled && !dialog.hasActionButtons()) { + dialog.dismiss() + } + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int): ListItemViewHolder { + val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet) + val viewHolder = ListItemViewHolder( + itemView = listItemView, + adapter = this + ) + viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content) + return viewHolder + } + + override fun getItemCount() = items.size + + override fun onBindViewHolder( + holder: ListItemViewHolder, + position: Int) { + holder.itemView.isEnabled = !disabledIndices.contains(position) + val currentItem = items[position] + + holder.titleView.text = currentItem.title + holder.itemView.background = dialog.getItemSelector() + currentItem.populateIcon(holder.iconView) + + val activatedIndex = dialog.config[KEY_ACTIVATED_INDEX] as? Int + holder.itemView.isActivated = activatedIndex != null && activatedIndex == position + + if (dialog.bodyFont != null) { + holder.titleView.typeface = dialog.bodyFont + } + } + + override fun positiveButtonClicked() { + val activatedIndex = dialog.config[KEY_ACTIVATED_INDEX] as? Int + if (activatedIndex != null) { + selection?.invoke(dialog, activatedIndex, items[activatedIndex]) + dialog.config.remove(KEY_ACTIVATED_INDEX) + } + } + + override fun replaceItems( + items: List, + listener: ListItemListener) { + this.items = items + if (listener != null) { + this.selection = listener + } + this.notifyDataSetChanged() + } + + override fun disableItems(indices: IntArray) { + this.disabledIndices = indices + notifyDataSetChanged() + } + + override fun checkItems(indices: IntArray) = Unit + + override fun uncheckItems(indices: IntArray) = Unit + + override fun toggleItems(indices: IntArray) = Unit + + override fun checkAllItems() = Unit + + override fun uncheckAllItems() = Unit + + override fun toggleAllChecked() = Unit + + override fun isItemChecked(index: Int) = false +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt new file mode 100644 index 000000000..ab2be552a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt @@ -0,0 +1,75 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.controllers.bottomsheet.items + +import androidx.annotation.CheckResult +import androidx.recyclerview.widget.LinearLayoutManager +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.internal.list.DialogAdapter +import com.afollestad.materialdialogs.list.customListAdapter +import com.afollestad.materialdialogs.list.getListAdapter + +typealias ListItemListener = + ((dialog: MaterialDialog, index: Int, item: IT) -> Unit)? + +@CheckResult fun MaterialDialog.listItemsWithImage( + items: List, + disabledIndices: IntArray? = null, + waitForPositiveButton: Boolean = true, + selection: ListItemListener = null): MaterialDialog { + + if (getListAdapter() != null) { + return updateListItemsWithImage( + items = items, + disabledIndices = disabledIndices + ) + } + + val layoutManager = LinearLayoutManager(windowContext) + return customListAdapter( + adapter = ListIconDialogAdapter( + dialog = this, + items = items, + disabledItems = disabledIndices, + waitForPositiveButton = waitForPositiveButton, + selection = selection + ), + layoutManager = layoutManager + ) +} + +fun MaterialDialog.updateListItemsWithImage( + items: List, + disabledIndices: IntArray? = null): MaterialDialog { + val adapter = getListAdapter() + check(adapter != null) { + "updateGridItems(...) can't be used before you've created a bottom sheet grid dialog." + } + if (adapter is DialogAdapter<*, *>) { + @Suppress("UNCHECKED_CAST") + (adapter as DialogAdapter).replaceItems(items) + + if (disabledIndices != null) { + adapter.disableItems(disabledIndices) + } + } + return this +} diff --git a/app/src/main/res/layout/menu_item_sheet.xml b/app/src/main/res/layout/menu_item_sheet.xml new file mode 100644 index 000000000..caa3612d5 --- /dev/null +++ b/app/src/main/res/layout/menu_item_sheet.xml @@ -0,0 +1,44 @@ + + + + + + + + \ No newline at end of file