mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Started meetings list
This commit is contained in:
parent
4a11554fa5
commit
3873028209
35 changed files with 1458 additions and 17 deletions
|
|
@ -145,6 +145,16 @@ class MainActivity : AppCompatActivity() {
|
|||
// TODO FIXME: uncomment
|
||||
// startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
|
||||
coreContext.greenToastToShowEvent.observe(this) {
|
||||
it.consume { pair ->
|
||||
val message = pair.first
|
||||
val icon = pair.second
|
||||
showGreenToast(message, icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
coreContext.postOnCoreThread {
|
||||
val startDestination = when (corePreferences.defaultFragment) {
|
||||
CONTACTS_FRAGMENT_ID -> {
|
||||
|
|
@ -163,10 +173,10 @@ class MainActivity : AppCompatActivity() {
|
|||
)
|
||||
R.id.conversationsFragment
|
||||
}
|
||||
/*MEETINGS_FRAGMENT_ID -> {
|
||||
MEETINGS_FRAGMENT_ID -> {
|
||||
Log.i("$TAG Latest visited page is meetings, setting it as start destination")
|
||||
R.id.meetingsFragment
|
||||
}*/
|
||||
}
|
||||
else -> { // Default
|
||||
Log.i("$TAG No latest visited page stored, using default one (call history)")
|
||||
R.id.historyFragment
|
||||
|
|
@ -179,13 +189,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
coreContext.greenToastToShowEvent.observe(this) {
|
||||
it.consume { pair ->
|
||||
val message = pair.first
|
||||
val icon = pair.second
|
||||
showGreenToast(message, icon)
|
||||
}
|
||||
}
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
|
@ -199,9 +203,9 @@ class MainActivity : AppCompatActivity() {
|
|||
R.id.conversationsFragment -> {
|
||||
CHAT_FRAGMENT_ID
|
||||
}
|
||||
/*R.id.meetingsFragment -> {
|
||||
R.id.meetingsFragment -> {
|
||||
MEETINGS_FRAGMENT_ID
|
||||
}*/
|
||||
}
|
||||
else -> { // Default
|
||||
HISTORY_FRAGMENT_ID
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ class ConversationFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// This fragment is displayed in a SlidingPane "child" area
|
||||
isSlidingPaneChild = true
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,18 @@ class ConversationsFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.navigateToMeetingsEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
if (findNavController().currentDestination?.id == R.id.conversationsFragment) {
|
||||
// To prevent any previously seen conversation to show up when navigating back to here later
|
||||
binding.chatNavContainer.findNavController().popBackStack()
|
||||
|
||||
val action = ConversationsFragmentDirections.actionConversationsFragmentToMeetingsFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import androidx.annotation.WorkerThread
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.contacts.ContactsManager
|
||||
import org.linphone.core.CallLog
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.ChatRoom.Capabilities
|
||||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreListenerStub
|
||||
|
|
@ -46,8 +46,22 @@ class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewMod
|
|||
private var currentFilter = ""
|
||||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
override fun onCallLogUpdated(core: Core, callLog: CallLog) {
|
||||
computeChatRoomsList(currentFilter)
|
||||
@WorkerThread
|
||||
override fun onChatRoomStateChanged(
|
||||
core: Core,
|
||||
chatRoom: ChatRoom,
|
||||
state: ChatRoom.State?
|
||||
) {
|
||||
Log.i(
|
||||
"$TAG Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]"
|
||||
)
|
||||
|
||||
when (state) {
|
||||
ChatRoom.State.Created, ChatRoom.State.Instantiated, ChatRoom.State.Deleted -> {
|
||||
computeChatRoomsList(currentFilter)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ class ContactFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// This fragment is displayed in a SlidingPane "child" area
|
||||
isSlidingPaneChild = true
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,18 @@ class ContactsFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.navigateToMeetingsEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
if (findNavController().currentDestination?.id == R.id.contactsFragment) {
|
||||
// To prevent any previously seen contact to show up when navigating back to here later
|
||||
binding.contactsNavContainer.findNavController().popBackStack()
|
||||
|
||||
val action = ContactsFragmentDirections.actionContactsFragmentToMeetingsFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ class EditContactFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// This fragment is displayed in a SlidingPane "child" area
|
||||
isSlidingPaneChild = true
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
|
||||
|
|
|
|||
|
|
@ -89,13 +89,16 @@ class BottomNavBarFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.setOnMeetingsClicked {
|
||||
// TODO: meeting feature
|
||||
if (sharedViewModel.currentlyDisplayedFragment.value != R.id.meetingsFragment) {
|
||||
sharedViewModel.navigateToMeetingsEvent.value = Event(true)
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.currentlyDisplayedFragment.observe(viewLifecycleOwner) {
|
||||
viewModel.contactsSelected.value = it == R.id.contactsFragment
|
||||
viewModel.callsSelected.value = it == R.id.historyFragment
|
||||
viewModel.conversationsSelected.value = it == R.id.conversationsFragment
|
||||
viewModel.meetingsSelected.value = it == R.id.meetingsFragment
|
||||
}
|
||||
|
||||
sharedViewModel.resetMissedCallsCountEvent.observe(viewLifecycleOwner) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import androidx.annotation.UiThread
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.linphone.R
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.viewmodel.SharedMainViewModel
|
||||
|
||||
|
|
@ -38,6 +37,8 @@ abstract class GenericFragment : Fragment() {
|
|||
|
||||
protected lateinit var sharedViewModel: SharedMainViewModel
|
||||
|
||||
protected var isSlidingPaneChild: Boolean = false
|
||||
|
||||
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
|
||||
override fun handleOnBackPressed() {
|
||||
Log.d("$TAG ${getFragmentRealClassName()} handleOnBackPressed")
|
||||
|
|
@ -114,7 +115,7 @@ abstract class GenericFragment : Fragment() {
|
|||
// This allow to navigate a SlidingPane child nav graph.
|
||||
// This only concerns fragments for which the nav graph is inside a SlidingPane layout.
|
||||
// In our case it's all graphs except the main one.
|
||||
if (findNavController().graph.id == R.id.main_nav_graph) return false
|
||||
if (!isSlidingPaneChild) return false
|
||||
|
||||
val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false
|
||||
Log.d(
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ class HistoryContactFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// This fragment is displayed in a SlidingPane "child" area
|
||||
isSlidingPaneChild = true
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,18 @@ class HistoryFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.navigateToMeetingsEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
if (findNavController().currentDestination?.id == R.id.historyFragment) {
|
||||
// To prevent any previously seen call log to show up when navigating back to here later
|
||||
binding.historyNavContainer.findNavController().popBackStack()
|
||||
|
||||
val action = HistoryFragmentDirections.actionHistoryFragmentToMeetingsFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
package org.linphone.ui.main.meetings.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.linphone.R
|
||||
import org.linphone.databinding.MeetingListCellBinding
|
||||
import org.linphone.databinding.MeetingsListDecorationBinding
|
||||
import org.linphone.ui.main.meetings.model.MeetingModel
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.HeaderAdapter
|
||||
|
||||
class MeetingsListAdapter(
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : ListAdapter<MeetingModel, RecyclerView.ViewHolder>(MeetingDiffCallback()), HeaderAdapter {
|
||||
var selectedAdapterPosition = -1
|
||||
|
||||
val meetingClickedEvent: MutableLiveData<Event<MeetingModel>> by lazy {
|
||||
MutableLiveData<Event<MeetingModel>>()
|
||||
}
|
||||
|
||||
val meetingLongClickedEvent: MutableLiveData<Event<MeetingModel>> by lazy {
|
||||
MutableLiveData<Event<MeetingModel>>()
|
||||
}
|
||||
|
||||
override fun displayHeaderForPosition(position: Int): Boolean {
|
||||
if (position == 0) return true
|
||||
|
||||
val previous = getItem(position - 1)
|
||||
val item = getItem(position)
|
||||
return previous.month != item.month
|
||||
}
|
||||
|
||||
override fun getHeaderViewForPosition(context: Context, position: Int): View {
|
||||
val binding = MeetingsListDecorationBinding.inflate(LayoutInflater.from(context))
|
||||
val item = getItem(position)
|
||||
binding.header.text = item.month
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val binding: MeetingListCellBinding = DataBindingUtil.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
R.layout.meeting_list_cell,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
(holder as ViewHolder).bind(getItem(position))
|
||||
}
|
||||
|
||||
fun resetSelection() {
|
||||
notifyItemChanged(selectedAdapterPosition)
|
||||
selectedAdapterPosition = -1
|
||||
}
|
||||
|
||||
inner class ViewHolder(
|
||||
val binding: MeetingListCellBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
@UiThread
|
||||
fun bind(meetingModel: MeetingModel) {
|
||||
with(binding) {
|
||||
model = meetingModel
|
||||
|
||||
val hasPrevious = bindingAdapterPosition > 0
|
||||
firstMeetingOfTheDay = if (hasPrevious) {
|
||||
val previous = getItem(bindingAdapterPosition - 1)
|
||||
previous.day != meetingModel.day || previous.dayNumber != meetingModel.dayNumber
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
binding.root.isSelected = bindingAdapterPosition == selectedAdapterPosition
|
||||
|
||||
binding.setOnClickListener {
|
||||
meetingClickedEvent.value = Event(meetingModel)
|
||||
}
|
||||
|
||||
binding.setOnLongClickListener {
|
||||
selectedAdapterPosition = bindingAdapterPosition
|
||||
binding.root.isSelected = true
|
||||
meetingLongClickedEvent.value = Event(meetingModel)
|
||||
true
|
||||
}
|
||||
|
||||
executePendingBindings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MeetingDiffCallback : DiffUtil.ItemCallback<MeetingModel>() {
|
||||
override fun areItemsTheSame(oldItem: MeetingModel, newItem: MeetingModel): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: MeetingModel, newItem: MeetingModel): Boolean {
|
||||
return oldItem.subject.value == newItem.subject.value && oldItem.time == newItem.time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 org.linphone.ui.main.meetings.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.linphone.databinding.MeetingFragmentBinding
|
||||
import org.linphone.ui.main.fragment.GenericFragment
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class MeetingFragment : GenericFragment() {
|
||||
companion object {
|
||||
private const val TAG = "[Meeting Fragment]"
|
||||
}
|
||||
|
||||
private lateinit var binding: MeetingFragmentBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = MeetingFragmentBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun goBack(): Boolean {
|
||||
sharedViewModel.closeSlidingPaneEvent.value = Event(true)
|
||||
// If not done, when going back to ConversationsFragment this fragment will be created again
|
||||
return findNavController().popBackStack()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// This fragment is displayed in a SlidingPane "child" area
|
||||
isSlidingPaneChild = true
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 org.linphone.ui.main.meetings.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.slidingpanelayout.widget.SlidingPaneLayout
|
||||
import org.linphone.R
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.MeetingsFragmentBinding
|
||||
import org.linphone.ui.main.fragment.GenericFragment
|
||||
import org.linphone.utils.SlidingPaneBackPressedCallback
|
||||
|
||||
class MeetingsFragment : GenericFragment() {
|
||||
companion object {
|
||||
private const val TAG = "[Meetings Fragment]"
|
||||
}
|
||||
|
||||
private lateinit var binding: MeetingsFragmentBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = MeetingsFragmentBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
|
||||
if (findNavController().currentDestination?.id == R.id.scheduleMeetingFragment) {
|
||||
// Holds fragment in place while new contact fragment slides over it
|
||||
return AnimationUtils.loadAnimation(activity, R.anim.hold)
|
||||
}
|
||||
return super.onCreateAnimation(transit, enter, nextAnim)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
binding.root.doOnPreDraw {
|
||||
val slidingPane = binding.slidingPaneLayout
|
||||
slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
|
||||
|
||||
sharedViewModel.isSlidingPaneSlideable.value = slidingPane.isSlideable
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback(
|
||||
viewLifecycleOwner,
|
||||
SlidingPaneBackPressedCallback(slidingPane)
|
||||
)
|
||||
}
|
||||
|
||||
sharedViewModel.closeSlidingPaneEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
it.consume {
|
||||
Log.i("$TAG Closing sliding pane")
|
||||
binding.slidingPaneLayout.closePane()
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.openSlidingPaneEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
it.consume {
|
||||
Log.i("$TAG Opening sliding pane")
|
||||
binding.slidingPaneLayout.openPane()
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.showScheduleMeetingEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
Log.i("$TAG Navigating to schedule meeting fragment")
|
||||
findNavController().navigate(R.id.action_global_scheduleMeetingFragment)
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.navigateToContactsEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
if (findNavController().currentDestination?.id == R.id.meetingsFragment) {
|
||||
// To prevent any previously seen meeting to show up when navigating back to here later
|
||||
binding.meetingsNavContainer.findNavController().popBackStack()
|
||||
|
||||
val action = MeetingsFragmentDirections.actionMeetingsFragmentToContactsFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.navigateToCallsEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
if (findNavController().currentDestination?.id == R.id.meetingsFragment) {
|
||||
// To prevent any previously seen meeting to show up when navigating back to here later
|
||||
binding.meetingsNavContainer.findNavController().popBackStack()
|
||||
|
||||
val action = MeetingsFragmentDirections.actionMeetingsFragmentToHistoryFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.navigateToConversationsEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
if (findNavController().currentDestination?.id == R.id.meetingsFragment) {
|
||||
// To prevent any previously seen meeting to show up when navigating back to here later
|
||||
binding.meetingsNavContainer.findNavController().popBackStack()
|
||||
|
||||
val action = MeetingsFragmentDirections.actionMeetingsFragmentToConversationsFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
sharedViewModel.currentlyDisplayedFragment.value = R.id.meetingsFragment
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 org.linphone.ui.main.meetings.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import org.linphone.R
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.MeetingsListFragmentBinding
|
||||
import org.linphone.ui.main.fragment.AbstractTopBarFragment
|
||||
import org.linphone.ui.main.meetings.adapter.MeetingsListAdapter
|
||||
import org.linphone.ui.main.meetings.viewmodel.MeetingsListViewModel
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.RecyclerViewHeaderDecoration
|
||||
import org.linphone.utils.hideKeyboard
|
||||
import org.linphone.utils.showKeyboard
|
||||
|
||||
class MeetingsListFragment : AbstractTopBarFragment() {
|
||||
companion object {
|
||||
private const val TAG = "[Meetings List Fragment]"
|
||||
}
|
||||
|
||||
private lateinit var binding: MeetingsListFragmentBinding
|
||||
|
||||
private lateinit var listViewModel: MeetingsListViewModel
|
||||
|
||||
private lateinit var adapter: MeetingsListAdapter
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = MeetingsListFragmentBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
listViewModel = requireActivity().run {
|
||||
ViewModelProvider(this)[MeetingsListViewModel::class.java]
|
||||
}
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
binding.viewModel = listViewModel
|
||||
|
||||
adapter = MeetingsListAdapter(viewLifecycleOwner)
|
||||
binding.meetingsList.setHasFixedSize(true)
|
||||
binding.meetingsList.adapter = adapter
|
||||
|
||||
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter, true)
|
||||
binding.meetingsList.addItemDecoration(headerItemDecoration)
|
||||
|
||||
val layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.meetingsList.layoutManager = layoutManager
|
||||
|
||||
binding.setNewMeetingClicked {
|
||||
sharedViewModel.showScheduleMeetingEvent.value = Event(true)
|
||||
}
|
||||
|
||||
binding.setTodayClickListener {
|
||||
val todayMeeting = listViewModel.meetings.value.orEmpty().find {
|
||||
it.isToday
|
||||
}
|
||||
val position = if (todayMeeting != null) {
|
||||
listViewModel.meetings.value.orEmpty().indexOf(todayMeeting)
|
||||
} else {
|
||||
0 // TODO FIXME: improve by getting closest meeting
|
||||
}
|
||||
binding.meetingsList.smoothScrollToPosition(position)
|
||||
}
|
||||
|
||||
listViewModel.meetings.observe(viewLifecycleOwner) {
|
||||
val currentCount = adapter.itemCount
|
||||
adapter.submitList(it)
|
||||
Log.i("$TAG Meetings list ready with [${it.size}] items")
|
||||
|
||||
if (currentCount < it.size) {
|
||||
binding.meetingsList.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.defaultAccountChangedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
Log.i(
|
||||
"$TAG Default account changed, updating avatar in top bar & re-computing meetings list"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TopBarFragment related
|
||||
|
||||
setViewModelAndTitle(
|
||||
listViewModel,
|
||||
getString(R.string.bottom_navigation_meetings_label)
|
||||
)
|
||||
|
||||
listViewModel.searchFilter.observe(viewLifecycleOwner) { filter ->
|
||||
listViewModel.applyFilter(filter.trim())
|
||||
}
|
||||
|
||||
listViewModel.focusSearchBarEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { show ->
|
||||
if (show) {
|
||||
// To automatically open keyboard
|
||||
binding.topBar.search.showKeyboard()
|
||||
} else {
|
||||
binding.topBar.search.hideKeyboard()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 org.linphone.ui.main.meetings.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.linphone.databinding.MeetingScheduleFragmentBinding
|
||||
import org.linphone.ui.main.fragment.GenericFragment
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class ScheduleMeetingFragment : GenericFragment() {
|
||||
companion object {
|
||||
private const val TAG = "[Schedule Meeting Fragment]"
|
||||
}
|
||||
|
||||
private lateinit var binding: MeetingScheduleFragmentBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = MeetingScheduleFragmentBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun goBack(): Boolean {
|
||||
sharedViewModel.closeSlidingPaneEvent.value = Event(true)
|
||||
// If not done, when going back to ConversationsFragment this fragment will be created again
|
||||
return findNavController().popBackStack()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 org.linphone.ui.main.meetings.model
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.core.ConferenceInfo
|
||||
import org.linphone.core.Participant
|
||||
import org.linphone.utils.TimestampUtils
|
||||
|
||||
class MeetingModel @WorkerThread constructor(conferenceInfo: ConferenceInfo) {
|
||||
val id = conferenceInfo.uri?.asStringUriOnly()
|
||||
|
||||
private val timestamp = conferenceInfo.dateTime
|
||||
|
||||
val day = TimestampUtils.dayOfWeek(timestamp)
|
||||
|
||||
val dayNumber = TimestampUtils.dayOfMonth(timestamp)
|
||||
|
||||
val month = TimestampUtils.month(timestamp)
|
||||
|
||||
val isToday = TimestampUtils.isToday(timestamp)
|
||||
|
||||
private val startTime = TimestampUtils.timeToString(timestamp)
|
||||
|
||||
private val endTime = TimestampUtils.timeToString(timestamp + (conferenceInfo.duration * 60))
|
||||
|
||||
val time = "$startTime - $endTime"
|
||||
|
||||
val isBroadcast = MutableLiveData<Boolean>()
|
||||
|
||||
val subject = MutableLiveData<String>()
|
||||
|
||||
init {
|
||||
subject.postValue(conferenceInfo.subject)
|
||||
|
||||
var allSpeaker = true
|
||||
for (participant in conferenceInfo.participantInfos) {
|
||||
if (participant.role == Participant.Role.Listener) {
|
||||
allSpeaker = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
isBroadcast.postValue(!allSpeaker)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 org.linphone.ui.main.meetings.viewmodel
|
||||
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.core.ConferenceInfo
|
||||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreListenerStub
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.meetings.model.MeetingModel
|
||||
import org.linphone.ui.main.viewmodel.AbstractTopBarViewModel
|
||||
|
||||
class MeetingsListViewModel @UiThread constructor() : AbstractTopBarViewModel() {
|
||||
companion object {
|
||||
private const val TAG = "[Meetings List ViewModel]"
|
||||
}
|
||||
|
||||
val meetings = MutableLiveData<ArrayList<MeetingModel>>()
|
||||
|
||||
val fetchInProgress = MutableLiveData<Boolean>()
|
||||
|
||||
private var currentFilter = ""
|
||||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
@WorkerThread
|
||||
override fun onConferenceInfoReceived(core: Core, conferenceInfo: ConferenceInfo) {
|
||||
Log.i("$TAG Conference info received [${conferenceInfo.uri?.asStringUriOnly()}]")
|
||||
computeMeetingsList(currentFilter)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
core.addListener(coreListener)
|
||||
|
||||
computeMeetingsList(currentFilter)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
core.removeListener(coreListener)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun applyFilter(filter: String = currentFilter) {
|
||||
currentFilter = filter
|
||||
|
||||
coreContext.postOnCoreThread {
|
||||
computeMeetingsList(filter)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun computeMeetingsList(filter: String) {
|
||||
val list = arrayListOf<MeetingModel>()
|
||||
|
||||
// TODO FIXME: get list from default account
|
||||
for (conferenceInfo in coreContext.core.conferenceInformationList) {
|
||||
val model = MeetingModel(conferenceInfo)
|
||||
// TODO FIXME: apply filter
|
||||
list.add(model)
|
||||
}
|
||||
|
||||
meetings.postValue(list)
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,10 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
|
|||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
||||
val navigateToMeetingsEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
||||
var currentlyDisplayedFragment = MutableLiveData<Int>()
|
||||
|
||||
/* Top bar related */
|
||||
|
|
@ -104,4 +108,10 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
|
|||
val showConversationEvent: MutableLiveData<Event<Pair<String, String>>> by lazy {
|
||||
MutableLiveData<Event<Pair<String, String>>>()
|
||||
}
|
||||
|
||||
/* Meetings related */
|
||||
|
||||
val showScheduleMeetingEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@ import androidx.annotation.AnyThread
|
|||
import java.text.DateFormat
|
||||
import java.text.Format
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.format.TextStyle
|
||||
import java.util.*
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
|
||||
class TimestampUtils {
|
||||
companion object {
|
||||
private const val TAG = "[Timestamp Utils]"
|
||||
|
||||
@AnyThread
|
||||
fun isToday(timestamp: Long, timestampInSecs: Boolean = true): Boolean {
|
||||
val cal = Calendar.getInstance()
|
||||
|
|
@ -60,6 +63,48 @@ class TimestampUtils {
|
|||
return dateFormatter.format(calendar.time)
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun dayOfWeek(timestamp: Long, timestampInSecs: Boolean = true): String {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = if (timestampInSecs) timestamp * 1000 else timestamp
|
||||
val dayName = calendar.getDisplayName(
|
||||
Calendar.DAY_OF_WEEK,
|
||||
TextStyle.SHORT.ordinal,
|
||||
Locale.getDefault()
|
||||
)
|
||||
val upperCased = dayName?.replaceFirstChar {
|
||||
if (it.isLowerCase()) {
|
||||
it.titlecase(
|
||||
Locale.getDefault()
|
||||
)
|
||||
} else {
|
||||
it.toString()
|
||||
}
|
||||
} ?: "?"
|
||||
val shorten = upperCased.substring(0, 3)
|
||||
return "$shorten."
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun dayOfMonth(timestamp: Long, timestampInSecs: Boolean = true): String {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = if (timestampInSecs) timestamp * 1000 else timestamp
|
||||
return calendar.get(Calendar.DAY_OF_MONTH).toString()
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun month(timestamp: Long, timestampInSecs: Boolean = true): String {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.timeInMillis = if (timestampInSecs) timestamp * 1000 else timestamp
|
||||
return calendar.getDisplayName(
|
||||
Calendar.MONTH,
|
||||
TextStyle.SHORT.ordinal,
|
||||
Locale.getDefault()
|
||||
)
|
||||
?.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
|
||||
?: "?"
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun timeToString(time: Long, timestampInSecs: Boolean = true): String {
|
||||
val use24hFormat = android.text.format.DateFormat.is24HourFormat(
|
||||
|
|
|
|||
9
app/src/main/res/drawable/calendar.xml
Normal file
9
app/src/main/res/drawable/calendar.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="256"
|
||||
android:viewportHeight="256">
|
||||
<path
|
||||
android:pathData="M208,32L184,32L184,24a8,8 0,0 0,-16 0v8L88,32L88,24a8,8 0,0 0,-16 0v8L48,32A16,16 0,0 0,32 48L32,208a16,16 0,0 0,16 16L208,224a16,16 0,0 0,16 -16L224,48A16,16 0,0 0,208 32ZM72,48v8a8,8 0,0 0,16 0L88,48h80v8a8,8 0,0 0,16 0L184,48h24L208,80L48,80L48,48ZM208,208L48,208L48,96L208,96L208,208ZM112,120v64a8,8 0,0 1,-16 0L96,132.94l-4.42,2.22a8,8 0,0 1,-7.16 -14.32l16,-8A8,8 0,0 1,112 120ZM171.16,150.45L152,176h16a8,8 0,0 1,0 16L136,192a8,8 0,0 1,-6.4 -12.8l28.78,-38.37A8,8 0,1 0,145.07 132a8,8 0,1 1,-13.85 -8A24,24 0,0 1,176 136,23.76 23.76,0 0,1 171.16,150.45Z"
|
||||
android:fillColor="#4e6074"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<size android:width="60dp" android:height="60dp" />
|
||||
<solid android:color="@color/orange_main_500"/>
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/slideshow.xml
Normal file
9
app/src/main/res/drawable/slideshow.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="256"
|
||||
android:viewportHeight="256">
|
||||
<path
|
||||
android:pathData="M192,48L64,48A16,16 0,0 0,48 64L48,192a16,16 0,0 0,16 16L192,208a16,16 0,0 0,16 -16L208,64A16,16 0,0 0,192 48ZM192,192L64,192L64,64L192,64L192,192ZM240,56L240,200a8,8 0,0 1,-16 0L224,56a8,8 0,0 1,16 0ZM32,56L32,200a8,8 0,0 1,-16 0L16,56a8,8 0,0 1,16 0Z"
|
||||
android:fillColor="#4e6074"/>
|
||||
</vector>
|
||||
32
app/src/main/res/layout-land/meetings_fragment.xml
Normal file
32
app/src/main/res/layout-land/meetings_fragment.xml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<androidx.slidingpanelayout.widget.SlidingPaneLayout
|
||||
android:id="@+id/sliding_pane_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/meetings_list"
|
||||
android:name="org.linphone.ui.main.meetings.fragment.MeetingsListFragment"
|
||||
android:layout_width="@dimen/sliding_pane_left_fragment_with_nav_width"
|
||||
android:layout_height="match_parent"
|
||||
app:layout="@layout/meetings_list_fragment"/>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/meetings_nav_container"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
app:defaultNavHost="false"
|
||||
app:navGraph="@navigation/meetings_nav_graph"/>
|
||||
|
||||
</androidx.slidingpanelayout.widget.SlidingPaneLayout>
|
||||
|
||||
</layout>
|
||||
137
app/src/main/res/layout-land/meetings_list_fragment.xml
Normal file
137
app/src/main/res/layout-land/meetings_list_fragment.xml
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:bind="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="newMeetingClicked"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="filterClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="todayClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.ui.main.meetings.viewmodel.MeetingsListViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/orange_main_500">
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="no_meeting_image, no_meeting_label"
|
||||
android:visibility="@{viewModel.meetings.empty ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/bottom_nav_bar"
|
||||
android:name="org.linphone.ui.main.fragment.BottomNavBarFragment"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="0dp"
|
||||
bind:layout="@layout/bottom_nav_bar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/top_bar"
|
||||
layout="@layout/top_bar"
|
||||
bind:viewModel="@{viewModel}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/top_bar_height"
|
||||
android:layout_marginEnd="9dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintEnd_toStartOf="@id/today"/>
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{todayClickListener}"
|
||||
android:id="@+id/today"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="9dp"
|
||||
android:src="@drawable/calendar"
|
||||
android:visibility="@{viewModel.searchBarVisible ? View.GONE : View.VISIBLE}"
|
||||
app:tint="@color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/top_bar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/top_bar" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_bar"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/no_meeting_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/illu"
|
||||
android:layout_margin="10dp"
|
||||
app:layout_constraintHeight_max="200dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/no_meeting_label"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintTop_toBottomOf="@id/background" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/section_header_style"
|
||||
android:id="@+id/no_meeting_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/meetings_list_empty"
|
||||
app:layout_constraintBottom_toTopOf="@id/background"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintTop_toBottomOf="@id/no_meeting_image" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/meetings_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/background"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/new_meeting"
|
||||
android:onClick="@{newMeetingClicked}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_margin="16dp"
|
||||
android:src="@drawable/plus_circle"
|
||||
app:tint="@color/gray_main2_700"
|
||||
app:backgroundTint="@color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/operation_in_progress"
|
||||
bind:visibility="@{viewModel.fetchInProgress}" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
26
app/src/main/res/layout/meeting_fragment.xml
Normal file
26
app/src/main/res/layout/meeting_fragment.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="backClickListener"
|
||||
type="View.OnClickListener" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:background="@color/white">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
130
app/src/main/res/layout/meeting_list_cell.xml
Normal file
130
app/src/main/res/layout/meeting_list_cell.xml
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="model"
|
||||
type="org.linphone.ui.main.meetings.model.MeetingModel" />
|
||||
<variable
|
||||
name="onClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="onLongClickListener"
|
||||
type="View.OnLongClickListener" />
|
||||
<variable
|
||||
name="firstMeetingOfTheDay"
|
||||
type="Boolean" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:onClick="@{onClickListener}"
|
||||
android:onLongClick="@{onLongClickListener}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/header_day"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@{firstMeetingOfTheDay ? @dimen/meeting_margin : @dimen/zero, default=@dimen/zero}"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@{model.day, default=`Mon.`}"
|
||||
android:visibility="@{firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:textColor="@color/gray_main2_500"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="@id/header_day_number"
|
||||
app:layout_constraintEnd_toEndOf="@id/header_day_number"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/today_background"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:src="@drawable/shape_circle_primary_background"
|
||||
android:visibility="@{model.isToday && firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_day" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_800"
|
||||
android:id="@+id/header_day_number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{model.dayNumber, default=`19`}"
|
||||
android:visibility="@{firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:textColor="@{model.isToday ? @color/white : @color/gray_main2_500, default=@color/gray_main2_500}"
|
||||
android:textSize="20sp"
|
||||
android:paddingBottom="4dp"
|
||||
app:layout_constraintStart_toStartOf="@id/today_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/today_background"
|
||||
app:layout_constraintBottom_toBottomOf="@id/today_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_day"/>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginTop="@{firstMeetingOfTheDay ? @dimen/meeting_margin : @dimen/zero, default=@dimen/zero}"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardElevation="5dp"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:layout_constraintStart_toEndOf="@id/header_day"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_700"
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{model.subject, default=`Meeting with John`}"
|
||||
android:textSize="13sp"
|
||||
android:textColor="@color/gray_main2_600"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:drawableStart="@{model.isBroadcast ? @drawable/slideshow : @drawable/users_three, default=@drawable/users_three}"
|
||||
android:drawablePadding="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/time"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{model.time, default=`10:00 - 12:00`}"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/gray_main2_500"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
||||
26
app/src/main/res/layout/meeting_schedule_fragment.xml
Normal file
26
app/src/main/res/layout/meeting_schedule_fragment.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="backClickListener"
|
||||
type="View.OnClickListener" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:background="@color/white">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
32
app/src/main/res/layout/meetings_fragment.xml
Normal file
32
app/src/main/res/layout/meetings_fragment.xml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<androidx.slidingpanelayout.widget.SlidingPaneLayout
|
||||
android:id="@+id/sliding_pane_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/meetings_list"
|
||||
android:name="org.linphone.ui.main.meetings.fragment.MeetingsListFragment"
|
||||
android:layout_width="@dimen/sliding_pane_left_fragment_width"
|
||||
android:layout_height="match_parent"
|
||||
app:layout="@layout/meetings_list_fragment"/>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/meetings_nav_container"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="@dimen/sliding_pane_right_fragment_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
app:defaultNavHost="false"
|
||||
app:navGraph="@navigation/meetings_nav_graph"/>
|
||||
|
||||
</androidx.slidingpanelayout.widget.SlidingPaneLayout>
|
||||
|
||||
</layout>
|
||||
22
app/src/main/res/layout/meetings_list_decoration.xml
Normal file
22
app/src/main/res/layout/meetings_list_decoration.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_800"
|
||||
android:id="@+id/header"
|
||||
android:background="@color/white"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="October"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/gray_main2_600"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
</layout>
|
||||
137
app/src/main/res/layout/meetings_list_fragment.xml
Normal file
137
app/src/main/res/layout/meetings_list_fragment.xml
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:bind="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="newMeetingClicked"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="filterClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="todayClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.ui.main.meetings.viewmodel.MeetingsListViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/orange_main_500">
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="no_meeting_image, no_meeting_label"
|
||||
android:visibility="@{viewModel.meetings.empty ? View.VISIBLE : View.GONE}" />
|
||||
|
||||
<include
|
||||
android:id="@+id/top_bar"
|
||||
layout="@layout/top_bar"
|
||||
bind:viewModel="@{viewModel}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/top_bar_height"
|
||||
android:layout_marginEnd="9dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/today"/>
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{todayClickListener}"
|
||||
android:id="@+id/today"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="9dp"
|
||||
android:src="@drawable/calendar"
|
||||
android:visibility="@{viewModel.searchBarVisible ? View.GONE : View.VISIBLE}"
|
||||
app:tint="@color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/top_bar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/top_bar" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@color/white"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_bar" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/no_meeting_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/illu"
|
||||
android:layout_margin="10dp"
|
||||
app:layout_constraintHeight_max="200dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/no_meeting_label"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/background" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/section_header_style"
|
||||
android:id="@+id/no_meeting_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/meetings_list_empty"
|
||||
app:layout_constraintBottom_toTopOf="@id/background"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/no_meeting_image" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/meetings_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/background"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_nav_bar" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/bottom_nav_bar"
|
||||
android:name="org.linphone.ui.main.fragment.BottomNavBarFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
bind:layout="@layout/bottom_nav_bar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/new_meeting"
|
||||
android:onClick="@{newMeetingClicked}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_margin="16dp"
|
||||
android:src="@drawable/plus_circle"
|
||||
app:tint="@color/gray_main2_700"
|
||||
app:backgroundTint="@color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_nav_bar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/operation_in_progress"
|
||||
bind:visibility="@{viewModel.fetchInProgress}" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
|
|
@ -23,6 +23,12 @@
|
|||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/contactsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_contactsFragment_to_meetingsFragment"
|
||||
app:destination="@id/meetingsFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/contactsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
|
@ -42,6 +48,12 @@
|
|||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/historyFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_historyFragment_to_meetingsFragment"
|
||||
app:destination="@id/meetingsFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/historyFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
|
@ -198,6 +210,12 @@
|
|||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/conversationsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_conversationsFragment_to_meetingsFragment"
|
||||
app:destination="@id/meetingsFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/conversationsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
|
@ -213,4 +231,42 @@
|
|||
app:launchSingleTop="true"
|
||||
app:popExitAnim="@anim/slide_out" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/meetingsFragment"
|
||||
android:name="org.linphone.ui.main.meetings.fragment.MeetingsFragment"
|
||||
android:label="MeetingsFragment"
|
||||
tools:layout="@layout/meetings_fragment">
|
||||
<action
|
||||
android:id="@+id/action_meetingsFragment_to_historyFragment"
|
||||
app:destination="@id/historyFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/meetingsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_meetingsFragment_to_conversationsFragment"
|
||||
app:destination="@id/conversationsFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/meetingsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_meetingsFragment_to_contactsFragment"
|
||||
app:destination="@id/contactsFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:popUpTo="@id/meetingsFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/scheduleMeetingFragment"
|
||||
android:name="org.linphone.ui.main.meetings.fragment.ScheduleMeetingFragment"
|
||||
android:label="ScheduleMeetingFragment"
|
||||
tools:layout="@layout/meeting_schedule_fragment"/>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_scheduleMeetingFragment"
|
||||
app:destination="@id/scheduleMeetingFragment"
|
||||
app:enterAnim="@anim/slide_in"
|
||||
app:launchSingleTop="true"
|
||||
app:popExitAnim="@anim/slide_out"/>
|
||||
|
||||
</navigation>
|
||||
24
app/src/main/res/navigation/meetings_nav_graph.xml
Normal file
24
app/src/main/res/navigation/meetings_nav_graph.xml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/meetings_nav_graph"
|
||||
app:startDestination="@id/emptyFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/emptyFragment"
|
||||
android:name="org.linphone.ui.main.fragment.EmptyFragment"
|
||||
android:label="EmptyFragment"
|
||||
tools:layout="@layout/empty_fragment"/>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/meetingFragment"
|
||||
android:name="org.linphone.ui.main.meetings.fragment.MeetingFragment"
|
||||
android:label="MeetingFragment"
|
||||
tools:layout="@layout/meeting_fragment"/>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_meetingFragment"
|
||||
app:destination="@id/meetingFragment"/>
|
||||
|
||||
</navigation>
|
||||
|
|
@ -50,4 +50,6 @@
|
|||
<dimen name="button_max_width">400dp</dimen>
|
||||
<dimen name="dialog_max_width">400dp</dimen>
|
||||
<dimen name="text_input_max_width">400dp</dimen>
|
||||
|
||||
<dimen name="meeting_margin">8dp</dimen>
|
||||
</resources>
|
||||
|
|
@ -331,6 +331,8 @@
|
|||
<string name="new_conversation_no_contact">No contact for the moment…</string>
|
||||
<string name="conversation_text_field_hint">Say something…</string>
|
||||
|
||||
<string name="meetings_list_empty">No meeting for the moment…</string>
|
||||
|
||||
<string name="operation_in_progress_overlay">Operation in progress, please wait</string>
|
||||
|
||||
<string name="call_action_blind_transfer">Transfer</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue