diff --git a/app/build.gradle b/app/build.gradle index 1c3191e..04eb635 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,6 +33,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } @@ -77,7 +78,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' //security - implementation "androidx.security:security-crypto:1.1.0-alpha05" + implementation "androidx.security:security-crypto:1.1.0-alpha06" // Import the Firebase BoM implementation platform('com.google.firebase:firebase-bom:31.3.0') @@ -89,6 +90,11 @@ dependencies { //Hilt implementation "com.google.dagger:hilt-android:2.44" kapt "com.google.dagger:hilt-compiler:2.44" + + //Time + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + + implementation "ru.tinkoff.decoro:decoro:1.1.1" } kapt { correctErrorTypes true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 049602f..06b4750 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,7 +41,7 @@ + android:resource="@drawable/ic_logo" /> diff --git a/app/src/main/java/ru/nsu/fit/modao/MasterActivity.kt b/app/src/main/java/ru/nsu/fit/modao/MasterActivity.kt index c1387b6..48969c3 100644 --- a/app/src/main/java/ru/nsu/fit/modao/MasterActivity.kt +++ b/app/src/main/java/ru/nsu/fit/modao/MasterActivity.kt @@ -30,4 +30,5 @@ class MasterActivity: AppCompatActivity() { return@setOnItemSelectedListener true } } + } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/adapter/ExpensesAdapter.kt b/app/src/main/java/ru/nsu/fit/modao/adapter/ExpensesAdapter.kt index f283b18..ed3c739 100644 --- a/app/src/main/java/ru/nsu/fit/modao/adapter/ExpensesAdapter.kt +++ b/app/src/main/java/ru/nsu/fit/modao/adapter/ExpensesAdapter.kt @@ -6,33 +6,89 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import ru.nsu.fit.modao.R +import ru.nsu.fit.modao.databinding.CounterEventItemBinding +import ru.nsu.fit.modao.databinding.DeletedEventItemBinding import ru.nsu.fit.modao.databinding.ExpenseItemBinding +import ru.nsu.fit.modao.databinding.TransferItemBinding import ru.nsu.fit.modao.models.Currency import ru.nsu.fit.modao.models.Expense import ru.nsu.fit.modao.models.ExpenseListItem import ru.nsu.fit.modao.models.LoadItems class ExpensesAdapter: RecyclerView.Adapter() { - private var expensesList: Array = arrayOf() + private var expensesList: MutableList = mutableListOf() private lateinit var listener: AdapterListener companion object { const val EXPENSE = 0 - const val LOAD = 1 + const val TRANSFER = 1 + const val DELETED = 2 + const val DELETING = 3 + const val LOAD = 4 } fun attachListener(listener: AdapterListener){ this.listener = listener } fun setList(list: Array){ - expensesList = list + expensesList = list.toMutableList() notifyDataSetChanged() } + fun addItems(list: Array) { + val size = expensesList.size + expensesList.addAll(list) + notifyItemRangeChanged(size, list.size) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + EXPENSE -> ExpensesHolder(inflater.inflate(R.layout.expense_item, parent, false)) + TRANSFER -> TransferHolder(inflater.inflate(R.layout.transfer_item, parent, false)) + DELETED -> DeletedEventHolder(inflater.inflate(R.layout.deleted_event_item, parent, false)) + DELETING -> CounterEventHolder(inflater.inflate(R.layout.counter_event_item, parent, false)) + LOAD -> LoadHolder(inflater.inflate(R.layout.empty_item, parent, false)) + else -> throw IllegalArgumentException("Illegal view type: $viewType") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is ExpensesHolder -> holder.bind(expensesList[position] as Expense, listener) + is TransferHolder -> holder.bind(expensesList[position] as Expense, listener) + is DeletedEventHolder -> holder.bind(expensesList[position] as Expense, listener) + is CounterEventHolder -> holder.bind(expensesList[position] as Expense, listener) + is LoadHolder -> holder.bind(expensesList[position] as LoadItems, listener) + } + + } + + override fun getItemViewType(position: Int): Int { + return when (expensesList[position]) { + is Expense -> { + val expense = expensesList[position] as Expense + + when (expense.status) { + 0 -> if (expense.type == 0) EXPENSE else TRANSFER + 1 -> if (expense.type == 0) EXPENSE else TRANSFER + -2 -> DELETING + -1 -> DELETED + else -> throw IllegalArgumentException("status ${expense.status}") + } + } + is LoadItems -> LOAD + } + } + + override fun getItemCount(): Int { + return expensesList.size + } class LoadHolder(item: View): RecyclerView.ViewHolder(item) { fun bind(load: LoadItems, listener: AdapterListener) { listener.onClickItem(load) } } - class ExpensesHolder(item: View): RecyclerView.ViewHolder(item){ - val binding = ExpenseItemBinding.bind(item) + class DeletedEventHolder(item: View): RecyclerView.ViewHolder(item) { + val binding = DeletedEventItemBinding.bind(item) fun bind(expense: Expense, listener: AdapterListener) = with(binding){ shortInfo.text = expense.name amountExpense.text = expense.price.toString() @@ -51,33 +107,65 @@ class ExpensesAdapter: RecyclerView.Adapter() { } } } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val inflater = LayoutInflater.from(parent.context) - return when (viewType) { - EXPENSE -> ExpensesHolder(inflater.inflate(R.layout.expense_item, parent, false)) - LOAD -> LoadHolder(inflater.inflate(R.layout.empty_item, parent, false)) - else -> throw IllegalArgumentException("Illegal view type: $viewType") + class CounterEventHolder(item: View): RecyclerView.ViewHolder(item) { + val binding = CounterEventItemBinding.bind(item) + fun bind(expense: Expense, listener: AdapterListener) = with(binding){ + shortInfo.text = expense.name + amountExpense.text = expense.price.toString() + if (expense.price!! > 0){ + if (expense.currency == Currency.RUB){ + currencyImage.setImageResource(R.drawable.ic_profit_rub) + } + } else { + if (expense.currency == Currency.RUB) { + currencyImage.setImageResource(R.drawable.ic_loss_rub) + amountExpense.setTextColor(Color.parseColor("#D46E6E")) + } + } + root.setOnClickListener { + listener.onClickItem(expense) + } } } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (holder) { - is ExpensesHolder -> holder.bind(expensesList[position] as Expense, listener) - is LoadHolder -> holder.bind(expensesList[position] as LoadItems, listener) + class TransferHolder(item: View): RecyclerView.ViewHolder(item) { + val binding = TransferItemBinding.bind(item) + fun bind(expense: Expense, listener: AdapterListener) = with(binding){ + shortInfo.text = expense.name + amountExpense.text = expense.price.toString() + if (expense.price!! > 0){ + if (expense.currency == Currency.RUB){ + currencyImage.setImageResource(R.drawable.ic_profit_rub) + } + } else { + if (expense.currency == Currency.RUB) { + currencyImage.setImageResource(R.drawable.ic_loss_rub) + amountExpense.setTextColor(Color.parseColor("#D46E6E")) + } + } + root.setOnClickListener { + listener.onClickItem(expense) + } } - } - - override fun getItemViewType(position: Int): Int { - return when (expensesList[position]) { - is Expense -> EXPENSE - is LoadItems -> LOAD + class ExpensesHolder(item: View): RecyclerView.ViewHolder(item){ + val binding = ExpenseItemBinding.bind(item) + fun bind(expense: Expense, listener: AdapterListener) = with(binding){ + shortInfo.text = expense.name + amountExpense.text = expense.price.toString() + if (expense.price!! > 0){ + if (expense.currency == Currency.RUB){ + currencyImage.setImageResource(R.drawable.ic_profit_rub) + } + } else { + if (expense.currency == Currency.RUB) { + currencyImage.setImageResource(R.drawable.ic_loss_rub) + amountExpense.setTextColor(Color.parseColor("#D46E6E")) + } + } + root.setOnClickListener { + listener.onClickItem(expense) + } } } - override fun getItemCount(): Int { - return expensesList.size - } - } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/adapter/FriendsAdapter.java b/app/src/main/java/ru/nsu/fit/modao/adapter/FriendsAdapter.java index 655e0c3..2ac7489 100644 --- a/app/src/main/java/ru/nsu/fit/modao/adapter/FriendsAdapter.java +++ b/app/src/main/java/ru/nsu/fit/modao/adapter/FriendsAdapter.java @@ -3,12 +3,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.button.MaterialButton; + import ru.nsu.fit.modao.R; import ru.nsu.fit.modao.models.User; @@ -46,10 +46,9 @@ public static final class FriendsHolder extends RecyclerView.ViewHolder { View itemView; public void bind(User friend, AdapterListener listener) { - ImageView imageView = itemView.findViewById(R.id.friendItem); - imageView.setOnClickListener(v -> listener.onClickItem(friend)); - TextView textView = itemView.findViewById(R.id.nameFriends); - textView.setText(friend.getUsername()); + MaterialButton cell = itemView.findViewById(R.id.friendItem); + cell.setOnClickListener(v -> listener.onClickItem(friend)); + cell.setText(friend.getUsername()); } public FriendsHolder(@NonNull View itemView) { diff --git a/app/src/main/java/ru/nsu/fit/modao/adapter/GroupAdapter.kt b/app/src/main/java/ru/nsu/fit/modao/adapter/GroupAdapter.kt index fcff5d7..9113e3e 100644 --- a/app/src/main/java/ru/nsu/fit/modao/adapter/GroupAdapter.kt +++ b/app/src/main/java/ru/nsu/fit/modao/adapter/GroupAdapter.kt @@ -1,18 +1,22 @@ package ru.nsu.fit.modao.adapter -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import ru.nsu.fit.modao.R +import ru.nsu.fit.modao.databinding.GroupItemArchiveBinding import ru.nsu.fit.modao.databinding.GroupItemBinding import ru.nsu.fit.modao.models.Group -class GroupAdapter: RecyclerView.Adapter() { +class GroupAdapter: RecyclerView.Adapter() { private var listener: AdapterListener? = null private var groupsList: Array = arrayOf() + companion object { + const val ACTIVE = 0 + const val ARCHIVE = 1 + } fun attachListener(listener: AdapterListener){ this.listener = listener } @@ -20,27 +24,55 @@ class GroupAdapter: RecyclerView.Adapter() { this.groupsList = groupsList notifyDataSetChanged() } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + ACTIVE -> GroupHolder(inflater.inflate(R.layout.group_item, parent, false)) + ARCHIVE -> ArchiveGroupHolder(inflater.inflate(R.layout.group_item_archive, parent, false)) + else -> throw IllegalArgumentException("Unknown type $viewType") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is GroupHolder -> holder.bind(groupsList[position], listener!!) + is ArchiveGroupHolder -> holder.bind(groupsList[position], listener!!) + } + + } + + override fun getItemCount(): Int { + return groupsList.size + } + + override fun getItemViewType(position: Int): Int { + return when (groupsList[position].typeGroup) { + 0 -> ACTIVE + 1 -> ARCHIVE + else -> throw IllegalArgumentException("Unknown type ${groupsList[position].typeGroup}") + } + } + class GroupHolder(item: View): RecyclerView.ViewHolder(item){ val binding = GroupItemBinding.bind(item) val view = item fun bind(group: Group, listener: AdapterListener){ binding.nameGroup.text = group.groupName - view.setOnClickListener(){ + binding.nameGroup.setOnClickListener { listener.onClickItem(group) } } } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.group_item, parent, false) - return GroupHolder(view) - } - - override fun onBindViewHolder(holder: GroupHolder, position: Int) { - holder.bind(groupsList[position], listener!!) - } - - override fun getItemCount(): Int { - return groupsList.size + class ArchiveGroupHolder(item: View): RecyclerView.ViewHolder(item) { + val binding = GroupItemArchiveBinding.bind(item) + val view = item + fun bind(group: Group, listener: AdapterListener){ + binding.nameGroup.text = group.groupName + binding.nameGroup.setOnClickListener { + listener.onClickItem(group) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/adapter/NotificationFriendsAdapter.kt b/app/src/main/java/ru/nsu/fit/modao/adapter/NotificationFriendsAdapter.kt index f888340..3ef8112 100644 --- a/app/src/main/java/ru/nsu/fit/modao/adapter/NotificationFriendsAdapter.kt +++ b/app/src/main/java/ru/nsu/fit/modao/adapter/NotificationFriendsAdapter.kt @@ -1,6 +1,5 @@ package ru.nsu.fit.modao.adapter -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -39,20 +38,17 @@ class NotificationFriendsAdapter: RecyclerView.Adapter, listenerGroup: AdapterListener){ if (elem.nameGroup != null){ - binding.frameNewGroup.visibility = View.VISIBLE binding.nameNewGroup.visibility = View.VISIBLE binding.nameNewGroup.text = elem.nameGroup binding.newGroup.visibility = View.VISIBLE - binding.root.setOnClickListener { - Log.d("MyTag", "Notification adapter group") + binding.nameNewGroup.setOnClickListener { listenerGroup.onClickItem(elem) } } else { - binding.frameNewFriend.visibility = View.VISIBLE binding.nameNewFriend.visibility = View.VISIBLE binding.newFriend.visibility = View.VISIBLE binding.nameNewFriend.text = elem.username - binding.root.setOnClickListener { + binding.nameNewFriend.setOnClickListener { listenerFriend.onClickItem(elem) } } diff --git a/app/src/main/java/ru/nsu/fit/modao/api/ApiService.kt b/app/src/main/java/ru/nsu/fit/modao/api/ApiService.kt index 3784535..d9d8caf 100644 --- a/app/src/main/java/ru/nsu/fit/modao/api/ApiService.kt +++ b/app/src/main/java/ru/nsu/fit/modao/api/ApiService.kt @@ -8,20 +8,45 @@ interface ApiService { @POST("/api/auth/login") suspend fun login(@Body user: User): Response + @POST("/api/auth/token") + suspend fun getAccessToken(@Body refreshToken: Tokens): Response + + @POST("/api/auth/refresh") + suspend fun getRefreshToken(@Body refreshToken: Tokens): Response + @GET("/user/myInfo") suspend fun getUser( @Header("Authorization") token: String ): Response - @GET("/user/listGroups") - suspend fun getUserGroups( + + @GET("/user/exitUser") + suspend fun exit( @Header("Authorization") token: String + ): Response + + @GET("/user/listGroups/{type}") + suspend fun getUserGroups( + @Header("Authorization") token: String, + @Path("type") type: Int ): Response> + @GET("/group/delete/{groupId}") + suspend fun deleteGroup( + @Header("Authorization") token: String, + @Path("groupId") groupId: Long + ): Response + @GET("/group/archive/{groupId}") + suspend fun archiveGroup( + @Header("Authorization") token: String, + @Path("groupId") groupId: Long + ): Response + @POST("/group/create") suspend fun createGroup( @Header("Authorization") token: String, @Body group: Group ): Response + @PUT("/group/addUserInGroup/{groupUUID}") suspend fun addToGroup( @Header("Authorization") token: String, @@ -38,13 +63,38 @@ interface ApiService { @Path("groupId") groupId: Long ): Response - @GET("/event/listEventsConfirmed/{mode}/{groupId}/{type}") + @PUT("/event/delete/{groupId}/{eventId}") + suspend fun deleteEvent( + @Header("Authorization") token: String, + @Path("groupId") groupId: Long, + @Path("eventId") eventId: Long, + @Body name: String + ): Response + + @GET("/group/deleteUser/{groupId}/{userId}") + suspend fun deleteUser( + @Header("Authorization") token: String, + @Path("groupId") groupId: Long, + @Path("userId") userId: Long + ): Response + + @GET("/group/archiveNo/{groupId}") + suspend fun makeGroupActive( + @Header("Authorization") token: String, + @Path("groupId") groupId: Long + ): Response + + @GET("/event/listEventsConfirmed/{mode}/{groupId}/{type}/{minTime}/{maxTime}") suspend fun getGroupExpenses( @Header("Authorization") token: String, @Path("groupId") id: Long, @Path("mode") mode: Int, - @Path("type") type: Int - ): Response> + @Path("type") type: Int, + @Path("minTime") minTime: Long, + @Path("maxTime") maxTime: Long, + @Query("offset") offset: Int, + @Query("limit") limit: Int + ): Response @GET("/event/listEventsUnconfirmed/{groupId}") suspend fun getGroupUnconfirmedExpenses( @@ -85,55 +135,66 @@ interface ApiService { @Header("Authorization") token: String, @Path("groupId") groupId: Long ): Response> + @GET("/debt/{userId}/{groupId}") suspend fun getUserDebtInGroup( @Header("Authorization") token: String, @Path("userId") userId: Long, @Path("groupId") groupId: Long ): Response> + @GET("/group/listOrganizers/{groupId}") suspend fun getListOrganizers( @Header("Authorization") token: String, @Path("groupId") groupId: Long ): Response> + @GET("/group/info/{groupId}") suspend fun getGroupInfo( @Header("Authorization") token: String, @Path("groupId") groupId: Long ): Response + @GET("/invitation/getInvitationsFriend") suspend fun getInvitationsFriend( @Header("Authorization") token: String ): Response> + @POST("/invitation/createInvitationFriend/{userUuid}") suspend fun addFriend( @Header("Authorization") token: String, @Path("userUuid") userUuid: String ): Response + @GET("/invitation/getInvitationsInGroup") suspend fun getInvitationsGroup( @Header("Authorization") token: String ): Response> + @POST("/invitation/acceptInvitationFriend/{invitationId}") suspend fun acceptInvitationFriend( @Header("Authorization") token: String, @Path("invitationId") invitationId: Long ): Response + @POST("/invitation/declineInvitationFriend/{invitationId}") suspend fun denyInvitationFriend( @Header("Authorization") token: String, @Path("invitationId") invitationId: Long ): Response + @POST("/invitation/acceptInvitationInGroup/{invitationId}") suspend fun acceptInvitationGroup( @Header("Authorization") token: String, @Path("invitationId") invitationId: Long ): Response + @POST("/invitation/declineInvitationInGroup/{invitationId}") suspend fun denyInvitationGroup( @Header("Authorization") token: String, @Path("invitationId") invitationId: Long ): Response + @GET("/user/listFriends") suspend fun getListFriends( @Header("Authorization") token: String diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/AddMemberFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/AddMemberFragment.kt index babecc7..b39a26c 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/AddMemberFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/AddMemberFragment.kt @@ -50,7 +50,6 @@ class AddMemberFragment : BottomSheetDialogFragment(), AdapterListener !members.contains(user)}.map { user -> ParticipantEvent( diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/AuthorizationFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/AuthorizationFragment.kt index 371429a..da8d381 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/AuthorizationFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/AuthorizationFragment.kt @@ -15,6 +15,7 @@ import ru.nsu.fit.modao.models.Authorization import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.utils.Constants.Companion.ACCESS_TOKEN import ru.nsu.fit.modao.utils.Constants.Companion.ID_USER +import ru.nsu.fit.modao.utils.Constants.Companion.REFRESH_TOKEN import ru.nsu.fit.modao.viewmodels.LoginViewModel import javax.inject.Inject @@ -54,7 +55,8 @@ class AuthorizationFragment : Fragment() { binding.tipMessage.setText(R.string.wait) loginViewModel.login( login = binding.personLogin.text.toString(), - password = binding.personPassword.text.toString() + password = binding.personPassword.text.toString(), + app.deviceToken ) } binding.buttonSignUp.setOnClickListener { @@ -67,7 +69,8 @@ class AuthorizationFragment : Fragment() { app.accessToken = it.accessToken app.refreshToken = it.refreshToken val edit = app.encryptedSharedPreferences.edit() - edit.putString(ACCESS_TOKEN, it.accessToken).putLong(ID_USER, it.id).apply() + edit.putString(ACCESS_TOKEN, it.accessToken).putLong(ID_USER, it.id) + .putString(REFRESH_TOKEN, it.refreshToken).apply() activity?.findViewById(R.id.bottomMenu)?.visibility = View.VISIBLE findNavController().navigate(AuthorizationFragmentDirections.actionAuthorizationFragmentToProfileFragment()) } diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/CreateExpenseFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/CreateExpenseFragment.kt index 400f261..0eceed7 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/CreateExpenseFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/CreateExpenseFragment.kt @@ -22,17 +22,25 @@ import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.viewmodels.CreateExpenseViewModel import ru.nsu.fit.modao.viewmodels.MainViewModel import javax.inject.Inject + @AndroidEntryPoint class CreateExpenseFragment : Fragment(), AdapterListener { private var _binding: FragmentCreateExpenseBinding? = null private val binding get() = _binding!! private var menuList = listOf("Who spent", "Participants", "Coefficient") private val adapter = MenuAdapter() + private val listDestinations = listOf( + R.id.enterCostFragment, + R.id.selectParticipantsFragment, + R.id.enterCoefficientsFragment + ) + @Inject lateinit var app: App private val args by navArgs() private val mainViewModel: MainViewModel by viewModels() private val createExpenseViewModel: CreateExpenseViewModel by viewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -50,6 +58,8 @@ class CreateExpenseFragment : Fragment(), AdapterListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + initOrganizer() binding.recyclerMenu.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) adapter.attachListener(this) @@ -59,10 +69,22 @@ class CreateExpenseFragment : Fragment(), AdapterListener { mainViewModel.getUsersInGroup(args.dataExpense.group.id!!) createExpenseViewModel.eventId.observe(viewLifecycleOwner) { - findNavController().navigate( - CreateExpenseFragmentDirections - .actionCreateExpenseFragmentToGroupExpensesFragment(args.dataExpense.group) - ) + if (args.dataExpense.group.isOrganizer == null) { + findNavController().navigate( + CreateExpenseFragmentDirections + .actionCreateExpenseFragmentToDataConfirmationFragment(args.dataExpense.group) + ) + } else if (args.dataExpense.group.isOrganizer!!) { + findNavController().navigate( + CreateExpenseFragmentDirections + .actionCreateExpenseFragmentToGroupExpensesFragment(args.dataExpense.group) + ) + } else { + findNavController().navigate( + CreateExpenseFragmentDirections + .actionCreateExpenseFragmentToDataConfirmationFragment(args.dataExpense.group) + ) + } } mainViewModel.usersInGroup.observe(viewLifecycleOwner) { createExpenseViewModel.participants.value = it.map { user -> @@ -100,6 +122,15 @@ class CreateExpenseFragment : Fragment(), AdapterListener { } } + private fun initOrganizer() { + if (args.dataExpense.group.isOrganizer == null) { + mainViewModel.getListOrganizers(args.dataExpense.group.id!!) + mainViewModel.organizers.observe(viewLifecycleOwner) { + args.dataExpense.group.isOrganizer = it.any { org -> org.id == app.userId } + } + } + } + override fun onClickItem(item: String) { when (item) { menuList[0] -> activity?.findNavController(R.id.innerFragment) diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/CreateGroupFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/CreateGroupFragment.kt index 570f71d..f652eac 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/CreateGroupFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/CreateGroupFragment.kt @@ -1,7 +1,6 @@ package ru.nsu.fit.modao.fragments import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -34,9 +33,11 @@ class CreateGroupFragment: Fragment() { super.onViewCreated(view, savedInstanceState) mainViewModel.groupId.observe(viewLifecycleOwner) { - mainViewModel.getUserGroups() - group?.id = it - findNavController().navigate(CreateGroupFragmentDirections.actionCreateGroupFragmentToGroupInfoFragment(group!!)) + mainViewModel.getGroupInfo(it) + } + mainViewModel.groupInfo.observe(viewLifecycleOwner) { + findNavController().navigate(CreateGroupFragmentDirections + .actionCreateGroupFragmentToGroupInfoFragment(it)) } binding.buttonNext.setOnClickListener { val name = binding.nameText.text.toString() @@ -46,7 +47,6 @@ class CreateGroupFragment: Fragment() { } group = Group(typeGroup = 0, groupName = name, description = binding.descriptionText.text.toString()) - Log.d("MyTag", "In button") mainViewModel.createGroup(group!!) } } diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/CreateNewEventFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/CreateNewEventFragment.kt index 25f09cf..a9f7c40 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/CreateNewEventFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/CreateNewEventFragment.kt @@ -2,6 +2,7 @@ package ru.nsu.fit.modao.fragments import android.app.AlertDialog import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -26,6 +27,7 @@ import javax.inject.Inject class CreateNewEventFragment : Fragment() { private var _binding: FragmentCreateNewEventBinding? = null private val binding get() = _binding!! + @Inject lateinit var app: App private val createExpenseViewModel: CreateExpenseViewModel by activityViewModels() @@ -48,38 +50,31 @@ class CreateNewEventFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + mainViewModel.getUsersInGroup(args.group.id!!) createExpenseViewModel.payFor = false + initObserver() + initButton() + initOrganizer() + } + + private fun initOrganizer() { + if (args.group.isOrganizer == null) { + mainViewModel.getListOrganizers(args.group.id!!) + mainViewModel.organizers.observe(viewLifecycleOwner) { + args.group.isOrganizer = it.any { org -> org.id == app.userId } + } + } + } + private fun initButton() { binding.grayNewTransfer.setOnClickListener { changeMode(View.VISIBLE, View.GONE) } binding.grayNewExpense.setOnClickListener { changeMode(View.GONE, View.VISIBLE) } - mainViewModel.usersInGroup.observe(viewLifecycleOwner) { - val array = it.map { user -> - createListParticipant(user) - }.toTypedArray() - createExpenseViewModel.users = array - createExpenseViewModel.participants.value = array.clone() - } - createExpenseViewModel.message.observe(viewLifecycleOwner) { - val builder = AlertDialog.Builder(context) - builder.setMessage(it) - builder.setPositiveButton("OK") { _, _ -> } - builder.create().show() - createExpenseViewModel.message.value = null - } - createExpenseViewModel.eventId.observe(viewLifecycleOwner) { - if (lastEvent === it){ - return@observe - } - createExpenseViewModel.eventId.value = lastEvent - findNavController().navigate(CreateNewEventFragmentDirections - .actionCreateAnExpenseFragmentToGroupExpensesFragment(args.group)) - } binding.buttonMoreOptions.setOnClickListener { createExpenseViewModel.payFor = false @@ -93,18 +88,26 @@ class CreateNewEventFragment : Fragment() { } val cost: Float try { - cost = binding.enterCost.text.toString().toFloat() + cost = binding.enterCost.text.toString().toFloat() } catch (e: NumberFormatException) { builder.setTitle("Enter cost") builder.create().show() return@setOnClickListener } - findNavController().navigate(CreateNewEventFragmentDirections - .actionCreateAnExpenseFragmentToCreateExpenseFragment(CreationExpense(args.group, cost, description))) + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToCreateExpenseFragment( + CreationExpense( + args.group, + cost, + description + ) + ) + ) } binding.buttonAll.setOnClickListener { - //createExpenseViewModel.participants.value?.forEach { user -> user.selected = true } + createExpenseViewModel.participants.value?.forEach { user -> user.selected = true } createExpenseViewModel.payFor = false createExpenseViewModel.participants.value = createExpenseViewModel.users Toast.makeText(context, "All members are selected", Toast.LENGTH_SHORT).show() @@ -112,18 +115,24 @@ class CreateNewEventFragment : Fragment() { binding.buttonTwo.setOnClickListener { createExpenseViewModel.payFor = false - findNavController().navigate(CreateNewEventFragmentDirections - .actionCreateAnExpenseFragmentToSelectSecondParticipantFragment(args.group)) + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToSelectSecondParticipantFragment(args.group) + ) } binding.selectParticipant.setOnClickListener { createExpenseViewModel.payFor = false - findNavController().navigate(CreateNewEventFragmentDirections - .actionCreateAnExpenseFragmentToSelectSecondParticipantFragment(args.group)) + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToSelectSecondParticipantFragment(args.group) + ) } binding.buttonPayFor.setOnClickListener { createExpenseViewModel.payFor = true - findNavController().navigate(CreateNewEventFragmentDirections - .actionCreateAnExpenseFragmentToSelectSecondParticipantFragment(args.group)) + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToSelectSecondParticipantFragment(args.group) + ) } binding.buttonFinish.setOnClickListener { var type = 0 @@ -138,8 +147,50 @@ class CreateNewEventFragment : Fragment() { } } + private fun initObserver() { + mainViewModel.usersInGroup.observe(viewLifecycleOwner) { + val array = it.map { user -> + createListParticipant(user) + }.toTypedArray() + createExpenseViewModel.users = array + createExpenseViewModel.participants.value = array.clone() + } + createExpenseViewModel.message.observe(viewLifecycleOwner) { + if (it != null) { + val builder = AlertDialog.Builder(context) + builder.setMessage(it) + builder.setPositiveButton("OK") { _, _ -> } + builder.create().show() + createExpenseViewModel.message.postValue(null) + } else Log.d("MyTag", "null") + } - private fun changeMode(transfer: Int, expense: Int){ + createExpenseViewModel.eventId.observe(viewLifecycleOwner) { + if (lastEvent === it) { + return@observe + } + createExpenseViewModel.eventId.value = lastEvent + if (args.group.isOrganizer == null) { + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToDataConfirmationFragment(args.group) + ) + } else if (args.group.isOrganizer!!) { + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToGroupExpensesFragment(args.group) + ) + } else { + findNavController().navigate( + CreateNewEventFragmentDirections + .actionCreateAnExpenseFragmentToDataConfirmationFragment(args.group) + ) + } + + } + } + + private fun changeMode(transfer: Int, expense: Int) { binding.grayNewExpense.visibility = transfer binding.newExpenseButton.visibility = expense binding.grayNewTransfer.visibility = expense @@ -150,6 +201,7 @@ class CreateNewEventFragment : Fragment() { binding.buttonAll.visibility = expense binding.buttonPayFor.visibility = expense } + private fun createListParticipant(user: User): ParticipantEvent { return if (user.id == app.userId) { ParticipantEvent( diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/DataConfirmationFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/DataConfirmationFragment.kt index 971c1f7..b282522 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/DataConfirmationFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/DataConfirmationFragment.kt @@ -2,11 +2,11 @@ package ru.nsu.fit.modao.fragments import android.app.AlertDialog import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -22,16 +22,20 @@ import ru.nsu.fit.modao.databinding.PopUpWindowDataConfBinding import ru.nsu.fit.modao.models.Expense import ru.nsu.fit.modao.models.ExpenseListItem import ru.nsu.fit.modao.models.LoadItems -import ru.nsu.fit.modao.utils.Constants +import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.viewmodels.MainViewModel +import javax.inject.Inject @AndroidEntryPoint class DataConfirmationFragment : Fragment(), AdapterListener { private var _binding: FragmentDataConfirmationBinding? = null private val binding get() = _binding!! private val mainViewModel: MainViewModel by viewModels() + @Inject + lateinit var app: App private val adapter = ExpensesAdapter() private val args by navArgs() + private var organizer = false override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentDataConfirmationBinding.inflate(inflater, container, false) return binding.root @@ -42,9 +46,25 @@ class DataConfirmationFragment : Fragment(), AdapterListener { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + class OnBack(enable: Boolean): OnBackPressedCallback(enable) { + override fun handleOnBackPressed() { + if (findNavController().currentBackStackEntry?.destination?.id == R.id.dataConfirmationFragment) { + if (findNavController().previousBackStackEntry?.destination?.id != R.id.groupInfoFragment) { + findNavController().navigate(DataConfirmationFragmentDirections + .actionDataConfirmationFragmentToGroupExpensesFragment(args.group)) + } else { + findNavController().popBackStack() + } + } else { + findNavController().popBackStack() + } + } + } + activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, OnBack(true)) initObserver() mainViewModel.getGroupUnconfirmedExpenses(args.group.id!!) + mainViewModel.getListOrganizers(args.group.id!!) adapter.attachListener(this) binding.recyclerUnconfirmed.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) binding.recyclerUnconfirmed.adapter = adapter @@ -52,7 +72,9 @@ class DataConfirmationFragment : Fragment(), AdapterListener { private fun initObserver() { mainViewModel.infoEvent.observe(viewLifecycleOwner) {item -> - Log.d("MyTag", "infoEvent") + if (item == null) { + return@observe + } val builder = AlertDialog.Builder(context) val view = layoutInflater.inflate(R.layout.pop_up_window_data_conf, null) val alertBinding = PopUpWindowDataConfBinding.bind(view) @@ -60,6 +82,11 @@ class DataConfirmationFragment : Fragment(), AdapterListener { alertBinding.cost.text = item.price?.toString() alertBinding.description.text = item.name alertBinding.whoCreated.text = item.usernameCreator + if (!organizer || args.group.typeGroup == 1) { + alertBinding.noButton.visibility = View.GONE + alertBinding.yesButton.visibility = View.GONE + alertBinding.textConfirm.visibility = View.GONE + } val dialog = builder.create() alertBinding.noButton.setOnClickListener { val id = item.id @@ -80,15 +107,18 @@ class DataConfirmationFragment : Fragment(), AdapterListener { } dialog.show() + mainViewModel.infoEvent.postValue(null) } mainViewModel.unconfirmedExpenses.observe(viewLifecycleOwner){ val list: MutableList = it.toMutableList() - if (it.size == Constants.PAGE_SIZE) { - list.add(Constants.PAGE_SIZE - 5, LoadItems(isLoad = false)) - } adapter.setList(list.toTypedArray()) } + mainViewModel.organizers.observe(viewLifecycleOwner) { + organizer = it.any { org -> org.id == app.userId } + } } + + override fun onClickItem(item: ExpenseListItem) { when (item) { is LoadItems -> { diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/FriendsFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/FriendsFragment.kt index 41ce4dd..c5cf9e7 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/FriendsFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/FriendsFragment.kt @@ -15,6 +15,10 @@ import ru.nsu.fit.modao.adapter.FriendsAdapter import ru.nsu.fit.modao.databinding.FragmentFriendsBinding import ru.nsu.fit.modao.models.User import ru.nsu.fit.modao.viewmodels.MainViewModel +import ru.tinkoff.decoro.MaskImpl +import ru.tinkoff.decoro.parser.UnderscoreDigitSlotsParser +import ru.tinkoff.decoro.watchers.MaskFormatWatcher + @AndroidEntryPoint class FriendsFragment : Fragment(), AdapterListener { private var _binding: FragmentFriendsBinding? = null @@ -38,7 +42,6 @@ class FriendsFragment : Fragment(), AdapterListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - adapter.setListener(this) binding.friendsRecycler.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) @@ -58,14 +61,16 @@ class FriendsFragment : Fragment(), AdapterListener { builder.setPositiveButton("OK") { _, _ -> } builder.create().show() } + val slots = UnderscoreDigitSlotsParser().parseSlots("___-___-___") + MaskFormatWatcher(MaskImpl.createTerminated(slots)).installOn(binding.AddByUUID) } override fun onClickItem(item: User) { - val builder = AlertDialog.Builder(context) + /*val builder = AlertDialog.Builder(context) builder.setTitle(item.username) builder.setMessage("Phone: " + item.phone_number + "\n" + "Bank: " + item.bank) builder.setPositiveButton("OK") { _, _ -> } - builder.create().show() + builder.create().show()*/ } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupExpensesFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupExpensesFragment.kt index e3c8ec1..f11dd71 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupExpensesFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupExpensesFragment.kt @@ -1,12 +1,12 @@ package ru.nsu.fit.modao.fragments import android.app.AlertDialog +import android.graphics.Paint import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.PopupWindow -import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -24,10 +24,10 @@ import ru.nsu.fit.modao.databinding.PopUpWindowDataConfBinding import ru.nsu.fit.modao.models.Expense import ru.nsu.fit.modao.models.ExpenseListItem import ru.nsu.fit.modao.models.LoadItems +import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.utils.Constants.Companion.PAGE_SIZE import ru.nsu.fit.modao.viewmodels.MainViewModel -import java.text.DateFormat -import java.util.* +import javax.inject.Inject @AndroidEntryPoint class GroupExpensesFragment : Fragment(), AdapterListener { @@ -36,14 +36,22 @@ class GroupExpensesFragment : Fragment(), AdapterListener { private val adapter = ExpensesAdapter() private val args by navArgs() private val mainViewModel: MainViewModel by viewModels() + @Inject + lateinit var app: App private var showEvent = true private var showTransfer = true private var showOnlyMy = false private var lastEvent: Expense? = null private lateinit var window: PopupWindow + private var totalPages = 2 + private var lastPage = 0 private lateinit var bindingPopupWindow: FilterExpensesBinding + private var newList = true + private var minTime: Long = 0 + private var maxTime: Long = 9999999999999L private val Boolean.intValue get() = if (this) 1 else 0 + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -58,24 +66,62 @@ class GroupExpensesFragment : Fragment(), AdapterListener { _binding = null } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mainViewModel.getGroupExpenses( + args.group.id!!, + showOnlyMy.intValue, + 2, + minTime, + maxTime, + 0, + PAGE_SIZE + ) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + newList = true + lastPage = 0 + minTime = 0 + maxTime = 9999999999999L setRecycler() setButtonOnClick() setPopupWindow() - mainViewModel.getGroupExpenses(args.group.id!!, showOnlyMy.intValue, 2) setObserver() + initOrganizer() } + private fun initOrganizer() { + if (args.group.isOrganizer == null) { + mainViewModel.getListOrganizers(args.group.id!!) + mainViewModel.organizers.observe(viewLifecycleOwner) { + args.group.isOrganizer = it.any { org -> org.id == app.userId } + } + } + } private fun setObserver() { mainViewModel.expenses.observe(viewLifecycleOwner) { + val list: MutableList = it.toMutableList() - if (it.size > PAGE_SIZE) { - list.add(PAGE_SIZE - 5, LoadItems(isLoad = false)) + + if (totalPages > lastPage + 1) { + lastPage++ + if (list.size > PAGE_SIZE - 5) { + list.add(PAGE_SIZE - 5, LoadItems(isLoad = false, lastPage)) + } + } + if (newList) { + newList = false + adapter.setList(list.toTypedArray()) + } else { + adapter.addItems(list.toTypedArray()) } - adapter.setList(list.toTypedArray()) + + } + mainViewModel.totalPages.observe(viewLifecycleOwner) { + totalPages = it } mainViewModel.infoEvent.observe(viewLifecycleOwner) { @@ -94,16 +140,33 @@ class GroupExpensesFragment : Fragment(), AdapterListener { bindingAlert.textConfirm.visibility = View.GONE builder.setView(bindingAlert.root) val dialog = builder.create() - bindingAlert.buttonDetails.setOnClickListener {_ -> - dialog.dismiss() - findNavController().navigate(GroupExpensesFragmentDirections - .actionGroupExpensesFragmentToSeeDetailsFragment(false, it)) + if (it.status != -2) { + bindingAlert.buttonDetails.setOnClickListener { _ -> + dialog.dismiss() + val action = GroupExpensesFragmentDirections + .actionGroupExpensesFragmentToSeeDetailsFragment(false, it) + action.group = args.group + findNavController().navigate(action) + } + } else { + bindingAlert.buttonDetails.text = "See deleted event" + bindingAlert.buttonDetails.paintFlags = + bindingAlert.buttonDetails.paintFlags or Paint.UNDERLINE_TEXT_FLAG + bindingAlert.buttonDetails.setOnClickListener {_ -> + dialog.dismiss() + mainViewModel.getEventInfo(it.deleteId!!, args.group.id!!) + } } + dialog.show() } } + private fun setButtonOnClick() { + if (args.group.typeGroup == 1) { + binding.buttonAddEvent.visibility = View.GONE + } binding.buttonAddEvent.setOnClickListener { findNavController().navigate( GroupExpensesFragmentDirections @@ -125,42 +188,44 @@ class GroupExpensesFragment : Fragment(), AdapterListener { showDatePicker() } } - private fun showDatePicker(){ + + private fun showDatePicker() { val dialog = MaterialDatePicker.Builder.dateRangePicker() .setTheme(R.style.MaterialCalendarTheme) .setPositiveButtonText("Save") .setNegativeButtonText("Cancel") .build() dialog.addOnPositiveButtonClickListener { - val date1 = Date(it.first) - val date2 = Date(it.second) - Toast.makeText(context, "${DateFormat.getDateInstance().format(date1)} " + - "- ${DateFormat.getDateInstance().format(date2)}", - Toast.LENGTH_LONG).show() + minTime = it.first - 3600000 * 3 + maxTime = it.second + 3600000 * 21 + getGroupExpenses() } dialog.show(childFragmentManager, "MyTag") } - private fun setRecycler(){ + + private fun setRecycler() { adapter.attachListener(this) binding.expensesRecycler.layoutManager = LinearLayoutManager(this.context, RecyclerView.VERTICAL, false) binding.expensesRecycler.adapter = adapter } - private fun setPopupWindow(){ + private fun setPopupWindow() { window = PopupWindow(context) - val view = LayoutInflater.from(context).inflate(R.layout.filter_expenses, binding.root, false) + val view = + LayoutInflater.from(context).inflate(R.layout.filter_expenses, binding.root, false) bindingPopupWindow = FilterExpensesBinding.bind(view) window.contentView = bindingPopupWindow.root - window.isFocusable = true bindingPopupWindow.event.isChecked = true bindingPopupWindow.transfer.isChecked = true + window.isFocusable = true setButton() } - private fun setButton(){ + + private fun setButton() { bindingPopupWindow.transfer.setOnClickListener { - if (showTransfer){ - if (bindingPopupWindow.event.isChecked){ + if (showTransfer) { + if (bindingPopupWindow.event.isChecked) { showTransfer = false bindingPopupWindow.transfer.isChecked = false } @@ -189,19 +254,21 @@ class GroupExpensesFragment : Fragment(), AdapterListener { getGroupExpenses() } } - private fun getGroupExpenses(){ - if (bindingPopupWindow.transfer.isChecked){ - if (bindingPopupWindow.event.isChecked) { - mainViewModel.getGroupExpenses(args.group.id!!, showOnlyMy.intValue, 2) - } else { - mainViewModel.getGroupExpenses(args.group.id!!, showOnlyMy.intValue, 1) - } - } else { - if (bindingPopupWindow.event.isChecked) { - mainViewModel.getGroupExpenses(args.group.id!!, showOnlyMy.intValue, 0) - } - } + + private fun getGroupExpenses() { + newList = true + lastPage = 0 + mainViewModel.getGroupExpenses( + args.group.id!!, + showOnlyMy.intValue, + getFilter(), + minTime, + maxTime, + 0, + PAGE_SIZE + ) } + override fun onClickItem(item: ExpenseListItem) { when (item) { is Expense -> mainViewModel.getEventInfo(item.id!!, args.group.id!!) @@ -210,8 +277,17 @@ class GroupExpensesFragment : Fragment(), AdapterListener { return } item.isLoad = true + mainViewModel.getGroupExpenses( + args.group.id!!, showOnlyMy.intValue, + getFilter(), minTime, maxTime, item.page, PAGE_SIZE + ) } } + } + private fun getFilter(): Int { + return if (bindingPopupWindow.transfer.isChecked) { + if (bindingPopupWindow.event.isChecked) 2 else 1 + } else 0 } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInfoFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInfoFragment.kt index 15a38b2..10739f1 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInfoFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInfoFragment.kt @@ -1,7 +1,7 @@ package ru.nsu.fit.modao.fragments +import android.graphics.Color import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -41,22 +41,24 @@ class GroupInfoFragment : Fragment() { super.onViewCreated(view, savedInstanceState) if (args.notification && first) { - Log.d("MyTag", "group info to data") first = false findNavController().navigate(GroupInfoFragmentDirections .actionGroupInfoFragmentToDataConfirmationFragment(args.group)) } binding.nameGroup.text = args.group.groupName - - mainViewModel.getListOrganizers(args.group.id!!) - mainViewModel.organizers.observe(viewLifecycleOwner) { - val isOrganizer = it.any { org -> org.id == app.userId } - if (isOrganizer) { - binding.buttonDataConfirmation.visibility = View.VISIBLE - binding.textDataConfirmation.visibility = View.VISIBLE - } + if (args.group.typeGroup == 1) { + binding.status.setTextColor(Color.parseColor("#FF00BCD4")) + binding.status.text = "Archived" + } else { + binding.status.setTextColor(Color.parseColor("#1AE622")) + binding.status.text = "Active" } + mainViewModel.getListOrganizers(args.group.id!!) + initObserver() + initButton() + } + private fun initButton() { binding.buttonGroupExpenses.setOnClickListener { findNavController().navigate( GroupInfoFragmentDirections.actionGroupInfoFragmentToGroupExpensesFragment( @@ -85,5 +87,12 @@ class GroupInfoFragment : Fragment() { ) ) } + + } + private fun initObserver() { + mainViewModel.organizers.observe(viewLifecycleOwner) { + args.group.isOrganizer = it.any { org -> org.id == app.userId } + } + } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInformationFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInformationFragment.kt index d5f6ce1..07380ca 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInformationFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupInformationFragment.kt @@ -2,16 +2,22 @@ package ru.nsu.fit.modao.fragments import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint +import ru.nsu.fit.modao.R import ru.nsu.fit.modao.databinding.FragmentGroupInformationBinding import ru.nsu.fit.modao.utils.App +import ru.nsu.fit.modao.utils.Constants import ru.nsu.fit.modao.viewmodels.MainViewModel +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter import javax.inject.Inject @AndroidEntryPoint @@ -19,33 +25,97 @@ class GroupInformationFragment : Fragment() { private var _binding: FragmentGroupInformationBinding? = null private val binding get() = _binding!! private val mainViewModel: MainViewModel by viewModels() + @Inject lateinit var app: App private val args by navArgs() - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { _binding = FragmentGroupInformationBinding.inflate(inflater, container, false) return binding.root } + override fun onDestroyView() { super.onDestroyView() _binding = null } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mainViewModel.getGroupInfo(args.group.id!!) - mainViewModel.groupInfo.observe(viewLifecycleOwner) { - binding.groupName.text = it.groupName - binding.groupUuid.text = it.uuid - binding.groupDescription.text = it.description + initView() + initButton() + initObserver() + } + private fun initView() { + val time = LocalDateTime.parse(args.group.time) + val pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + binding.dataCreation.text = time.format(pattern) + if (args.group.isOrganizer == null) { + mainViewModel.getListOrganizers(args.group.id!!) + } else if (args.group.isOrganizer!!) { + binding.archiveGroup.visibility = View.VISIBLE + binding.deleteGroup.visibility = View.VISIBLE + binding.titleGroupUuid.visibility = View.VISIBLE + binding.groupUuid.visibility = View.VISIBLE + } + + } + private fun initButton() { + if (args.group.typeGroup == 1) { + binding.archiveGroup.setText(R.string.makeGroupActive) + binding.archiveGroup.setOnClickListener { + mainViewModel.makeGroupActive(args.group.id!!) + } + } else { + binding.archiveGroup.setOnClickListener { + mainViewModel.archiveGroup(args.group.id!!) + } } - mainViewModel.getListOrganizers(args.group.id!!) + binding.deleteGroup.setOnClickListener { + mainViewModel.deleteGroup(args.group.id!!) + } + } + + private fun initObserver() { mainViewModel.organizers.observe(viewLifecycleOwner) { val isOrganizer = it.any { org -> org.id == app.userId } + args.group.isOrganizer = isOrganizer if (isOrganizer) { + binding.archiveGroup.visibility = View.VISIBLE + binding.deleteGroup.visibility = View.VISIBLE binding.titleGroupUuid.visibility = View.VISIBLE binding.groupUuid.visibility = View.VISIBLE } } + mainViewModel.groupInfo.observe(viewLifecycleOwner) { + binding.groupName.text = it.groupName + binding.groupUuid.text = it.uuid + binding.groupDescription.text = it.description + } + mainViewModel.tipMessage.observe(viewLifecycleOwner) { + when (it) { + "Deleted" -> findNavController().navigate( + GroupInformationFragmentDirections + .actionGlobalNestedGroups() + ) + + "Archived" -> findNavController().navigate( + GroupInformationFragmentDirections + .actionGlobalNestedGroups() + ) + + Constants.SUCCESS -> findNavController().navigate( + GroupInformationFragmentDirections + .actionGlobalNestedGroups() + ) + + else -> Log.d("MyTag", "Unknown message $it") + } + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupMembersFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupMembersFragment.kt index e839986..b22094a 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupMembersFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupMembersFragment.kt @@ -16,7 +16,10 @@ import ru.nsu.fit.modao.adapter.AdapterListener import ru.nsu.fit.modao.adapter.FriendsAdapter import ru.nsu.fit.modao.databinding.FragmentGroupMembersBinding import ru.nsu.fit.modao.models.User +import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.viewmodels.MainViewModel +import javax.inject.Inject + @AndroidEntryPoint class GroupMembersFragment : Fragment(), AdapterListener { private var _binding: FragmentGroupMembersBinding? = null @@ -24,6 +27,9 @@ class GroupMembersFragment : Fragment(), AdapterListener { private val mainViewModel: MainViewModel by viewModels() private val args by navArgs() private val adapter = FriendsAdapter() + private var organizer = false + @Inject + lateinit var app: App override fun onCreateView( inflater: LayoutInflater, @@ -51,7 +57,7 @@ class GroupMembersFragment : Fragment(), AdapterListener { mainViewModel.usersInGroup.observe(viewLifecycleOwner) { adapter.setFriendsList(it) } - + mainViewModel.getListOrganizers(args.group.id!!) mainViewModel.getUsersInGroup(args.group.id!!) binding.buttonAddMember.setOnClickListener { findNavController().navigate(GroupMembersFragmentDirections @@ -63,15 +69,26 @@ class GroupMembersFragment : Fragment(), AdapterListener { builder.setPositiveButton("OK") { _, _ -> } builder.create().show() } + mainViewModel.organizers.observe(viewLifecycleOwner) { + organizer = it.any { org -> org.id == app.userId } + } } override fun onClickItem(item: User) { - val builder = AlertDialog.Builder(context) - builder.setTitle(item.username) - builder.setMessage("Phone: " + item.phone_number + "\n" + "Bank: " + item.bank) - builder.setPositiveButton("OK") { _, _ -> } - builder.create().show() + + /*if (organizer && item.id != app.userId) { + val builder = AlertDialog.Builder(context) + builder.setTitle(item.username) + builder.setPositiveButton("OK") { _, _ -> } + if (organizer) { + builder.setNegativeButton("Delete user") { _, _ -> + mainViewModel.deleteUser(args.group.id!!, item.id!!) + } + } + builder.create().show() + }*/ + } diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupsFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupsFragment.kt index 6bcd0ae..7b54785 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/GroupsFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/GroupsFragment.kt @@ -2,10 +2,11 @@ package ru.nsu.fit.modao.fragments import android.app.AlertDialog import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.PopupWindow +import android.widget.RadioButton import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -13,11 +14,17 @@ import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.hilt.android.AndroidEntryPoint +import ru.nsu.fit.modao.R import ru.nsu.fit.modao.adapter.AdapterListener import ru.nsu.fit.modao.adapter.GroupAdapter +import ru.nsu.fit.modao.databinding.FilterGroupsBinding import ru.nsu.fit.modao.databinding.FragmentGroupsBinding import ru.nsu.fit.modao.models.Group import ru.nsu.fit.modao.viewmodels.MainViewModel +import ru.tinkoff.decoro.MaskImpl +import ru.tinkoff.decoro.parser.UnderscoreDigitSlotsParser +import ru.tinkoff.decoro.watchers.MaskFormatWatcher + @AndroidEntryPoint class GroupsFragment : Fragment(), AdapterListener { private var _binding: FragmentGroupsBinding? = null @@ -25,6 +32,9 @@ class GroupsFragment : Fragment(), AdapterListener { private val adapter: GroupAdapter = GroupAdapter() private val mainViewModel: MainViewModel by viewModels() private val args by navArgs() + private lateinit var window: PopupWindow + private lateinit var bindingPopupWindow: FilterGroupsBinding + private var lastSelect: RadioButton? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -42,18 +52,17 @@ class GroupsFragment : Fragment(), AdapterListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setPopupWindow() if (args.notification){ mainViewModel.getGroupInfo(args.groupId) - Log.d("MyTag", "Notification") mainViewModel.groupInfo.observe(viewLifecycleOwner) { - Log.d("MyTag", "groups to group info") val action = GroupsFragmentDirections.actionGroupsFragmentToGroupInfoFragment(it) action.notification = true findNavController().navigate(action) } } - mainViewModel.getUserGroups() + mainViewModel.getUserGroups(0) adapter.attachListener(this) mainViewModel.userGroups.observe(viewLifecycleOwner){ adapter.setGroups(it) @@ -74,11 +83,49 @@ class GroupsFragment : Fragment(), AdapterListener { builder.setPositiveButton("OK") { _, _ -> } builder.create().show() } - - + binding.filterIcon.setOnClickListener { + window.showAsDropDown(binding.filterIcon, -250, 0) + } + val slots = UnderscoreDigitSlotsParser().parseSlots("___-___-___") + MaskFormatWatcher(MaskImpl.createTerminated(slots)).installOn(binding.editGroupUUID) } override fun onClickItem(item: Group) { findNavController().navigate(GroupsFragmentDirections.actionGroupsFragmentToGroupInfoFragment(item)) } + private fun setPopupWindow(){ + window = PopupWindow(context) + val view = LayoutInflater.from(context).inflate(R.layout.filter_groups, binding.root, false) + bindingPopupWindow = FilterGroupsBinding.bind(view) + window.contentView = bindingPopupWindow.root + window.isFocusable = true + bindingPopupWindow.active.isChecked = true + lastSelect = bindingPopupWindow.active + setButton() + } + private fun setButton() { + bindingPopupWindow.active.setOnClickListener { + if (switch(bindingPopupWindow.active)) { + mainViewModel.getUserGroups(0) + } + } + bindingPopupWindow.archive.setOnClickListener { + if (switch(bindingPopupWindow.archive)) { + mainViewModel.getUserGroups(1) + } + } + bindingPopupWindow.allGroups.setOnClickListener { + if (switch(bindingPopupWindow.allGroups)) { + mainViewModel.getUserGroups(2) + } + } + } + private fun switch(radioButton: RadioButton): Boolean { + if (lastSelect !== radioButton) { + lastSelect?.isChecked = false + lastSelect = radioButton + return true + } + return false + } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/ProfileFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/ProfileFragment.kt index fcdd5c5..a6eb27a 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/ProfileFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/ProfileFragment.kt @@ -1,9 +1,13 @@ package ru.nsu.fit.modao.fragments +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context.CLIPBOARD_SERVICE import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -12,6 +16,8 @@ import dagger.hilt.android.AndroidEntryPoint import ru.nsu.fit.modao.R import ru.nsu.fit.modao.databinding.FragmentProfileBinding import ru.nsu.fit.modao.utils.App +import ru.nsu.fit.modao.utils.Constants.Companion.FAIL +import ru.nsu.fit.modao.utils.Constants.Companion.SUCCESS import ru.nsu.fit.modao.viewmodels.MainViewModel import javax.inject.Inject @AndroidEntryPoint @@ -34,19 +40,32 @@ class ProfileFragment: Fragment() { mainViewModel.user.observe(viewLifecycleOwner){ binding.personName.text = it.username - binding.personBank.text = it.bank - binding.personPhone.text = it.phone_number binding.personUuid.text = it.uuid } mainViewModel.getUser() binding.logOutLayout.setOnClickListener { - activity?.findViewById(R.id.bottomMenu)?.visibility = View.GONE - val sharedPreferences = app.encryptedSharedPreferences - sharedPreferences.edit().clear().apply() - findNavController().navigate(ProfileFragmentDirections - .actionProfileFragmentToAuthorizationFragment()) + mainViewModel.exit() + } + binding.personUuid.setOnLongClickListener { + val clipboard = context?.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager? + val clip = ClipData.newPlainText("text", binding.personUuid.text.toString()) + clipboard?.setPrimaryClip(clip) + Toast.makeText(context, "Copied!", Toast.LENGTH_LONG).show() + true + } + mainViewModel.tipMessage.observe(viewLifecycleOwner) { + when (it) { + FAIL -> Toast.makeText(context, + "Check internet connection", Toast.LENGTH_LONG).show() + SUCCESS -> { + activity?.findViewById(R.id.bottomMenu)?.visibility = View.GONE + val sharedPreferences = app.encryptedSharedPreferences + sharedPreferences.edit().clear().apply() + findNavController().navigate(ProfileFragmentDirections + .actionProfileFragmentToAuthorizationFragment()) + } + } } - } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/RegistrationFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/RegistrationFragment.kt index 0df9d57..b4cec57 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/RegistrationFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/RegistrationFragment.kt @@ -39,7 +39,7 @@ class RegistrationFragment: Fragment() { binding.tipMessage.text = it } loginViewModel.userId.observe(viewLifecycleOwner) { - loginViewModel.login(login = login!!, password = password!!) + loginViewModel.login(login = login!!, password = password!!, app.deviceToken) } loginViewModel.token.observe(viewLifecycleOwner) { app.userId = it.id diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/SeeDetailsFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/SeeDetailsFragment.kt index a80ef4e..c49b600 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/SeeDetailsFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/SeeDetailsFragment.kt @@ -5,6 +5,7 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -14,7 +15,13 @@ import dagger.hilt.android.AndroidEntryPoint import ru.nsu.fit.modao.adapter.ParticipantsEventAdapter import ru.nsu.fit.modao.databinding.FragmentSeeDetailsBinding import ru.nsu.fit.modao.models.ParticipantEvent +import ru.nsu.fit.modao.utils.App +import ru.nsu.fit.modao.utils.Constants.Companion.FAIL +import ru.nsu.fit.modao.utils.Constants.Companion.SUCCESS import ru.nsu.fit.modao.viewmodels.MainViewModel +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import javax.inject.Inject @AndroidEntryPoint class SeeDetailsFragment : Fragment() { @@ -23,6 +30,8 @@ class SeeDetailsFragment : Fragment() { private val adapter = ParticipantsEventAdapter() private val args by navArgs() private val mainViewModel: MainViewModel by viewModels() + @Inject + lateinit var app: App override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -39,11 +48,22 @@ class SeeDetailsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + initOrganizer() setRecycler() initView() + initObserver() - + } + private fun initOrganizer() { + if (args.group?.isOrganizer == null) { + mainViewModel.getListOrganizers(args.group?.id!!) + mainViewModel.organizers.observe(viewLifecycleOwner) { + args.group?.isOrganizer = it.any { org -> org.id == app.userId } + processOrganizer(args.group?.isOrganizer!!) + } + } else { + processOrganizer(args.group?.isOrganizer!!) + } } private fun setRecycler(){ val list = args.expense.expenseDtoList?.map { @@ -58,6 +78,7 @@ class SeeDetailsFragment : Fragment() { binding.whoParticipatedRecycler.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) binding.whoParticipatedRecycler.adapter = adapter + binding.deleteButton } private fun initView(){ if (!args.isConfirmation){ @@ -68,22 +89,56 @@ class SeeDetailsFragment : Fragment() { else { initButton() } + + binding.deleteButton.setOnClickListener { + mainViewModel.deleteEvent(args.group?.id!!, args.expense.id!!, args.expense.name!!) + } + val time = LocalDateTime.parse(args.expense.time) + val pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") binding.theCost.text = args.expense.price.toString() binding.theSpender.text = args.expense.usernamePaying binding.theCreator.text = args.expense.usernameCreator + binding.theDate.text = time.format(pattern) + binding.expenseName.text = args.expense.name } private fun initButton() { binding.noButton2.setOnClickListener { val id = args.expense.id - mainViewModel.notConfirmEvent(args.group!!.id!!, id!!.toLong()) - findNavController().navigate(SeeDetailsFragmentDirections - .actionSeeDetailsFragmentToDataConfirmationFragment(args.group!!)) + mainViewModel.notConfirmEvent(args.group!!.id!!, id!!) } binding.yesButton2.setOnClickListener { val id = args.expense.id - mainViewModel.confirmEvent(args.group!!.id!!, id!!.toLong()) - findNavController().navigate(SeeDetailsFragmentDirections - .actionSeeDetailsFragmentToDataConfirmationFragment(args.group!!)) + mainViewModel.confirmEvent(args.group!!.id!!, id!!) + } + + } + private fun processOrganizer(isOrganizer: Boolean) { + if (isOrganizer && !args.isConfirmation && args.expense.status!! >= 0) { + binding.deleteButton.visibility = View.VISIBLE + } + if (!isOrganizer) { + binding.noButton2.visibility = View.GONE + binding.yesButton2.visibility = View.GONE + binding.textConfirm2.visibility = View.GONE + } else if (args.isConfirmation) { + binding.noButton2.visibility = View.VISIBLE + binding.yesButton2.visibility = View.VISIBLE + binding.textConfirm2.visibility = View.VISIBLE + } + } + + private fun initObserver() { + mainViewModel.tipMessage.observe(viewLifecycleOwner) { + when (it) { + SUCCESS -> { + Toast.makeText(context, "Deleted", Toast.LENGTH_LONG).show() + findNavController().navigate(SeeDetailsFragmentDirections + .actionSeeDetailsFragmentToGroupExpensesFragment(args.group!!)) + } + FAIL -> Toast.makeText(context, "Fail", Toast.LENGTH_LONG).show() + "OK" -> findNavController().navigate(SeeDetailsFragmentDirections + .actionSeeDetailsFragmentToDataConfirmationFragment(args.group!!)) + } } } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/SelectSecondParticipantFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/SelectSecondParticipantFragment.kt index a847a35..7ca3131 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/SelectSecondParticipantFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/SelectSecondParticipantFragment.kt @@ -47,10 +47,8 @@ class SelectSecondParticipantFragment : BottomSheetDialogFragment(), initRecycler() binding.buttonDone.setOnClickListener { //createExpenseViewModel.users.forEach { Log.d("MyTag", "${it.username!!} select ${it.selected} sponsor ${it.isSponsor}") } - createExpenseViewModel.participants.value = createExpenseViewModel.users.filter { - it.id == app.userId || - it.id == lastUser?.id - }.toTypedArray() + createExpenseViewModel.participants.value?.forEach { it.selected = it.id == app.userId || + it.id == lastUser?.id } //createExpenseViewModel.participants.value?.forEach { Log.d("MyTag", "${it.username!!} select ${it.selected} sponsor ${it.isSponsor}") } findNavController().popBackStack(R.id.createAnExpenseFragment, inclusive = false) } diff --git a/app/src/main/java/ru/nsu/fit/modao/fragments/StartFragment.kt b/app/src/main/java/ru/nsu/fit/modao/fragments/StartFragment.kt index ca9c313..630c158 100644 --- a/app/src/main/java/ru/nsu/fit/modao/fragments/StartFragment.kt +++ b/app/src/main/java/ru/nsu/fit/modao/fragments/StartFragment.kt @@ -12,6 +12,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import dagger.hilt.android.AndroidEntryPoint import ru.nsu.fit.modao.R import ru.nsu.fit.modao.databinding.FragmentStartBinding +import ru.nsu.fit.modao.models.Tokens import ru.nsu.fit.modao.notification.PushService.Companion.KEY_ACTION import ru.nsu.fit.modao.notification.PushService.Companion.KEY_GROUP_ID import ru.nsu.fit.modao.notification.PushService.Companion.TO_DATA_CONFIRMATION @@ -19,6 +20,7 @@ import ru.nsu.fit.modao.notification.PushService.Companion.TO_NOTIFICATION import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.utils.Constants.Companion.ACCESS_TOKEN import ru.nsu.fit.modao.utils.Constants.Companion.ID_USER +import ru.nsu.fit.modao.utils.Constants.Companion.REFRESH_TOKEN import ru.nsu.fit.modao.viewmodels.MainViewModel import javax.inject.Inject @@ -41,11 +43,20 @@ class StartFragment : Fragment() { super.onViewCreated(view, savedInstanceState) val sharedPreferences = app.encryptedSharedPreferences - val accessToken: String? = sharedPreferences.getString(ACCESS_TOKEN, null) - if (accessToken == null){ + val refreshToken = sharedPreferences.getString(REFRESH_TOKEN, null) + if (refreshToken == null){ findNavController().navigate(StartFragmentDirections.actionStartFragmentToAuthorizationFragment()) } else { - app.accessToken = accessToken + mainViewModel.getRefreshToken(Tokens(refreshToken = refreshToken)) + } + mainViewModel.tokens.observe(viewLifecycleOwner) { + app.accessToken = it.accessToken + if (it.refreshToken != null) { + app.refreshToken = it.refreshToken + } + val edit = sharedPreferences.edit() + edit.putString(ACCESS_TOKEN, it.accessToken) + .putString(REFRESH_TOKEN, it.refreshToken).apply() app.userId = sharedPreferences.getLong(ID_USER, -1) binding.progressBar.visibility = View.VISIBLE mainViewModel.getUser() diff --git a/app/src/main/java/ru/nsu/fit/modao/models/Expense.kt b/app/src/main/java/ru/nsu/fit/modao/models/Expense.kt index 732f7ab..69a7126 100644 --- a/app/src/main/java/ru/nsu/fit/modao/models/Expense.kt +++ b/app/src/main/java/ru/nsu/fit/modao/models/Expense.kt @@ -8,9 +8,13 @@ data class Expense ( val currency: Currency? = null, val id: Long? = null, val type: Int = 0, + val description: String = "Description", val name: String? = null, val groupId: Long? = null, val price: Float? = null, + val deleteId: Long? = null, + val time: String? = null, + val status: Int? = null, val userPayingId: Long? = null, val usernameCreator: String? = null, val usernamePaying: String? = null, diff --git a/app/src/main/java/ru/nsu/fit/modao/models/Group.kt b/app/src/main/java/ru/nsu/fit/modao/models/Group.kt index 594534f..884686d 100644 --- a/app/src/main/java/ru/nsu/fit/modao/models/Group.kt +++ b/app/src/main/java/ru/nsu/fit/modao/models/Group.kt @@ -3,14 +3,6 @@ package ru.nsu.fit.modao.models import android.os.Parcelable import kotlinx.parcelize.Parcelize -/* - private Long id; - private String groupName; - private String description; - private String uuid; - private Integer typeGroup; - private List userIdList; - */ @Parcelize data class Group( @@ -19,6 +11,36 @@ data class Group( val description: String? = null, val groupName: String? = null, val uuid: String? = null, - val userIdList: Array? = null - //val name: String? = null -) : Parcelable \ No newline at end of file + val userIdList: Array? = null, + val time: String? = null, + var isOrganizer: Boolean? = null +) : Parcelable { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Group + + if (id != other.id) return false + if (typeGroup != other.typeGroup) return false + if (description != other.description) return false + if (groupName != other.groupName) return false + if (uuid != other.uuid) return false + if (userIdList != null) { + if (other.userIdList == null) return false + if (!userIdList.contentEquals(other.userIdList)) return false + } else if (other.userIdList != null) return false + + return true + } + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + (typeGroup ?: 0) + result = 31 * result + (description?.hashCode() ?: 0) + result = 31 * result + (groupName?.hashCode() ?: 0) + result = 31 * result + (uuid?.hashCode() ?: 0) + result = 31 * result + (userIdList?.contentHashCode() ?: 0) + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/models/LoadItems.kt b/app/src/main/java/ru/nsu/fit/modao/models/LoadItems.kt index 791e1c0..bef8333 100644 --- a/app/src/main/java/ru/nsu/fit/modao/models/LoadItems.kt +++ b/app/src/main/java/ru/nsu/fit/modao/models/LoadItems.kt @@ -1,3 +1,3 @@ package ru.nsu.fit.modao.models -data class LoadItems(var isLoad: Boolean): ExpenseListItem() +data class LoadItems(var isLoad: Boolean, val page: Int): ExpenseListItem() diff --git a/app/src/main/java/ru/nsu/fit/modao/models/PageInfo.kt b/app/src/main/java/ru/nsu/fit/modao/models/PageInfo.kt new file mode 100644 index 0000000..72fd9b7 --- /dev/null +++ b/app/src/main/java/ru/nsu/fit/modao/models/PageInfo.kt @@ -0,0 +1,10 @@ +package ru.nsu.fit.modao.models + +data class PageInfo( + val sort: SortInfo? = null, + val offset: Int? = null, + val pageNumber: Int? = null, + val pageSize: Int? = null, + val paged: Boolean? = null, + val unpaged: Boolean? = null +) \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/models/PagingExpenses.kt b/app/src/main/java/ru/nsu/fit/modao/models/PagingExpenses.kt new file mode 100644 index 0000000..1239c6b --- /dev/null +++ b/app/src/main/java/ru/nsu/fit/modao/models/PagingExpenses.kt @@ -0,0 +1,33 @@ +package ru.nsu.fit.modao.models + +data class PagingExpenses( + val content: Array? = null, + val pageable: PageInfo? = null, + val totalPages: Int? = null, + val totalElements: Int? = null, + val last: Boolean? = null, + val number: Int? = null, + val sort: SortInfo? = null, + val size: Int? = null, + val numberOfElements: Int? = null, + val first: Boolean? = null, + val empty: Boolean? = null, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PagingExpenses + + if (content != null) { + if (other.content == null) return false + if (!content.contentEquals(other.content)) return false + } else if (other.content != null) return false + + return true + } + + override fun hashCode(): Int { + return content?.contentHashCode() ?: 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/models/Registration.kt b/app/src/main/java/ru/nsu/fit/modao/models/Registration.kt new file mode 100644 index 0000000..d9b8c81 --- /dev/null +++ b/app/src/main/java/ru/nsu/fit/modao/models/Registration.kt @@ -0,0 +1,6 @@ +package ru.nsu.fit.modao.models + +data class Registration( + val id: Long? = null, + val message: String? = null +) diff --git a/app/src/main/java/ru/nsu/fit/modao/models/SortInfo.kt b/app/src/main/java/ru/nsu/fit/modao/models/SortInfo.kt new file mode 100644 index 0000000..f0cdb4e --- /dev/null +++ b/app/src/main/java/ru/nsu/fit/modao/models/SortInfo.kt @@ -0,0 +1,7 @@ +package ru.nsu.fit.modao.models + +data class SortInfo( + val empty: Boolean? = null, + val sorted: Boolean? = null, + val unsorted: Boolean? = null, +) diff --git a/app/src/main/java/ru/nsu/fit/modao/models/Tokens.kt b/app/src/main/java/ru/nsu/fit/modao/models/Tokens.kt new file mode 100644 index 0000000..7a504a5 --- /dev/null +++ b/app/src/main/java/ru/nsu/fit/modao/models/Tokens.kt @@ -0,0 +1,6 @@ +package ru.nsu.fit.modao.models + +data class Tokens( + var refreshToken: String? = null, + var accessToken: String? = null +) \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/models/User.kt b/app/src/main/java/ru/nsu/fit/modao/models/User.kt index f32e744..f54395c 100644 --- a/app/src/main/java/ru/nsu/fit/modao/models/User.kt +++ b/app/src/main/java/ru/nsu/fit/modao/models/User.kt @@ -9,6 +9,8 @@ data class User( val idPicture: Int? = null, val id: Long? = null, - val uuid: String? = null - //val groupCustomPairIdNameList: ArrayList? = null + val uuid: String? = null, + val deviceToken: String? = null, + val packageName: String = "ru.nsu.fit.modao", + val appVersion: String = "1" ) \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/notification/PushService.kt b/app/src/main/java/ru/nsu/fit/modao/notification/PushService.kt index 52c3b74..75f40ae 100644 --- a/app/src/main/java/ru/nsu/fit/modao/notification/PushService.kt +++ b/app/src/main/java/ru/nsu/fit/modao/notification/PushService.kt @@ -7,6 +7,7 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build +import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import com.google.firebase.messaging.FirebaseMessagingService @@ -16,7 +17,8 @@ import ru.nsu.fit.modao.R import kotlin.random.Random private const val CHANNEL_ID = "my_channel" -class PushService: FirebaseMessagingService() { + +class PushService : FirebaseMessagingService() { override fun onNewToken(token: String) { super.onNewToken(token) } @@ -27,30 +29,38 @@ class PushService: FirebaseMessagingService() { val intent = Intent(this, MasterActivity::class.java) message.data.forEach { intent.putExtra(it.key, it.value) + Log.d("MyTag", "${it.key} - ${it.value}") } - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationID = Random.nextInt() - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel(notificationManager) } intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) } else { PendingIntent.getActivity(this, 0, intent, 0) } val notification = NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(message.data[KEY_TITLE]) - .setContentText(message.data[KEY_MESSAGE]) + .setContentTitle(message.notification?.title) + .setContentText(message.notification?.body) .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setSmallIcon(R.drawable.ic_launcher_foreground) + .setSmallIcon(R.drawable.ic_logo) .setAutoCancel(true) .setContentIntent(pendingIntent) .build() notificationManager.notify(notificationID, notification) } + @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(notificationManager: NotificationManager) { val channelName = "channelName" @@ -59,11 +69,8 @@ class PushService: FirebaseMessagingService() { } companion object { - const val INTENT_FILTER = "PUSH_EVENT" const val KEY_ACTION = "action" const val KEY_GROUP_ID = "groupID" - const val KEY_TITLE = "title" - const val KEY_MESSAGE = "message" const val TO_NOTIFICATION = "move to notification" const val TO_DATA_CONFIRMATION = "move to data confirmation" diff --git a/app/src/main/java/ru/nsu/fit/modao/repository/MainRepository.kt b/app/src/main/java/ru/nsu/fit/modao/repository/MainRepository.kt index 0590619..6868be2 100644 --- a/app/src/main/java/ru/nsu/fit/modao/repository/MainRepository.kt +++ b/app/src/main/java/ru/nsu/fit/modao/repository/MainRepository.kt @@ -5,14 +5,27 @@ import ru.nsu.fit.modao.models.* import ru.nsu.fit.modao.utils.App import ru.nsu.fit.modao.utils.Constants -class MainRepository (private val app: App) { +class MainRepository(private val app: App) { suspend fun login(user: User): Response { return app.api.login(user) } + suspend fun createUser(user: User): Response { return app.api.createUser(user) } + suspend fun getAccessToken(refreshToken: Tokens): Response { + return app.api.getAccessToken(refreshToken) + } + + suspend fun getRefreshToken(refreshToken: Tokens): Response { + return app.api.getRefreshToken(refreshToken) + } + + suspend fun exit(): Response { + return app.api.exit(Constants.AUTH + app.accessToken) + } + suspend fun getUser(): Response { return app.api.getUser(Constants.AUTH + app.accessToken) } @@ -22,14 +35,33 @@ class MainRepository (private val app: App) { } - suspend fun getGroupExpenses(id: Long, mode: Int, type: Int): Response> { - return app.api.getGroupExpenses(Constants.AUTH + app.accessToken, id, mode, type) + suspend fun getGroupExpenses( + id: Long, mode: Int, + type: Int, minTime: Long, maxTime: Long, + offset: Int, limit: Int + ): Response { + return app.api.getGroupExpenses( + Constants.AUTH + app.accessToken, + id, mode, type, minTime, maxTime, offset, limit + ) } suspend fun createExpense(expense: Expense): Response { return app.api.createExpense(Constants.AUTH + app.accessToken, expense) } + suspend fun deleteUser(groupId: Long, userId: Long): Response { + return app.api.deleteUser(Constants.AUTH + app.accessToken, groupId, userId) + } + + suspend fun deleteEvent(groupId: Long, eventId: Long, name: String): Response { + return app.api.deleteEvent(Constants.AUTH + app.accessToken, groupId, eventId, name) + } + + suspend fun makeGroupActive(groupId: Long): Response { + return app.api.makeGroupActive(Constants.AUTH + app.accessToken, groupId) + } + suspend fun addUserToGroup(groupId: Long, userId: Long): Response { return app.api.addUserToGroup(Constants.AUTH + app.accessToken, groupId, userId) } @@ -57,39 +89,59 @@ class MainRepository (private val app: App) { suspend fun getEventInfo(eventId: Long, groupId: Long): Response { return app.api.getEventInfo(Constants.AUTH + app.accessToken, eventId, groupId) } + suspend fun getGroupUnconfirmedExpenses(groupId: Long): Response> { return app.api.getGroupUnconfirmedExpenses(Constants.AUTH + app.accessToken, groupId) } - suspend fun getUserGroups(): Response>{ - return app.api.getUserGroups(Constants.AUTH + app.accessToken) + + suspend fun deleteGroup(groupId: Long): Response { + return app.api.deleteGroup(Constants.AUTH + app.accessToken, groupId) } - suspend fun getGroupInfo(groupId: Long): Response{ - return app.api.getGroupInfo(Constants.AUTH + app.accessToken ,groupId) + + suspend fun archiveGroup(groupId: Long): Response { + return app.api.archiveGroup(Constants.AUTH + app.accessToken, groupId) + } + + suspend fun getUserGroups(type: Int): Response> { + return app.api.getUserGroups(Constants.AUTH + app.accessToken, type) } + + suspend fun getGroupInfo(groupId: Long): Response { + return app.api.getGroupInfo(Constants.AUTH + app.accessToken, groupId) + } + suspend fun addFriend(userUuid: String): Response { return app.api.addFriend(Constants.AUTH + app.accessToken, userUuid) } + suspend fun getInvitationsFriend(): Response> { return app.api.getInvitationsFriend(Constants.AUTH + app.accessToken) } + suspend fun getInvitationsGroup(): Response> { return app.api.getInvitationsGroup(Constants.AUTH + app.accessToken) } - suspend fun acceptInvitationFriend(invitationId: Long):Response { + + suspend fun acceptInvitationFriend(invitationId: Long): Response { return app.api.acceptInvitationFriend(Constants.AUTH + app.accessToken, invitationId) } - suspend fun denyInvitationFriend(invitationId: Long):Response { + + suspend fun denyInvitationFriend(invitationId: Long): Response { return app.api.denyInvitationFriend(Constants.AUTH + app.accessToken, invitationId) } - suspend fun acceptInvitationGroup(invitationId: Long):Response { + + suspend fun acceptInvitationGroup(invitationId: Long): Response { return app.api.acceptInvitationGroup(Constants.AUTH + app.accessToken, invitationId) } - suspend fun denyInvitationGroup(invitationId: Long):Response { + + suspend fun denyInvitationGroup(invitationId: Long): Response { return app.api.denyInvitationGroup(Constants.AUTH + app.accessToken, invitationId) } + suspend fun getListFriends(): Response> { return app.api.getListFriends(Constants.AUTH + app.accessToken) } + suspend fun addToGroup(groupUUID: String): Response { return app.api.addToGroup(Constants.AUTH + app.accessToken, groupUUID) } diff --git a/app/src/main/java/ru/nsu/fit/modao/utils/App.kt b/app/src/main/java/ru/nsu/fit/modao/utils/App.kt index 391ea37..4bb0542 100644 --- a/app/src/main/java/ru/nsu/fit/modao/utils/App.kt +++ b/app/src/main/java/ru/nsu/fit/modao/utils/App.kt @@ -1,8 +1,10 @@ package ru.nsu.fit.modao.utils import android.app.Application +import android.content.SharedPreferences import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey +import com.google.firebase.messaging.FirebaseMessaging import dagger.hilt.android.HiltAndroidApp import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -28,35 +30,45 @@ class App: Application() { val api: ApiService by lazy { retrofit.create(ApiService::class.java) } -/* + override fun onCreate() { super.onCreate() FirebaseMessaging.getInstance().token.addOnCompleteListener { if (!it.isSuccessful) { return@addOnCompleteListener } - val token = it.result - Log.d("MyTag", token) + deviceToken = it.result } - val intentFilter = IntentFilter() - intentFilter.addAction(INTENT_FILTER) - val receiver = MyReceiver() - registerReceiver(receiver, intentFilter) - }*/ + } + + val encryptedSharedPreferences: SharedPreferences by lazy { + val pref = applicationContext.getSharedPreferences("init_pref", MODE_PRIVATE) + val isEncrypted = pref + .getBoolean("is encrypted", true) + if (isEncrypted) { + try { + val masterKey: MasterKey = MasterKey.Builder(applicationContext) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + EncryptedSharedPreferences.create( + applicationContext, + "secret_shared_prefs", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + } catch (e: Exception) { + pref.edit().putBoolean("is encrypted", false).apply() + applicationContext.getSharedPreferences("secret_shared_prefs", MODE_PRIVATE) + } + } else { + applicationContext.getSharedPreferences("secret_shared_prefs", MODE_PRIVATE) + } + - val encryptedSharedPreferences by lazy { - val masterKey: MasterKey = MasterKey.Builder(applicationContext) - .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) - .build() - EncryptedSharedPreferences.create( - applicationContext, - "secret_shared_prefs", - masterKey, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) } var userId: Long = -1 var accessToken: String? = null var refreshToken: String? = null + var deviceToken: String? = null } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/utils/Constatns.kt b/app/src/main/java/ru/nsu/fit/modao/utils/Constatns.kt index c354311..bad332a 100644 --- a/app/src/main/java/ru/nsu/fit/modao/utils/Constatns.kt +++ b/app/src/main/java/ru/nsu/fit/modao/utils/Constatns.kt @@ -2,17 +2,18 @@ package ru.nsu.fit.modao.utils class Constants { companion object { - //const val BASE_URL = "http://192.168.137.1:8080" + //const val BASE_URL = "http://192.168.137.1:8080" // laptop wifi //const val BASE_URL = "http://192.168.137.52:8080" //const val BASE_URL = "http://192.168.137.3:8080" //wifi sister - //const val BASE_URL = "http://139.59.143.34:5000" - const val BASE_URL = "http://192.168.0.101:8080" //my wifi + const val BASE_URL = "http://139.59.143.34:5000" //server + //const val BASE_URL = "http://192.168.0.101:8080" //my wifi //const val BASE_URL = "http://192.168.0.107:8080" - const val PAGE_SIZE = 10 + const val PAGE_SIZE = 20 const val AUTH = "Bearer " const val ACCESS_TOKEN = "Access token" const val REFRESH_TOKEN = "Refresh token" const val ID_USER = "User id" - const val BASE_URL_FCM = "https://fcm.googleapis.com" + const val FAIL = "Fail" + const val SUCCESS = "Success" } } \ No newline at end of file diff --git a/app/src/main/java/ru/nsu/fit/modao/viewmodels/CreateExpenseViewModel.kt b/app/src/main/java/ru/nsu/fit/modao/viewmodels/CreateExpenseViewModel.kt index 2ecfa6a..eaaf52d 100644 --- a/app/src/main/java/ru/nsu/fit/modao/viewmodels/CreateExpenseViewModel.kt +++ b/app/src/main/java/ru/nsu/fit/modao/viewmodels/CreateExpenseViewModel.kt @@ -63,10 +63,14 @@ class CreateExpenseViewModel @Inject constructor(private val repository: MainRep message.value = "Select participants" return } - - if (type == 1 || payFor) { + if (type == 1 && participantsEvent.size != 1) { + message.value = "Select participants" + return + } + if (type == 1 || payFor || !sponsor.selected) { sponsor.coefficient = 0f } + viewModelScope.launch(handler) { val expense = Expense( name = description, price = sum, diff --git a/app/src/main/java/ru/nsu/fit/modao/viewmodels/LoginViewModel.kt b/app/src/main/java/ru/nsu/fit/modao/viewmodels/LoginViewModel.kt index 609d78a..f314cbe 100644 --- a/app/src/main/java/ru/nsu/fit/modao/viewmodels/LoginViewModel.kt +++ b/app/src/main/java/ru/nsu/fit/modao/viewmodels/LoginViewModel.kt @@ -1,13 +1,14 @@ package ru.nsu.fit.modao.viewmodels -import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import ru.nsu.fit.modao.models.Authorization +import ru.nsu.fit.modao.models.Registration import ru.nsu.fit.modao.models.User import ru.nsu.fit.modao.repository.MainRepository import javax.inject.Inject @@ -19,12 +20,12 @@ class LoginViewModel @Inject constructor(private val repository: MainRepository) val userId = MutableLiveData() val message = MutableLiveData() private val handler = CoroutineExceptionHandler { _, _ -> message.value = "Server problems"} - fun login(login: String, password: String){ + fun login(login: String, password: String, deviceToken: String?){ if (login == "" || password == ""){ message.value = "Enter the data" return } - val user = User(login = login, password = password) + val user = User(login = login, password = password, deviceToken = deviceToken) viewModelScope.launch(handler) { val response = repository.login(user) if (response.isSuccessful){ @@ -45,7 +46,29 @@ class LoginViewModel @Inject constructor(private val repository: MainRepository) if (response.isSuccessful){ userId.value = response.body() } else { - message.value = "User with this login already exists" + val gson = Gson() + val errorResponse = gson.fromJson(response.errorBody()?.string()!!, + Registration::class.java) + when (errorResponse.message!!) { + "Пароль должен содержать " + + "как минимум одну букву в нижнем регистре" -> { + message.value = "The password must contain at least " + + "one letter in lowercase" + } + "Пароль должен содержать " + + "как минимум одну букву в верхнем регистре" -> { + message.value = "The password must contain at least " + + "one uppercase letter" + } + "Пароль слишком короткий\n" -> { + message.value = "The password must contain at least 6 characters" + } + "Логин должен быть от 3 до 20 символов\n" -> { + message.value = "Login must be from 3 to 20 characters " + } + else -> message.value = "User with this login already exists" + } + } } } diff --git a/app/src/main/java/ru/nsu/fit/modao/viewmodels/MainViewModel.kt b/app/src/main/java/ru/nsu/fit/modao/viewmodels/MainViewModel.kt index 1950735..a79fc8e 100644 --- a/app/src/main/java/ru/nsu/fit/modao/viewmodels/MainViewModel.kt +++ b/app/src/main/java/ru/nsu/fit/modao/viewmodels/MainViewModel.kt @@ -9,6 +9,8 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import ru.nsu.fit.modao.models.* import ru.nsu.fit.modao.repository.MainRepository +import ru.nsu.fit.modao.utils.Constants.Companion.FAIL +import ru.nsu.fit.modao.utils.Constants.Companion.SUCCESS import javax.inject.Inject @HiltViewModel @@ -27,16 +29,20 @@ class MainViewModel @Inject constructor(private val repository: MainRepository) val groupInfo = MutableLiveData() val invitationUser = MutableLiveData>() val listFriends = MutableLiveData>() - + val tokens = MutableLiveData() + val totalPages = MutableLiveData() private val handler = CoroutineExceptionHandler { _, throwable -> messageHandler.value = throwable.message - Log.e("MyTag", "${throwable.message}\n" + - "${throwable.cause}\n${throwable.localizedMessage}\n" + - "${throwable.suppressed}\n${throwable.suppressedExceptions}\n" + - "${throwable.stackTrace}") + Log.e( + "MyTag", "${throwable.message}\n" + + "${throwable.cause}\n${throwable.localizedMessage}\n" + + "${throwable.suppressed}\n${throwable.suppressedExceptions}\n" + + "${throwable.stackTrace}" + ) } + fun getUser() { viewModelScope.launch(handler) { val response = repository.getUser() @@ -44,94 +50,208 @@ class MainViewModel @Inject constructor(private val repository: MainRepository) user.value = response.body() } else { tipMessage.value = "error" - Log.e("MyError", response.message()) + Log.e("MyError", "Error ${response.errorBody()?.string()}") + } + } + } + + fun getAccessToken(refreshToken: Tokens) { + viewModelScope.launch(handler) { + val response = repository.getAccessToken(refreshToken) + if (response.isSuccessful) { + tokens.value?.accessToken = response.body()?.accessToken + } else { + tipMessage.value = "error" + Log.e("MyError", "Error ${response.errorBody()?.string()}") + } + } + } + + fun deleteGroup(groupId: Long) { + viewModelScope.launch(handler) { + val response = repository.deleteGroup(groupId) + if (response.isSuccessful) { + tipMessage.value = "Deleted" + } else { + Log.d("MyTag", "Error ${response.errorBody()?.string()}") + } + } + } + + fun archiveGroup(groupId: Long) { + viewModelScope.launch(handler) { + val response = repository.archiveGroup(groupId) + if (response.isSuccessful) { + tipMessage.value = "Archived" + } else { + Log.d("MyTag", "Error ${response.errorBody()?.string()}") + } + } + } + + fun getRefreshToken(refreshToken: Tokens) { + viewModelScope.launch(handler) { + val response = repository.getRefreshToken(refreshToken) + if (response.isSuccessful) { + tokens.value = Tokens( + refreshToken = response.body()?.refreshToken, + accessToken = response.body()?.accessToken + ) + } else { + tipMessage.value = "error" + Log.e("MyError", "Error ${response.errorBody()?.string()}") } } } - fun getUserGroups(){ + + fun exit() { viewModelScope.launch(handler) { - val response = repository.getUserGroups() - if (response.isSuccessful){ + val response = repository.exit() + if (response.isSuccessful) { + tipMessage.value = SUCCESS + } else { + tipMessage.value = FAIL + } + } + } + + fun getUserGroups(type: Int) { + viewModelScope.launch(handler) { + val response = repository.getUserGroups(type) + if (response.isSuccessful) { userGroups.value = response.body() } else { - Log.e("MyTag", response.message()) + Log.e("MyError", "Error ${response.errorBody()?.string()}") } } } - fun createGroup(group: Group){ + + fun createGroup(group: Group) { viewModelScope.launch(handler) { val response = repository.createGroup(group) - if (response.isSuccessful){ + if (response.isSuccessful) { groupId.value = response.body() + } else { + Log.e("MyError", "Error ${response.errorBody()?.string()}") + } + } + } + + fun getGroupExpenses( + id: Long, mode: Int, filter: Int, + minTime: Long, maxTime: Long, + offset: Int, limit: Int + ) { + viewModelScope.launch(handler) { + val response = repository.getGroupExpenses( + id, mode, filter, + minTime, maxTime, offset, limit + ) + if (response.isSuccessful) { + expenses.value = response.body()?.content!! + totalPages.value = response.body()?.totalPages!! + } else { + Log.e("MyError", "Error ${response.errorBody()?.string()}") } - else { - Log.e("MyError", response.message()) + } + } + + fun deleteEvent(groupId: Long, eventId: Long, name: String) { + viewModelScope.launch(handler) { + val response = repository.deleteEvent(groupId, eventId, name) + if (response.isSuccessful) { + tipMessage.value = SUCCESS + } else { + tipMessage.value = FAIL + Log.e("MyError", "Error ${response.errorBody()?.string()}") } } } - fun getGroupExpenses(id: Long, mode: Int, filter: Int){ + + fun makeGroupActive(groupId: Long) { viewModelScope.launch(handler) { - val response = repository.getGroupExpenses(id, mode, filter) - if (response.isSuccessful){ - expenses.value = response.body() + val response = repository.makeGroupActive(groupId) + if (response.isSuccessful) { + tipMessage.value = SUCCESS + } else { + tipMessage.value = FAIL + Log.e("MyError", "Error ${response.errorBody()?.string()}") } - else { - Log.e("MyError", response.message()) + } + } + + fun deleteUser(groupId: Long, userId: Long) { + viewModelScope.launch(handler) { + val response = repository.deleteUser(groupId, userId) + if (response.isSuccessful) { + tipMessage.value = SUCCESS + getUsersInGroup(groupId) + } else { + tipMessage.value = FAIL + Log.e("MyError", "Error ${response.errorBody()?.string()}") } } } - fun getUsersInGroup(groupId: Long){ + + fun getUsersInGroup(groupId: Long) { viewModelScope.launch(handler) { val response = repository.getUsersInGroup(groupId) - if (response.isSuccessful){ + if (response.isSuccessful) { usersInGroup.value = response.body() } else { Log.e("MyTag", response.message()) } } } - fun confirmEvent(groupId: Long, eventId: Long){ + + fun confirmEvent(groupId: Long, eventId: Long) { viewModelScope.launch(handler) { val response = repository.confirmEvent(groupId, eventId) if (!response.isSuccessful) { Log.d("MyTag", response.message()) } else { + tipMessage.value = "OK" getGroupUnconfirmedExpenses(groupId) } } } - fun notConfirmEvent(groupId: Long, eventId: Long){ + + fun notConfirmEvent(groupId: Long, eventId: Long) { viewModelScope.launch(handler) { val response = repository.notConfirmEvent(groupId, eventId) if (!response.isSuccessful) { Log.d("MyTag", response.message()) } else { + tipMessage.value = "OK" getGroupUnconfirmedExpenses(groupId) } } } - fun addUserToGroup(groupId: Long, userId: Long){ + + fun addUserToGroup(groupId: Long, userId: Long) { viewModelScope.launch(handler) { val response = repository.addUserToGroup(groupId, userId) - if (!response.isSuccessful){ + if (!response.isSuccessful) { Log.e("My errors", "Error add friend") } } } - fun getUserDebtInGroup(userId: Long, groupId: Long){ + + fun getUserDebtInGroup(userId: Long, groupId: Long) { viewModelScope.launch(handler) { val response = repository.getUserDebtInGroup(userId, groupId) - if (response.isSuccessful){ + if (response.isSuccessful) { userExpenses.value = response.body() } else { Log.e("MyTag", response.message()) } } } - fun getListOrganizers(groupId: Long){ + + fun getListOrganizers(groupId: Long) { viewModelScope.launch(handler) { val response = repository.getListOrganizers(groupId) - if (response.isSuccessful){ + if (response.isSuccessful) { organizers.value = response.body() } else { organizers.value = arrayOf() @@ -139,122 +259,133 @@ class MainViewModel @Inject constructor(private val repository: MainRepository) } } - fun getEventInfo(eventId: Long, groupId: Long){ + fun getEventInfo(eventId: Long, groupId: Long) { viewModelScope.launch(handler) { val response = repository.getEventInfo(eventId, groupId) - if (response.isSuccessful){ + if (response.isSuccessful) { infoEvent.value = response.body() } else { Log.e("MyTag", response.message()) } } } - fun getGroupUnconfirmedExpenses(groupId: Long){ + + fun getGroupUnconfirmedExpenses(groupId: Long) { viewModelScope.launch(handler) { val response = repository.getGroupUnconfirmedExpenses(groupId) - if (response.isSuccessful){ + if (response.isSuccessful) { unconfirmedExpenses.value = response.body() } else { Log.e("MyTag", response.message()) } } } - fun getGroupInfo(groupId: Long){ + + fun getGroupInfo(groupId: Long) { viewModelScope.launch(handler) { val response = repository.getGroupInfo(groupId) - if (response.isSuccessful){ + if (response.isSuccessful) { groupInfo.value = response.body() } else { Log.e("MyTag", response.message()) } } } - fun addFriend(userUuid: String){ + + fun addFriend(userUuid: String) { viewModelScope.launch(handler) { val response = repository.addFriend(userUuid) - if (response.isSuccessful){ + if (response.isSuccessful) { tipMessage.value = "Success" } else { tipMessage.value = "Incorrect data" } } } + fun getInvitationsFriend() { viewModelScope.launch(handler) { val response = repository.getInvitationsFriend() - if (response.isSuccessful){ + if (response.isSuccessful) { invitationUser.value = response.body() } else { Log.d("MyTag", "Fail") } } } + fun getInvitationsGroup() { viewModelScope.launch(handler) { val response = repository.getInvitationsGroup() - if (response.isSuccessful){ + if (response.isSuccessful) { invitationUser.value = response.body() } else { Log.d("MyTag", "Fail") } } } - fun acceptInvitationFriend(invitationId: Long){ + + fun acceptInvitationFriend(invitationId: Long) { viewModelScope.launch(handler) { val response = repository.acceptInvitationFriend(invitationId) - if (response.isSuccessful){ + if (response.isSuccessful) { tipMessage.value = "Success" } else { tipMessage.value = "Fail" } } } - fun denyInvitationFriend(invitationId: Long){ + + fun denyInvitationFriend(invitationId: Long) { viewModelScope.launch(handler) { val response = repository.denyInvitationFriend(invitationId) - if (response.isSuccessful){ + if (response.isSuccessful) { tipMessage.value = "Success" } else { tipMessage.value = "Fail" } } } - fun acceptInvitationGroup(invitationId: Long){ + + fun acceptInvitationGroup(invitationId: Long) { viewModelScope.launch(handler) { val response = repository.acceptInvitationGroup(invitationId) - if (response.isSuccessful){ + if (response.isSuccessful) { tipMessage.value = "Success" } else { tipMessage.value = "Fail" } } } - fun denyInvitationGroup(invitationId: Long){ + + fun denyInvitationGroup(invitationId: Long) { viewModelScope.launch(handler) { val response = repository.denyInvitationGroup(invitationId) - if (response.isSuccessful){ + if (response.isSuccessful) { tipMessage.value = "Success" } else { tipMessage.value = "Fail" } } } - fun getListFriends(){ + + fun getListFriends() { viewModelScope.launch(handler) { val response = repository.getListFriends() - if (response.isSuccessful){ + if (response.isSuccessful) { listFriends.value = response.body() } else { Log.d("MyTag", "Fail") } } } - fun addToGroup(groupUUID: String){ + + fun addToGroup(groupUUID: String) { viewModelScope.launch(handler) { val response = repository.addToGroup(groupUUID) - if (response.isSuccessful){ + if (response.isSuccessful) { tipMessage.value = "Success" - getUserGroups() + getUserGroups(0) } else { tipMessage.value = "Incorrect uuid" } diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/balloons.xml b/app/src/main/res/drawable/balloons.xml new file mode 100644 index 0000000..67986e0 --- /dev/null +++ b/app/src/main/res/drawable/balloons.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/baseline_calendar_month_24.xml b/app/src/main/res/drawable/baseline_calendar_month_24.xml new file mode 100644 index 0000000..edc07b4 --- /dev/null +++ b/app/src/main/res/drawable/baseline_calendar_month_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/baseline_cancel_24.xml b/app/src/main/res/drawable/baseline_cancel_24.xml new file mode 100644 index 0000000..d48a921 --- /dev/null +++ b/app/src/main/res/drawable/baseline_cancel_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_info_24.xml b/app/src/main/res/drawable/baseline_info_24.xml new file mode 100644 index 0000000..26aca9d --- /dev/null +++ b/app/src/main/res/drawable/baseline_info_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_people_outline_24.xml b/app/src/main/res/drawable/baseline_people_outline_24.xml new file mode 100644 index 0000000..b10ecee --- /dev/null +++ b/app/src/main/res/drawable/baseline_people_outline_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_warning_24.xml b/app/src/main/res/drawable/baseline_warning_24.xml new file mode 100644 index 0000000..b1726a3 --- /dev/null +++ b/app/src/main/res/drawable/baseline_warning_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/button_add_expense.xml b/app/src/main/res/drawable/button_add_expense.xml deleted file mode 100644 index 811cfb0..0000000 --- a/app/src/main/res/drawable/button_add_expense.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/button_add_friend.xml b/app/src/main/res/drawable/button_add_friend.xml deleted file mode 100644 index 42413eb..0000000 --- a/app/src/main/res/drawable/button_add_friend.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/button_add_group.xml b/app/src/main/res/drawable/button_add_group.xml deleted file mode 100644 index 5f70e15..0000000 --- a/app/src/main/res/drawable/button_add_group.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/button_add_member.xml b/app/src/main/res/drawable/button_add_member.xml deleted file mode 100644 index 2863e4d..0000000 --- a/app/src/main/res/drawable/button_add_member.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/button_all_in.xml b/app/src/main/res/drawable/button_all_in.xml deleted file mode 100644 index 40f265e..0000000 --- a/app/src/main/res/drawable/button_all_in.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/button_background.xml b/app/src/main/res/drawable/button_background.xml deleted file mode 100644 index 12707c0..0000000 --- a/app/src/main/res/drawable/button_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/button_data_conf.xml b/app/src/main/res/drawable/button_data_conf.xml deleted file mode 100644 index e7bd086..0000000 --- a/app/src/main/res/drawable/button_data_conf.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/button_expenses_group.xml b/app/src/main/res/drawable/button_expenses_group.xml deleted file mode 100644 index 8ce7f6c..0000000 --- a/app/src/main/res/drawable/button_expenses_group.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/button_finish.xml b/app/src/main/res/drawable/button_finish.xml deleted file mode 100644 index 0195fa8..0000000 --- a/app/src/main/res/drawable/button_finish.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/button_group.xml b/app/src/main/res/drawable/button_group.xml deleted file mode 100644 index bfe8ed9..0000000 --- a/app/src/main/res/drawable/button_group.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/button_info_group.xml b/app/src/main/res/drawable/button_info_group.xml deleted file mode 100644 index 972e482..0000000 --- a/app/src/main/res/drawable/button_info_group.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/button_member.xml b/app/src/main/res/drawable/button_member.xml deleted file mode 100644 index 2a312fc..0000000 --- a/app/src/main/res/drawable/button_member.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/button_more_options.xml b/app/src/main/res/drawable/button_more_options.xml deleted file mode 100644 index c47a182..0000000 --- a/app/src/main/res/drawable/button_more_options.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/button_next.xml b/app/src/main/res/drawable/button_next.xml deleted file mode 100644 index 14f30ff..0000000 --- a/app/src/main/res/drawable/button_next.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/button_select_participant.xml b/app/src/main/res/drawable/button_select_participant.xml deleted file mode 100644 index 8cc3f6b..0000000 --- a/app/src/main/res/drawable/button_select_participant.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/button_two.xml b/app/src/main/res/drawable/button_two.xml deleted file mode 100644 index 84d3766..0000000 --- a/app/src/main/res/drawable/button_two.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/continue_button.xml b/app/src/main/res/drawable/continue_button.xml deleted file mode 100644 index 1e670b3..0000000 --- a/app/src/main/res/drawable/continue_button.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/create_expense_head.xml b/app/src/main/res/drawable/create_expense_head.xml deleted file mode 100644 index 35a30ae..0000000 --- a/app/src/main/res/drawable/create_expense_head.xml +++ /dev/null @@ -1,25163 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/done_button.xml b/app/src/main/res/drawable/done_button.xml deleted file mode 100644 index 3e93a81..0000000 --- a/app/src/main/res/drawable/done_button.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/field_enter_cost.xml b/app/src/main/res/drawable/field_enter_cost.xml deleted file mode 100644 index 54537ed..0000000 --- a/app/src/main/res/drawable/field_enter_cost.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/field_enter_description_expense.xml b/app/src/main/res/drawable/field_enter_description_expense.xml deleted file mode 100644 index a302c0d..0000000 --- a/app/src/main/res/drawable/field_enter_description_expense.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/field_for_entering_cost.xml b/app/src/main/res/drawable/field_for_entering_cost.xml deleted file mode 100644 index 847d418..0000000 --- a/app/src/main/res/drawable/field_for_entering_cost.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/frame_find_new_friend.xml b/app/src/main/res/drawable/frame_find_new_friend.xml deleted file mode 100644 index 5ab9e1f..0000000 --- a/app/src/main/res/drawable/frame_find_new_friend.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/frame_new_expense.xml b/app/src/main/res/drawable/frame_new_expense.xml deleted file mode 100644 index 0425137..0000000 --- a/app/src/main/res/drawable/frame_new_expense.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/frame_new_friend.xml b/app/src/main/res/drawable/frame_new_friend.xml deleted file mode 100644 index 32973e7..0000000 --- a/app/src/main/res/drawable/frame_new_friend.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/frame_new_group.xml b/app/src/main/res/drawable/frame_new_group.xml deleted file mode 100644 index 46a733b..0000000 --- a/app/src/main/res/drawable/frame_new_group.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/gray_new_expense.xml b/app/src/main/res/drawable/gray_new_expense.xml deleted file mode 100644 index baf956c..0000000 --- a/app/src/main/res/drawable/gray_new_expense.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/gray_new_transfer.xml b/app/src/main/res/drawable/gray_new_transfer.xml deleted file mode 100644 index f9ff919..0000000 --- a/app/src/main/res/drawable/gray_new_transfer.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_add_friend.xml b/app/src/main/res/drawable/ic_add_friend.xml deleted file mode 100644 index a40cdf6..0000000 --- a/app/src/main/res/drawable/ic_add_friend.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_logo.xml b/app/src/main/res/drawable/ic_logo.xml new file mode 100644 index 0000000..5134c90 --- /dev/null +++ b/app/src/main/res/drawable/ic_logo.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_logo_square.xml b/app/src/main/res/drawable/ic_logo_square.xml new file mode 100644 index 0000000..acfa6d7 --- /dev/null +++ b/app/src/main/res/drawable/ic_logo_square.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_new_friend.xml b/app/src/main/res/drawable/ic_new_friend.xml new file mode 100644 index 0000000..340e068 --- /dev/null +++ b/app/src/main/res/drawable/ic_new_friend.xml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_new_group.xml b/app/src/main/res/drawable/ic_new_group.xml new file mode 100644 index 0000000..da89c31 --- /dev/null +++ b/app/src/main/res/drawable/ic_new_group.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_notification.xml b/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..45b906f --- /dev/null +++ b/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_wallet.xml b/app/src/main/res/drawable/ic_wallet.xml deleted file mode 100644 index 4c1cce1..0000000 --- a/app/src/main/res/drawable/ic_wallet.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_add_friend.xml b/app/src/main/res/drawable/icon_add_friend.xml new file mode 100644 index 0000000..20dad5c --- /dev/null +++ b/app/src/main/res/drawable/icon_add_friend.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/login_in.xml b/app/src/main/res/drawable/login_in.xml deleted file mode 100644 index 1eb13d1..0000000 --- a/app/src/main/res/drawable/login_in.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/money_transfer.xml b/app/src/main/res/drawable/money_transfer.xml new file mode 100644 index 0000000..d6126e6 --- /dev/null +++ b/app/src/main/res/drawable/money_transfer.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/new_expense.xml b/app/src/main/res/drawable/new_expense.xml deleted file mode 100644 index 863df4a..0000000 --- a/app/src/main/res/drawable/new_expense.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/new_group_icon.xml b/app/src/main/res/drawable/new_group_icon.xml new file mode 100644 index 0000000..fb815b4 --- /dev/null +++ b/app/src/main/res/drawable/new_group_icon.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/new_transfer.xml b/app/src/main/res/drawable/new_transfer.xml deleted file mode 100644 index c06a1e9..0000000 --- a/app/src/main/res/drawable/new_transfer.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/next.xml b/app/src/main/res/drawable/next.xml deleted file mode 100644 index 14f30ff..0000000 --- a/app/src/main/res/drawable/next.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/no_button.xml b/app/src/main/res/drawable/no_button.xml deleted file mode 100644 index da67296..0000000 --- a/app/src/main/res/drawable/no_button.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/notification_header.xml b/app/src/main/res/drawable/notification_header.xml deleted file mode 100644 index 350fe0e..0000000 --- a/app/src/main/res/drawable/notification_header.xml +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/sign_up.xml b/app/src/main/res/drawable/sign_up.xml deleted file mode 100644 index 5edc9b2..0000000 --- a/app/src/main/res/drawable/sign_up.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/white_background.xml b/app/src/main/res/drawable/white_background.xml index b76d4b4..c7c858e 100644 --- a/app/src/main/res/drawable/white_background.xml +++ b/app/src/main/res/drawable/white_background.xml @@ -1,6 +1,6 @@ - - - diff --git a/app/src/main/res/layout/activity_master.xml b/app/src/main/res/layout/activity_master.xml index fdf85b5..045791c 100644 --- a/app/src/main/res/layout/activity_master.xml +++ b/app/src/main/res/layout/activity_master.xml @@ -12,6 +12,7 @@ android:id="@+id/bottomMenu" android:layout_width="match_parent" android:layout_height="65dp" + style="@style/Widget.MaterialComponents.BottomNavigationView.PrimarySurface" android:background="@color/bottom_menu" android:visibility="gone" app:itemIconSize="30dp" diff --git a/app/src/main/res/layout/counter_event_item.xml b/app/src/main/res/layout/counter_event_item.xml new file mode 100644 index 0000000..0b4f0b4 --- /dev/null +++ b/app/src/main/res/layout/counter_event_item.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/deleted_event_item.xml b/app/src/main/res/layout/deleted_event_item.xml new file mode 100644 index 0000000..7713d7e --- /dev/null +++ b/app/src/main/res/layout/deleted_event_item.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/filter_groups.xml b/app/src/main/res/layout/filter_groups.xml new file mode 100644 index 0000000..f326ec9 --- /dev/null +++ b/app/src/main/res/layout/filter_groups.xml @@ -0,0 +1,38 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add_member.xml b/app/src/main/res/layout/fragment_add_member.xml index fcc19eb..9018f12 100644 --- a/app/src/main/res/layout/fragment_add_member.xml +++ b/app/src/main/res/layout/fragment_add_member.xml @@ -22,14 +22,17 @@ app:layout_constraintTop_toTopOf="parent" /> - - + app:layout_constraintTop_toBottomOf="@+id/personPassword" /> - + app:layout_constraintStart_toStartOf="parent" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_create_expense.xml b/app/src/main/res/layout/fragment_create_expense.xml index cf0b2e2..b6ac26d 100644 --- a/app/src/main/res/layout/fragment_create_expense.xml +++ b/app/src/main/res/layout/fragment_create_expense.xml @@ -6,19 +6,23 @@ android:layout_height="match_parent" tools:context=".fragments.CreateExpenseFragment"> - + app:layout_constraintStart_toStartOf="parent" /> - - - + app:layout_constraintStart_toStartOf="parent" /> - - - + + + + - - - - + app:strokeColor="@color/gray" + app:strokeWidth="2dp" /> - - - - - + + + + @@ -195,14 +247,4 @@ -