diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index a06aaa03c..062408c08 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import android.text.format.DateUtils; import android.view.View; import android.widget.ImageView; +import android.widget.ProgressBar; import android.widget.TextView; import androidx.emoji.widget.EmojiTextView; import butterknife.BindView; @@ -103,6 +104,12 @@ public class ConversationItem holder.dialogAvatar.setController(null); + if (conversation.isUpdating()) { + holder.progressBar.setVisibility(View.VISIBLE); + } else { + holder.progressBar.setVisibility(View.GONE); + } + if (adapter.hasFilter()) { FlexibleUtils.highlightText(holder.dialogName, conversation.getDisplayName(), String.valueOf(adapter.getFilter(String.class)), @@ -274,6 +281,8 @@ public class ConversationItem ImageView passwordProtectedRoomImageView; @BindView(R.id.favoriteConversationImageView) ImageView pinnedConversationImageView; + @BindView(R.id.actionProgressBar) + ProgressBar progressBar; ConversationItemViewHolder(View view, FlexibleAdapter adapter) { super(view, adapter); diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index eed08d541..8cca94fc2 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -323,13 +323,13 @@ public interface NcApi { @Url String url, @Query("search") String query, @Nullable @Query("limit") Integer limit); - // Url is: /api/{apiVersion}/room/{token}/pin + // Url is: /api/{apiVersion}/room/{token}/favorite @POST Observable addConversationToFavorites( @Header("Authorization") String authorization, @Url String url); - // Url is: /api/{apiVersion}/room/{token}/favorites + // Url is: /api/{apiVersion}/room/{token}/favorite @DELETE Observable removeConversationFromFavorites( @Header("Authorization") String authorization, diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java deleted file mode 100644 index e14619e65..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ /dev/null @@ -1,728 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.controllers; - -import android.app.SearchManager; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.text.InputType; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import androidx.annotation.NonNull; -import androidx.appcompat.widget.SearchView; -import androidx.core.graphics.drawable.RoundedBitmapDrawable; -import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; -import androidx.core.view.MenuItemCompat; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import autodagger.AutoInjector; -import butterknife.BindView; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat; -import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler; -import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler; -import com.facebook.common.executors.UiThreadImmediateExecutorService; -import com.facebook.common.references.CloseableReference; -import com.facebook.datasource.DataSource; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.imagepipeline.core.ImagePipeline; -import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; -import com.facebook.imagepipeline.image.CloseableImage; -import com.facebook.imagepipeline.request.ImageRequest; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.kennyc.bottomsheet.BottomSheet; -import com.nextcloud.talk.R; -import com.nextcloud.talk.activities.MagicCallActivity; -import com.nextcloud.talk.adapters.items.CallItem; -import com.nextcloud.talk.adapters.items.ConversationItem; -import com.nextcloud.talk.api.NcApi; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.controllers.bottomsheet.CallMenuController; -import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController; -import com.nextcloud.talk.events.BottomSheetLockEvent; -import com.nextcloud.talk.events.EventStatus; -import com.nextcloud.talk.events.MoreMenuClickEvent; -import com.nextcloud.talk.interfaces.ConversationMenuInterface; -import com.nextcloud.talk.jobs.DeleteConversationWorker; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.participants.Participant; -import com.nextcloud.talk.utils.ApiUtils; -import com.nextcloud.talk.utils.ConductorRemapping; -import com.nextcloud.talk.utils.DisplayUtils; -import com.nextcloud.talk.utils.KeyboardUtils; -import com.nextcloud.talk.utils.animations.SharedElementTransition; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.preferences.AppPreferences; -import com.uber.autodispose.AutoDispose; -import com.yarolegovich.lovelydialog.LovelySaveStateHandler; -import com.yarolegovich.lovelydialog.LovelyStandardDialog; -import eu.davidea.fastscroller.FastScroller; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.inject.Inject; -import org.apache.commons.lang3.builder.CompareToBuilder; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.parceler.Parcels; -import retrofit2.HttpException; - -@AutoInjector(NextcloudTalkApplication.class) -public class ConversationsListController extends BaseController - implements SearchView.OnQueryTextListener, - FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller - .OnScrollStateChangeListener, ConversationMenuInterface { - - public static final String TAG = "ConversationsListController"; - public static final int ID_DELETE_CONVERSATION_DIALOG = 0; - private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery"; - @Inject - UserUtils userUtils; - - @Inject - EventBus eventBus; - - @Inject - NcApi ncApi; - - @Inject - Context context; - - @Inject - AppPreferences appPreferences; - - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - - @BindView(R.id.swipeRefreshLayoutView) - SwipeRefreshLayout swipeRefreshLayout; - - @BindView(R.id.fast_scroller) - FastScroller fastScroller; - - @BindView(R.id.floatingActionButton) - FloatingActionButton floatingActionButton; - - private UserEntity currentUser; - private FlexibleAdapter adapter; - private List callItems = new ArrayList<>(); - - private BottomSheet bottomSheet; - private MenuItem searchItem; - private SearchView searchView; - private String searchQuery; - - private View view; - private boolean shouldUseLastMessageLayout; - - private String credentials; - - private boolean adapterWasNull = true; - - private boolean isRefreshing; - - private LovelySaveStateHandler saveStateHandler; - - private Bundle conversationMenuBundle = null; - - public ConversationsListController() { - super(); - setHasOptionsMenu(true); - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_conversations_rv, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication() - .getComponentApplication() - .inject(this); - - if (getActionBar() != null) { - getActionBar().show(); - } - - if (saveStateHandler == null) { - saveStateHandler = new LovelySaveStateHandler(); - } - - if (adapter == null) { - adapter = new FlexibleAdapter<>(callItems, getActivity(), true); - } else { - //progressBarView.setVisibility(View.GONE); - } - - adapter.addListener(this); - prepareViews(); - } - - private void loadUserAvatar(MenuItem menuItem) { - if (getActivity() != null) { - int avatarSize = (int) DisplayUtils.convertDpToPixel(menuItem.getIcon().getIntrinsicHeight(), - getActivity()); - ImageRequest imageRequest = DisplayUtils.getImageRequestForUrl( - ApiUtils.getUrlForAvatarWithNameAndPixels(currentUser.getBaseUrl(), - currentUser.getUserId(), avatarSize), null); - - ImagePipeline imagePipeline = Fresco.getImagePipeline(); - DataSource> dataSource = - imagePipeline.fetchDecodedImage(imageRequest, null); - dataSource.subscribe(new BaseBitmapDataSubscriber() { - @Override - protected void onNewResultImpl(Bitmap bitmap) { - if (bitmap != null && getResources() != null) { - RoundedBitmapDrawable roundedBitmapDrawable = - RoundedBitmapDrawableFactory.create(getResources(), bitmap); - roundedBitmapDrawable.setCircular(true); - roundedBitmapDrawable.setAntiAlias(true); - menuItem.setIcon(roundedBitmapDrawable); - } - } - - @Override - protected void onFailureImpl(DataSource> dataSource) { - menuItem.setIcon(R.drawable.ic_settings_white_24dp); - } - }, UiThreadImmediateExecutorService.getInstance()); - } - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - currentUser = userUtils.getCurrentUser(); - - if (currentUser != null) { - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); - shouldUseLastMessageLayout = currentUser.hasSpreedFeatureCapability("last-room-activity"); - fetchData(false); - } - } - - private void initSearchView() { - if (getActivity() != null) { - SearchManager searchManager = - (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); - if (searchItem != null) { - searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - searchView.setMaxWidth(Integer.MAX_VALUE); - searchView.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); - int imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && appPreferences.getIsKeyboardIncognito()) { - imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; - } - searchView.setImeOptions(imeOptions); - searchView.setQueryHint(getResources().getString(R.string.nc_search)); - if (searchManager != null) { - searchView.setSearchableInfo( - searchManager.getSearchableInfo(getActivity().getComponentName())); - } - searchView.setOnQueryTextListener(this); - } - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_settings: - ArrayList names = new ArrayList<>(); - names.add("userAvatar.transitionTag"); - getRouter().pushController((RouterTransaction.with(new SettingsController()) - .pushChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), - new VerticalChangeHandler())) - .popChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), - new VerticalChangeHandler())))); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_conversation_plus_filter, menu); - searchItem = menu.findItem(R.id.action_search); - initSearchView(); - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - searchItem.setVisible(callItems.size() > 0); - if (adapter.hasFilter()) { - searchItem.expandActionView(); - searchView.setQuery(adapter.getFilter(String.class), false); - } - - MenuItem menuItem = menu.findItem(R.id.action_settings); - loadUserAvatar(menuItem); - } - - private void fetchData(boolean fromBottomSheet) { - isRefreshing = true; - - callItems = new ArrayList<>(); - - ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(currentUser.getBaseUrl())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(roomsOverall -> { - - if (adapterWasNull) { - adapterWasNull = false; - //progressBarView.setVisibility(View.GONE); - } - - /*if (roomsOverall.getOcs().getData().size() > 0) { - if (emptyLayoutView.getVisibility() != View.GONE) { - emptyLayoutView.setVisibility(View.GONE); - } - - if (swipeRefreshLayout.getVisibility() != View.VISIBLE) { - swipeRefreshLayout.setVisibility(View.VISIBLE); - } - } else { - if (emptyLayoutView.getVisibility() != View.VISIBLE) { - emptyLayoutView.setVisibility(View.VISIBLE); - } - - if (swipeRefreshLayout.getVisibility() != View.GONE) { - swipeRefreshLayout.setVisibility(View.GONE); - } - }*/ - - Conversation conversation; - for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) { - conversation = roomsOverall.getOcs().getData().get(i); - if (shouldUseLastMessageLayout) { - if (getActivity() != null) { - ConversationItem conversationItem = new ConversationItem(conversation - , currentUser, getActivity()); - callItems.add(conversationItem); - } - } else { - CallItem callItem = new CallItem(conversation, currentUser); - callItems.add(callItem); - } - } - - if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { - Collections.sort(callItems, (o1, o2) -> { - Conversation conversation1 = ((ConversationItem) o1).getModel(); - Conversation conversation2 = ((ConversationItem) o2).getModel(); - return new CompareToBuilder() - .append(conversation2.isFavorite(), conversation1.isFavorite()) - .append(conversation2.getLastActivity(), conversation1.getLastActivity()) - .toComparison(); - }); - } else { - Collections.sort(callItems, (callItem, t1) -> - Long.compare(((CallItem) t1).getModel().getLastPing(), - ((CallItem) callItem).getModel().getLastPing())); - } - - adapter.updateDataSet(callItems, false); - - if (searchItem != null) { - searchItem.setVisible(callItems.size() > 0); - } - - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - }, throwable -> { - if (searchItem != null) { - searchItem.setVisible(false); - } - - if (throwable instanceof HttpException) { - HttpException exception = (HttpException) throwable; - switch (exception.code()) { - case 401: - /*if (getParentController() != null && - getParentController().getRouter() != null) { - getParentController().getRouter().pushController((RouterTransaction.with - (new WebViewLoginController(currentUser.getBaseUrl(), - true)) - .pushChangeHandler(new VerticalChangeHandler()) - .popChangeHandler(new VerticalChangeHandler()))); - }*/ - break; - default: - break; - } - } - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - }, () -> { - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setRefreshing(false); - } - - if (fromBottomSheet) { - new Handler().postDelayed(() -> { - bottomSheet.setCancelable(true); - if (bottomSheet.isShowing()) { - bottomSheet.cancel(); - } - }, 2500); - } - - isRefreshing = false; - }); - } - - private void prepareViews() { - SmoothScrollLinearLayoutManager layoutManager = - new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - - recyclerView.setAdapter(adapter); - - swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false)); - swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); - - //emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen()); - floatingActionButton.setOnClickListener(v -> { - showNewConversationsScreen(); - }); - - fastScroller.addOnScrollStateChangeListener(this); - adapter.setFastScroller(fastScroller); - - fastScroller.setBubbleTextCreator(position -> { - String displayName; - if (shouldUseLastMessageLayout) { - displayName = ((ConversationItem) adapter.getItem(position)).getModel().getDisplayName(); - } else { - displayName = ((CallItem) adapter.getItem(position)).getModel().getDisplayName(); - } - - if (displayName.length() > 8) { - displayName = displayName.substring(0, 4) + "..."; - } - return displayName; - }); - } - - private void showNewConversationsScreen() { - Bundle bundle = new Bundle(); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_NEW_CONVERSATION(), true); - getRouter().pushController((RouterTransaction.with(new ContactsController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler()))); - } - - @Override - public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - saveStateHandler.saveInstanceState(outState); - - if (searchView != null && !TextUtils.isEmpty(searchView.getQuery())) { - outState.putString(KEY_SEARCH_QUERY, searchView.getQuery().toString()); - } - - super.onSaveViewState(view, outState); - } - - @Override - public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { - super.onRestoreViewState(view, savedViewState); - searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, ""); - if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { - //Dialog won't be restarted automatically, so we need to call this method. - //Each dialog knows how to restore its viewState - showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); - } - } - - @Override - public boolean onQueryTextChange(String newText) { - if (adapter.hasNewFilter(newText) || !TextUtils.isEmpty(searchQuery)) { - - if (!TextUtils.isEmpty(searchQuery)) { - adapter.setFilter(searchQuery); - searchQuery = ""; - adapter.filterItems(); - } else { - adapter.setFilter(newText); - adapter.filterItems(300); - } - } - - if (swipeRefreshLayout != null) { - swipeRefreshLayout.setEnabled(!adapter.hasFilter()); - } - - return true; - } - - @Override - public boolean onQueryTextSubmit(String query) { - return onQueryTextChange(query); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(BottomSheetLockEvent bottomSheetLockEvent) { - if (bottomSheet != null) { - if (!bottomSheetLockEvent.isCancelable()) { - bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); - } else { - if (bottomSheetLockEvent.getDelay() != 0 && bottomSheetLockEvent.isShouldRefreshData()) { - fetchData(true); - } else { - bottomSheet.setCancelable(bottomSheetLockEvent.isCancelable()); - if (bottomSheet.isShowing() && bottomSheetLockEvent.isCancel()) { - bottomSheet.cancel(); - } - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMessageEvent(MoreMenuClickEvent moreMenuClickEvent) { - Bundle bundle = new Bundle(); - Conversation conversation = moreMenuClickEvent.getConversation(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_MENU_TYPE(), - Parcels.wrap(CallMenuController.MenuType.REGULAR)); - - prepareAndShowBottomSheetWithBundle(bundle, true); - } - - private void prepareAndShowBottomSheetWithBundle(Bundle bundle, - boolean shouldShowCallMenuController) { - if (view == null) { - view = getActivity().getLayoutInflater().inflate(R.layout.bottom_sheet, null, false); - } - - if (shouldShowCallMenuController) { - getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new CallMenuController(bundle, this)) - .popChangeHandler(new VerticalChangeHandler()) - .pushChangeHandler(new VerticalChangeHandler())); - } else { - getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new EntryMenuController(bundle)) - .popChangeHandler(new VerticalChangeHandler()) - .pushChangeHandler(new VerticalChangeHandler())); - } - - if (bottomSheet == null) { - bottomSheet = new BottomSheet.Builder(getActivity()).setView(view).create(); - } - - bottomSheet.setOnShowListener( - dialog -> new KeyboardUtils(getActivity(), bottomSheet.getLayout(), true)); - bottomSheet.setOnDismissListener( - dialog -> getActionBar().setDisplayHomeAsUpEnabled(getRouter().getBackstackSize() > 1)); - bottomSheet.show(); - } - - @Override - public String getTitle() { - return getResources().getString(R.string.nc_app_name); - } - - @Override - public void onFastScrollerStateChange(boolean scrolling) { - swipeRefreshLayout.setEnabled(!scrolling); - } - - @Override - public boolean onItemClick(View view, int position) { - Object clickedItem = adapter.getItem(position); - if (clickedItem != null && getActivity() != null) { - Conversation conversation; - if (shouldUseLastMessageLayout) { - conversation = ((ConversationItem) clickedItem).getModel(); - } else { - conversation = ((CallItem) clickedItem).getModel(); - } - - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); - - if (conversation.hasPassword && (conversation.getParticipantType() - .equals(Participant.ParticipantType.GUEST) || - conversation.getParticipantType() - .equals(Participant.ParticipantType.USER_FOLLOWING_LINK))) { - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); - prepareAndShowBottomSheetWithBundle(bundle, false); - } else { - currentUser = userUtils.getCurrentUser(); - - if (currentUser.hasSpreedFeatureCapability("chat-v2")) { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), - Parcels.wrap(conversation)); - ConductorRemapping.INSTANCE.remapChatController(getRouter(), currentUser.getId(), - conversation.getToken(), bundle, false); - } else { - overridePushHandler(new NoOpControllerChangeHandler()); - overridePopHandler(new NoOpControllerChangeHandler()); - Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); - callIntent.putExtras(bundle); - startActivity(callIntent); - } - } - } - - return true; - } - - @Override - public void onItemLongClick(int position) { - if (currentUser.hasSpreedFeatureCapability("last-room-activity")) { - Object clickedItem = adapter.getItem(position); - if (clickedItem != null) { - Conversation conversation; - if (shouldUseLastMessageLayout) { - conversation = ((ConversationItem) clickedItem).getModel(); - } else { - conversation = ((CallItem) clickedItem).getModel(); - } - - MoreMenuClickEvent moreMenuClickEvent = new MoreMenuClickEvent(conversation); - onMessageEvent(moreMenuClickEvent); - } - } - } - - @Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(EventStatus eventStatus) { - if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) { - switch (eventStatus.getEventType()) { - case CONVERSATION_UPDATE: - if (eventStatus.isAllGood() && !isRefreshing) { - fetchData(false); - } - break; - default: - break; - } - } - } - - private void showDeleteConversationDialog(Bundle savedInstanceState) { - if (getActivity() != null - && conversationMenuBundle != null - && currentUser != null - && conversationMenuBundle.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID()) - == currentUser.getId()) { - - Conversation conversation = - Parcels.unwrap(conversationMenuBundle.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - - if (conversation != null) { - new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL) - .setTopColorRes(R.color.nc_darkRed) - .setIcon(DisplayUtils.getTintedDrawable(context.getResources(), - R.drawable.ic_delete_black_24dp, R.color.bg_default)) - .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed)) - .setTitle(R.string.nc_delete_call) - .setMessage(conversation.getDeleteWarningMessage()) - .setPositiveButton(R.string.nc_delete, new View.OnClickListener() { - @Override - public void onClick(View v) { - Data.Builder data = new Data.Builder(); - data.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), - conversationMenuBundle.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID())); - data.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - conversationMenuBundle = null; - deleteConversation(data.build()); - } - }) - .setNegativeButton(R.string.nc_cancel, new View.OnClickListener() { - @Override - public void onClick(View v) { - conversationMenuBundle = null; - } - }) - .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler) - .setSavedInstanceState(savedInstanceState) - .show(); - } - } - } - - private void deleteConversation(Data data) { - OneTimeWorkRequest deleteConversationWorker = - new OneTimeWorkRequest.Builder(DeleteConversationWorker.class).setInputData(data).build(); - WorkManager.getInstance().enqueue(deleteConversationWorker); - } - - private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { - switch (dialogId) { - case ID_DELETE_CONVERSATION_DIALOG: - showDeleteConversationDialog(savedInstanceState); - break; - default: - break; - } - } - - @Override - public void openLovelyDialogWithIdAndBundle(int dialogId, Bundle bundle) { - conversationMenuBundle = bundle; - switch (dialogId) { - case ID_DELETE_CONVERSATION_DIALOG: - showLovelyDialog(dialogId, null); - break; - default: - break; - } - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java deleted file mode 100644 index 9d4ed53e4..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.controllers.bottomsheet; - -import android.content.ComponentName; -import android.content.Intent; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import autodagger.AutoInjector; -import butterknife.BindView; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.kennyc.bottomsheet.adapters.AppAdapter; -import com.nextcloud.talk.R; -import com.nextcloud.talk.adapters.items.AppItem; -import com.nextcloud.talk.adapters.items.MenuItem; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.ConversationsListController; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.events.BottomSheetLockEvent; -import com.nextcloud.talk.interfaces.ConversationMenuInterface; -import com.nextcloud.talk.jobs.LeaveConversationWorker; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.utils.DisplayUtils; -import com.nextcloud.talk.utils.ShareUtils; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; -import java.util.ArrayList; -import java.util.List; -import javax.inject.Inject; -import org.greenrobot.eventbus.EventBus; -import org.parceler.Parcel; -import org.parceler.Parcels; - -@AutoInjector(NextcloudTalkApplication.class) -public class CallMenuController extends BaseController - implements FlexibleAdapter.OnItemClickListener { - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - - @Inject - EventBus eventBus; - - @Inject - UserUtils userUtils; - - private Conversation conversation; - private List menuItems; - private FlexibleAdapter adapter; - private MenuType menuType; - private Intent shareIntent; - - private UserEntity currentUser; - private ConversationMenuInterface conversationMenuInterface; - - public CallMenuController(Bundle args) { - super(); - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_MENU_TYPE())) { - this.menuType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_MENU_TYPE())); - } - } - - public CallMenuController(Bundle args, - @Nullable ConversationMenuInterface conversationMenuInterface) { - super(); - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_MENU_TYPE())) { - this.menuType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_MENU_TYPE())); - } - this.conversationMenuInterface = conversationMenuInterface; - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_call_menu, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication() - .getComponentApplication() - .inject(this); - prepareViews(); - } - - private void prepareViews() { - LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - - prepareMenu(); - if (adapter == null) { - adapter = new FlexibleAdapter<>(menuItems, getActivity(), false); - } - - adapter.addListener(this); - recyclerView.setAdapter(adapter); - } - - private void prepareIntent() { - shareIntent = new Intent(Intent.ACTION_SEND); - shareIntent.setType("text/plain"); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, - String.format(getResources().getString(R.string.nc_share_subject), - getResources().getString(R.string.nc_app_name))); - } - - private void prepareMenu() { - menuItems = new ArrayList<>(); - - if (menuType.equals(MenuType.REGULAR)) { - if (!TextUtils.isEmpty(conversation.getDisplayName())) { - menuItems.add(new MenuItem(conversation.getDisplayName(), 0, null)); - } else if (!TextUtils.isEmpty(conversation.getName())) { - menuItems.add(new MenuItem(conversation.getName(), 0, null)); - } else { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_configure_room), 0, null)); - } - - currentUser = userUtils.getCurrentUser(); - - if (conversation.isFavorite()) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_remove_from_favorites), 97, - DisplayUtils.getTintedDrawable(getResources(), R.drawable.ic_star_border_black_24dp, - R.color.grey_600))); - } else if (currentUser.hasSpreedFeatureCapability("favorites")) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_add_to_favorites) - , 98, DisplayUtils.getTintedDrawable(getResources(), R.drawable.ic_star_black_24dp, - R.color.grey_600))); - } - - if (conversation.isNameEditable(currentUser)) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_rename), 2, - getResources().getDrawable(R.drawable - .ic_pencil_grey600_24dp))); - } - - if (conversation.canModerate(currentUser)) { - if (!conversation.isPublic()) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_public), 3, - getResources().getDrawable(R.drawable - .ic_link_grey600_24px))); - } else { - if (conversation.isHasPassword()) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_change_password), 4, - getResources().getDrawable(R.drawable - .ic_lock_grey600_24px))); - menuItems.add(new MenuItem(getResources().getString(R.string.nc_clear_password), 5, - getResources().getDrawable(R.drawable - .ic_lock_open_grey600_24dp))); - } else { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_set_password), 6, - getResources().getDrawable(R.drawable - .ic_lock_plus_grey600_24dp))); - } - } - - menuItems.add(new MenuItem(getResources().getString(R.string.nc_delete_call), 9, - getResources().getDrawable(R.drawable - .ic_delete_grey600_24dp))); - } - - if (conversation.isPublic()) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_share_link), 7, - getResources().getDrawable(R.drawable - .ic_link_grey600_24px))); - if (conversation.canModerate(currentUser)) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_private), 8, - getResources().getDrawable(R.drawable - .ic_group_grey600_24px))); - } - } - - if (conversation.canLeave(currentUser)) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_leave), 1, - DisplayUtils.getTintedDrawable(getResources(), - R.drawable.ic_exit_to_app_black_24dp, R.color.grey_600) - )); - } - } else if (menuType.equals(MenuType.SHARE)) { - prepareIntent(); - List appInfoList = - ShareUtils.getShareApps(getActivity(), shareIntent, null, - null); - menuItems.add(new AppItem(getResources().getString(R.string.nc_share_link_via), "", "", - getResources().getDrawable(R.drawable.ic_link_grey600_24px))); - if (appInfoList != null) { - for (AppAdapter.AppInfo appInfo : appInfoList) { - menuItems.add( - new AppItem(appInfo.title, appInfo.packageName, appInfo.name, appInfo.drawable)); - } - } - } else { - menuItems.add( - new MenuItem(getResources().getString(R.string.nc_start_conversation), 0, null)); - menuItems.add(new MenuItem(getResources().getString(R.string.nc_new_conversation), 1, - getResources().getDrawable(R.drawable.ic_add_grey600_24px))); - menuItems.add(new MenuItem(getResources().getString(R.string.nc_join_via_link), 2, - getResources().getDrawable(R.drawable.ic_link_grey600_24px))); - } - } - - @Override - public boolean onItemClick(View view, int position) { - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - - if (menuType.equals(MenuType.REGULAR)) { - MenuItem menuItem = (MenuItem) adapter.getItem(position); - if (menuItem != null) { - - int tag = menuItem.getTag(); - if (tag == 5) { - conversation.setPassword(""); - } - - if (tag > 0) { - if (tag == 1 || tag == 9) { - if (tag == 1) { - Data data; - if ((data = getWorkerData()) != null) { - OneTimeWorkRequest leaveConversationWorker = - new OneTimeWorkRequest.Builder(LeaveConversationWorker.class).setInputData(data) - .build(); - WorkManager.getInstance().enqueue(leaveConversationWorker); - } - } else { - Bundle deleteConversationBundle; - if ((deleteConversationBundle = getDeleteConversationBundle()) != null) { - conversationMenuInterface.openLovelyDialogWithIdAndBundle( - ConversationsListController.ID_DELETE_CONVERSATION_DIALOG, - deleteConversationBundle); - } - } - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); - } else { - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), tag); - if (tag != 2 && tag != 4 && tag != 6 && tag != 7) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - getRouter().pushController( - RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (tag != 7) { - getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_MENU_TYPE(), - Parcels.wrap(MenuType.SHARE)); - getRouter().pushController( - RouterTransaction.with(new CallMenuController(bundle, null)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } - } - } - } - } else if (menuType.equals(MenuType.SHARE) && position != 0) { - AppItem appItem = (AppItem) adapter.getItem(position); - if (appItem != null && getActivity() != null) { - if (!conversation.hasPassword) { - shareIntent.putExtra(Intent.EXTRA_TEXT, ShareUtils.getStringForIntent(getActivity(), null, - userUtils, conversation)); - Intent intent = new Intent(shareIntent); - intent.setComponent(new ComponentName(appItem.getPackageName(), appItem.getName())); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); - getActivity().startActivity(intent); - } else { - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 7); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SHARE_INTENT(), - Parcels.wrap(shareIntent)); - bundle.putString(BundleKeys.INSTANCE.getKEY_APP_ITEM_PACKAGE_NAME(), - appItem.getPackageName()); - bundle.putString(BundleKeys.INSTANCE.getKEY_APP_ITEM_NAME(), appItem.getName()); - getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } - } - } - - return true; - } - - private Data getWorkerData() { - if (!TextUtils.isEmpty(conversation.getToken())) { - Data.Builder data = new Data.Builder(); - data.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - data.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), currentUser.getId()); - return data.build(); - } - - return null; - } - - private Bundle getDeleteConversationBundle() { - if (!TextUtils.isEmpty(conversation.getToken())) { - Bundle bundle = new Bundle(); - bundle.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), currentUser.getId()); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - return bundle; - } - - return null; - } - - @Parcel - public enum MenuType { - REGULAR, SHARE - } -} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java index 2e8ee91d3..098553aae 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java @@ -22,6 +22,7 @@ package com.nextcloud.talk.models.json.conversations; import android.content.res.Resources; import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonIgnore; import com.bluelinelabs.logansquare.annotation.JsonObject; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -90,6 +91,7 @@ public class Conversation { public Long lobbyTimer; @JsonField(name = "lastReadMessage") public int lastReadMessage; + public boolean updating; public boolean isPublic() { return (ConversationType.ROOM_PUBLIC_CALL.equals(type)); diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/NextcloudTalkRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/NextcloudTalkRepositoryImpl.kt index e1487949b..d4b7fedac 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/NextcloudTalkRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/NextcloudTalkRepositoryImpl.kt @@ -22,16 +22,50 @@ package com.nextcloud.talk.newarch.data.repository import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.newarch.data.source.remote.ApiService import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository +import com.nextcloud.talk.newarch.utils.getCredentials import com.nextcloud.talk.utils.ApiUtils class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : NextcloudTalkRepository { + override suspend fun deleteConversationForUser( + user: UserEntity, + conversation: Conversation + ): GenericOverall { + return apiService.deleteConversation(user.getCredentials(), ApiUtils.getRoom(user.baseUrl, conversation.token)) + } + + override suspend fun leaveConversationForUser( + user: UserEntity, + conversation: Conversation + ): GenericOverall { + return apiService.leaveConversation(user.getCredentials(), ApiUtils.getUrlForRemoveSelfFromRoom(user + .baseUrl, conversation.token)) + } + + override suspend fun setFavoriteValueForConversation( + user: UserEntity, + conversation: Conversation, + favorite: Boolean + ): GenericOverall { + if (favorite) { + return apiService.addConversationToFavorites( + user.getCredentials(), + ApiUtils.getUrlForConversationFavorites(user.baseUrl, conversation.token) + ) + } else { + return apiService.removeConversationFromFavorites( + user.getCredentials(), + ApiUtils.getUrlForConversationFavorites(user.baseUrl, conversation.token) + ) + } + } + override suspend fun getConversationsForUser(user: UserEntity): List { return apiService.getConversations( - ApiUtils.getCredentials(user.username, user.token), + user.getCredentials(), ApiUtils.getUrlForGetRooms(user.baseUrl) - ) - .ocs.data + ).ocs.data } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt index 06efefc49..5807d9fad 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt @@ -21,8 +21,12 @@ package com.nextcloud.talk.newarch.data.source.remote import com.nextcloud.talk.models.json.conversations.RoomsOverall +import com.nextcloud.talk.models.json.generic.GenericOverall +import io.reactivex.Observable +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Header +import retrofit2.http.POST import retrofit2.http.Url interface ApiService { @@ -36,4 +40,29 @@ interface ApiService { "Authorization" ) authorization: String, @Url url: String ): RoomsOverall + + @POST + suspend fun addConversationToFavorites( + @Header("Authorization") authorization: String, + @Url url: String + ): GenericOverall + + @DELETE + suspend fun removeConversationFromFavorites( + @Header("Authorization") authorization: String, + @Url url: String + ): GenericOverall + + @DELETE + suspend fun leaveConversation( + @Header("Authorization") authorization: String, + @Url url: String + ): GenericOverall + + @DELETE + suspend fun deleteConversation( + @Header("Authorization") authorization: String, + @Url url: String + ): GenericOverall + } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt index 7324a4765..1555da125 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt @@ -79,7 +79,7 @@ val NetworkModule = module { single { createSslSocketFactory(get(), get()) } single { createCache(androidApplication() as NextcloudTalkApplication) } single { createOkHttpClient(androidContext(), get(), get(), get(), get(), get(), get(), get()) } - + factory { createApiErrorHandler() } single { createNextcloudTalkRepository(get()) } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/NextcloudTalkRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/NextcloudTalkRepository.kt index c3e777117..7987d1b56 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/NextcloudTalkRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/NextcloudTalkRepository.kt @@ -22,7 +22,15 @@ package com.nextcloud.talk.newarch.domain.repository import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.generic.GenericOverall interface NextcloudTalkRepository { suspend fun getConversationsForUser(user: UserEntity): List + suspend fun setFavoriteValueForConversation( + user: UserEntity, + conversation: Conversation, + favorite: Boolean + ) : GenericOverall + suspend fun deleteConversationForUser(user: UserEntity, conversation: Conversation) : GenericOverall + suspend fun leaveConversationForUser(userEntity: UserEntity, conversation: Conversation) : GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/DeleteConversationUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/DeleteConversationUseCase.kt new file mode 100644 index 000000000..4527b782e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/DeleteConversationUseCase.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.newarch.domain.usecases + +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler +import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository +import com.nextcloud.talk.newarch.domain.usecases.base.UseCase +import org.koin.core.parameter.DefinitionParameters + +class DeleteConversationUseCase constructor( + private val nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler? +) : UseCase(apiErrorHandler) { + + override suspend fun run(params: Any?): GenericOverall { + val definitionParameters = params as DefinitionParameters + return nextcloudTalkRepository.deleteConversationForUser(user, definitionParameters.get(0)) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/LeaveConversationUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/LeaveConversationUseCase.kt new file mode 100644 index 000000000..1267d8b87 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/LeaveConversationUseCase.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.newarch.domain.usecases + +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler +import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository +import com.nextcloud.talk.newarch.domain.usecases.base.UseCase +import org.koin.core.parameter.DefinitionParameters + +class LeaveConversationUseCase constructor( + private val nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler? +) : UseCase(apiErrorHandler) { + + override suspend fun run(params: Any?): GenericOverall { + val definitionParameters = params as DefinitionParameters + return nextcloudTalkRepository.leaveConversationForUser(user, definitionParameters.get(0)) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt new file mode 100644 index 000000000..ea9929043 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/SetConversationFavoriteValueUseCase.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.newarch.domain.usecases + +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler +import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository +import com.nextcloud.talk.newarch.domain.usecases.base.UseCase +import org.koin.core.parameter.DefinitionParameters + +class SetConversationFavoriteValueUseCase constructor( + private val nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler? +) : UseCase(apiErrorHandler) { + + override suspend fun run(params: Any?): GenericOverall { + val definitionParameters = params as DefinitionParameters + return nextcloudTalkRepository.setFavoriteValueForConversation(user, definitionParameters.get(0), definitionParameters.get(1)) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/base/BaseUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/base/BaseUseCase.kt index 6c987062c..514540ca9 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/base/BaseUseCase.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/base/BaseUseCase.kt @@ -21,6 +21,7 @@ package com.nextcloud.talk.newarch.domain.usecases.base import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt index d4085206a..66b35e6a6 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationListViewModelFactory.kt @@ -23,16 +23,24 @@ package com.nextcloud.talk.newarch.features.conversationsList import android.app.Application import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase +import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase import com.nextcloud.talk.utils.database.user.UserUtils class ConversationListViewModelFactory constructor( private val application: Application, private val conversationsUseCase: GetConversationsUseCase, + private val setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, + private val leaveConversationUseCase: LeaveConversationUseCase, + private val deleteConversationUseCase: DeleteConversationUseCase, private val userUtils: UserUtils ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return ConversationsListViewModel(application, conversationsUseCase, userUtils) as T + return ConversationsListViewModel(application, conversationsUseCase, + setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase, + userUtils) as T } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt index bbd398c1d..ae7461cdf 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt @@ -38,23 +38,25 @@ import androidx.core.view.MenuItemCompat import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView.ViewHolder import butterknife.OnClick +import com.afollestad.materialdialogs.LayoutMode.WRAP_CONTENT +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler import com.nextcloud.talk.R +import com.nextcloud.talk.R.drawable import com.nextcloud.talk.adapters.items.ConversationItem import com.nextcloud.talk.controllers.ContactsController import com.nextcloud.talk.controllers.SettingsController -import com.nextcloud.talk.controllers.bottomsheet.CallMenuController.MenuType -import com.nextcloud.talk.controllers.bottomsheet.CallMenuController.MenuType.REGULAR -import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView -import com.nextcloud.talk.newarch.mvvm.ViewState.FAILED -import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED -import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED_EMPTY -import com.nextcloud.talk.newarch.mvvm.ViewState.LOADING import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView +import com.nextcloud.talk.newarch.utils.ViewState.FAILED +import com.nextcloud.talk.newarch.utils.ViewState.LOADED +import com.nextcloud.talk.newarch.utils.ViewState.LOADED_EMPTY +import com.nextcloud.talk.newarch.utils.ViewState.LOADING import com.nextcloud.talk.utils.ConductorRemapping import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.animations.SharedElementTransition @@ -213,12 +215,17 @@ class ConversationsListView : BaseView(), OnQueryTextListener, if (value.equals(FAILED)) { view?.stateWithMessageView?.errorStateTextView?.text = messageData - if (messageData.equals(context.resources.getString(R.string.nc_no_connection_error))) { + if (messageData.equals( + context.resources.getString(R.string.nc_no_connection_error) + ) + ) { view?.stateWithMessageView?.errorStateImageView?.setImageResource( - R.drawable.ic_cloud_off_white_24dp) + R.drawable.ic_cloud_off_white_24dp + ) } else { view?.stateWithMessageView?.errorStateImageView?.setImageResource( - R.drawable.ic_announcement_white_24dp) + R.drawable.ic_announcement_white_24dp + ) } view?.floatingActionButton?.visibility = View.GONE } else { @@ -236,7 +243,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, } }) - conversationsListData.observe(this@ConversationsListView, Observer { + conversationsLiveListData.observe(this@ConversationsListView, Observer { val newConversations = mutableListOf() for (conversation in it) { newConversations.add(ConversationItem(conversation, viewModel.currentUser, activity)) @@ -318,12 +325,64 @@ class ConversationsListView : BaseView(), OnQueryTextListener, override fun onItemLongClick(position: Int) { val clickedItem = recyclerViewAdapter.getItem(position) - if (clickedItem != null) { - val conversation = (clickedItem as ConversationItem).model - val bundle = Bundle() - bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap(conversation)) - bundle.putParcelable(BundleKeys.KEY_MENU_TYPE, Parcels.wrap(REGULAR)) - //prepareAndShowBottomSheetWithBundle(bundle, true) + clickedItem?.let { + val conversationItem = it as ConversationItem + val conversation = conversationItem.model + + activity?.let { activity -> + MaterialDialog(activity, BottomSheet(WRAP_CONTENT)).show { + cornerRadius(res = R.dimen.corner_radius) + title(text = conversation.displayName) + listItemsWithImage( + viewModel.getConversationMenuItemsForConversation(conversation) + ) { dialog, + index, item -> + + when (item.iconRes) { + drawable.ic_star_border_black_24dp -> { + viewModel.changeFavoriteValueForConversation(conversation, false) + + } + drawable.ic_star_black_24dp -> { + viewModel.changeFavoriteValueForConversation(conversation, true) + } + drawable.ic_link_grey600_24px -> { + startActivity(viewModel.getShareIntentForConversation(conversation)) + } + drawable.ic_exit_to_app_black_24dp -> { + MaterialDialog(activity).show { + title(R.string.nc_leave) + message(R.string.nc_leave_message) + positiveButton(R.string.nc_simple_leave) { dialog -> + viewModel.leaveConversation(conversation) + } + negativeButton(R.string.nc_cancel) + icon(drawable.ic_exit_to_app_black_24dp) + } + } + drawable.ic_delete_grey600_24dp -> { + MaterialDialog(activity).show { + title(R.string.nc_delete) + message(text = conversation.deleteWarningMessage) + positiveButton(R.string.nc_delete_call) { dialog -> + viewModel.deleteConversation(conversation) + } + negativeButton(R.string.nc_cancel) + icon( + drawable = DisplayUtils.getTintedDrawable( + resources, drawable + .ic_delete_grey600_24dp, R.color.nc_darkRed + ) + ) + } + } + else -> { + + } + } + } + } + } } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt index 405ed0eab..1787a9a83 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt @@ -21,6 +21,7 @@ package com.nextcloud.talk.newarch.features.conversationsList import android.app.Application +import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Drawable import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory @@ -33,29 +34,42 @@ import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber import com.facebook.imagepipeline.image.CloseableImage import com.nextcloud.talk.R +import com.nextcloud.talk.R.drawable +import com.nextcloud.talk.R.string +import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.newarch.conversationsList.mvp.BaseViewModel import com.nextcloud.talk.newarch.data.model.ErrorModel +import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase +import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse -import com.nextcloud.talk.newarch.mvvm.ViewState -import com.nextcloud.talk.newarch.mvvm.ViewState.FAILED -import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED -import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED_EMPTY -import com.nextcloud.talk.newarch.mvvm.ViewState.LOADING +import com.nextcloud.talk.newarch.utils.ViewState +import com.nextcloud.talk.newarch.utils.ViewState.FAILED +import com.nextcloud.talk.newarch.utils.ViewState.LOADED +import com.nextcloud.talk.newarch.utils.ViewState.LOADED_EMPTY +import com.nextcloud.talk.newarch.utils.ViewState.LOADING import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.ShareUtils import com.nextcloud.talk.utils.database.user.UserUtils import org.apache.commons.lang3.builder.CompareToBuilder +import org.koin.core.parameter.parametersOf class ConversationsListViewModel constructor( application: Application, - private val conversationsUseCase: GetConversationsUseCase, + private val getConversationsUseCase: GetConversationsUseCase, + private val setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, + private val leaveConversationUseCase: LeaveConversationUseCase, + private val deleteConversationUseCase: DeleteConversationUseCase, private val userUtils: UserUtils ) : BaseViewModel(application) { - val conversationsListData = MutableLiveData>() + private var conversations: MutableList = mutableListOf() + val conversationsLiveListData = MutableLiveData>() val viewState = MutableLiveData(LOADING) var messageData: String? = null val searchQuery = MutableLiveData() @@ -63,23 +77,91 @@ class ConversationsListViewModel constructor( var currentUserAvatar: MutableLiveData = MutableLiveData() get() { if (field.value == null) { - field.value = context.resources.getDrawable(R.drawable.ic_settings_white_24dp) + field.value = context.resources.getDrawable(drawable.ic_settings_white_24dp) } return field } + fun leaveConversation(conversation: Conversation) { + leaveConversationUseCase.user = currentUser + + setConversationUpdateStatus(conversation, true) + + leaveConversationUseCase.invoke(viewModelScope, parametersOf(conversation), + object : UseCaseResponse { + override fun onSuccess(result: GenericOverall) { + // TODO: Use binary search to find the right room + conversations.find { it.roomId == conversation.roomId }?.let { + conversations.remove(it) + conversationsLiveListData.value = conversations + } + } + + override fun onError(errorModel: ErrorModel?) { + setConversationUpdateStatus(conversation, false) + } + }) + } + + fun deleteConversation(conversation: Conversation) { + deleteConversationUseCase.user = currentUser + + setConversationUpdateStatus(conversation, true) + + deleteConversationUseCase.invoke(viewModelScope, parametersOf(conversation), + object : UseCaseResponse { + override fun onSuccess(result: GenericOverall) { + // TODO: Use binary search to find the right room + conversations.find { it.roomId == conversation.roomId }?.let { + conversations.remove(it) + conversationsLiveListData.value = conversations + } + } + + override fun onError(errorModel: ErrorModel?) { + setConversationUpdateStatus(conversation, false) + } + }) + + } + + fun changeFavoriteValueForConversation( + conversation: Conversation, + favorite: Boolean + ) { + setConversationFavoriteValueUseCase.user = currentUser + + setConversationUpdateStatus(conversation, true) + + setConversationFavoriteValueUseCase.invoke(viewModelScope, parametersOf(conversation, favorite), + object : UseCaseResponse { + override fun onSuccess(result: GenericOverall) { + // TODO: Use binary search to find the right room + conversations.find { it.roomId == conversation.roomId }?.apply { + updating = false + isFavorite = favorite + conversationsLiveListData.value = conversations + } + } + + override fun onError(errorModel: ErrorModel?) { + setConversationUpdateStatus(conversation, false) + } + }) + } + fun loadConversations() { currentUser = userUtils.currentUser - if (viewState.value?.equals(FAILED)!! || !conversationsUseCase.isUserInitialized() || - conversationsUseCase.user != currentUser + if (viewState.value?.equals(FAILED)!! || !getConversationsUseCase.isUserInitialized() || + getConversationsUseCase.user != currentUser ) { - conversationsUseCase.user = currentUser + getConversationsUseCase.user = currentUser viewState.value = LOADING } - conversationsUseCase.invoke( + getConversationsUseCase.invoke( viewModelScope, null, object : UseCaseResponse> { override fun onSuccess(result: List) { val newConversations = result.toMutableList() @@ -91,7 +173,8 @@ class ConversationsListViewModel constructor( .toComparison() }) - conversationsListData.value = newConversations + conversations = newConversations + conversationsLiveListData.value = conversations viewState.value = if (newConversations.isNotEmpty()) LOADED else LOADED_EMPTY messageData = "" } @@ -133,4 +216,88 @@ class ConversationsListViewModel constructor( }, UiThreadImmediateExecutorService.getInstance()) } + + fun getShareIntentForConversation(conversation: Conversation): Intent { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra( + Intent.EXTRA_SUBJECT, + String.format( + context.getString(R.string.nc_share_subject), + context.getString(R.string.nc_app_name) + ) + ) + + // TODO, make sure we ask for password if needed + putExtra( + Intent.EXTRA_TEXT, ShareUtils.getStringForIntent( + context, null, + userUtils, conversation + ) + ) + + type = "text/plain" + } + + val intent = Intent.createChooser(sendIntent, context.getString(string.nc_share_link)) + // TODO filter our own app once we're there + return intent + } + + fun getConversationMenuItemsForConversation(conversation: Conversation): MutableList { + val items = mutableListOf() + + if (conversation.isFavorite) { + items.add( + BasicListItemWithImage( + drawable.ic_star_border_black_24dp, + context.getString(string.nc_remove_from_favorites) + ) + ) + } else { + items.add( + BasicListItemWithImage( + drawable.ic_star_black_24dp, + context.getString(string.nc_add_to_favorites) + ) + ) + } + + if (conversation.isPublic) { + items.add( + (BasicListItemWithImage( + drawable + .ic_link_grey600_24px, context.getString(string.nc_share_link) + )) + ) + } + + if (conversation.canLeave(currentUser)) { + items.add( + BasicListItemWithImage( + drawable.ic_exit_to_app_black_24dp, context.getString + (string.nc_leave) + ) + ) + } + + if (conversation.canModerate(currentUser)) { + items.add( + BasicListItemWithImage( + drawable.ic_delete_grey600_24dp, context.getString( + string.nc_delete_call + ) + ) + ) + } + + return items + } + + private fun setConversationUpdateStatus(conversation: Conversation, value: Boolean) { + conversations.find { it.roomId == conversation.roomId }?.apply { + updating = value + conversationsLiveListData.value = conversations + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt index 07696291c..bf5c2e96b 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/di/module/ConversationsListModule.kt @@ -22,18 +22,35 @@ package com.nextcloud.talk.newarch.features.conversationsList.di.module import android.app.Application import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler -import com.nextcloud.talk.newarch.di.module.createApiErrorHandler import com.nextcloud.talk.newarch.domain.repository.NextcloudTalkRepository +import com.nextcloud.talk.newarch.domain.usecases.DeleteConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase +import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase import com.nextcloud.talk.newarch.features.conversationsList.ConversationListViewModelFactory import com.nextcloud.talk.utils.database.user.UserUtils import org.koin.android.ext.koin.androidApplication import org.koin.dsl.module val ConversationsListModule = module { - single { createGetConversationsUseCase(get(), createApiErrorHandler()) } + single { createGetConversationsUseCase(get(), get()) } + single { createSetConversationFavoriteValueUseCase(get(), get()) } + single { createLeaveConversationUseCase(get(), get()) } + single { createDeleteConversationuseCase(get(), get()) } //viewModel { ConversationsListViewModel(get(), get()) } - factory { createConversationListViewModelFactory(androidApplication(), get(), get()) } + factory { + createConversationListViewModelFactory( + androidApplication(), get(), get(), get(), get + (), get() + ) + } +} + +fun createSetConversationFavoriteValueUseCase( + nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler +): SetConversationFavoriteValueUseCase { + return SetConversationFavoriteValueUseCase(nextcloudTalkRepository, apiErrorHandler) } fun createGetConversationsUseCase( @@ -43,11 +60,32 @@ fun createGetConversationsUseCase( return GetConversationsUseCase(nextcloudTalkRepository, apiErrorHandler) } +fun createLeaveConversationUseCase( + nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler +): LeaveConversationUseCase { + return LeaveConversationUseCase(nextcloudTalkRepository, apiErrorHandler) +} + +fun createDeleteConversationuseCase( + nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler +): DeleteConversationUseCase { + return DeleteConversationUseCase(nextcloudTalkRepository, apiErrorHandler) +} + fun createConversationListViewModelFactory( application: Application, - conversationsUseCase: + getConversationsUseCase: GetConversationsUseCase, + setConversationFavoriteValueUseCase: SetConversationFavoriteValueUseCase, + leaveConversationUseCase: LeaveConversationUseCase, + deleteConversationUseCase: DeleteConversationUseCase, userUtils: UserUtils ): ConversationListViewModelFactory { - return ConversationListViewModelFactory(application, conversationsUseCase, userUtils) + return ConversationListViewModelFactory( + application, getConversationsUseCase, + setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase, + userUtils + ) } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/Extensions.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/Extensions.kt new file mode 100644 index 000000000..eeca61937 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/Extensions.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.newarch.utils + +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.utils.ApiUtils + +fun UserEntity.getCredentials() = ApiUtils.getCredentials(username, token) \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/mvvm/ViewState.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/ViewState.kt similarity index 95% rename from app/src/main/java/com/nextcloud/talk/newarch/mvvm/ViewState.kt rename to app/src/main/java/com/nextcloud/talk/newarch/utils/ViewState.kt index a45011ce5..ab613ff2a 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/mvvm/ViewState.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/ViewState.kt @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.newarch.mvvm +package com.nextcloud.talk.newarch.utils enum class ViewState { LOADING, diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index 3cb107627..9dbc4626c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -246,9 +246,10 @@ public class DisplayUtils { public static Drawable getTintedDrawable(Resources res, @DrawableRes int drawableResId, @ColorRes int colorResId) { Drawable drawable = res.getDrawable(drawableResId); + Drawable mutableDrawable = drawable.mutate(); int color = res.getColor(colorResId); - drawable.setTint(color); - return drawable; + mutableDrawable.setTint(color); + return mutableDrawable; } public static Drawable getDrawableForMentionChipSpan(Context context, String id, diff --git a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java index 68975581d..e2c551a18 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.java @@ -42,8 +42,7 @@ import java.util.Set; public class ShareUtils { public static String getStringForIntent(Context context, @Nullable String password, - UserUtils userUtils, Conversation - conversation) { + UserUtils userUtils, Conversation conversation) { UserEntity userEntity = userUtils.getCurrentUser(); String shareString = ""; diff --git a/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml b/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml index ac4fbac98..ce2fcb870 100644 --- a/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml +++ b/app/src/main/res/drawable/ic_exit_to_app_black_24dp.xml @@ -21,5 +21,6 @@ - + diff --git a/app/src/main/res/drawable/ic_star_black_24dp.xml b/app/src/main/res/drawable/ic_star_black_24dp.xml index dd6e8eb46..0dd5dd623 100644 --- a/app/src/main/res/drawable/ic_star_black_24dp.xml +++ b/app/src/main/res/drawable/ic_star_black_24dp.xml @@ -21,5 +21,6 @@ - + diff --git a/app/src/main/res/drawable/ic_star_border_black_24dp.xml b/app/src/main/res/drawable/ic_star_border_black_24dp.xml index 9f531c8ee..17976f296 100644 --- a/app/src/main/res/drawable/ic_star_border_black_24dp.xml +++ b/app/src/main/res/drawable/ic_star_border_black_24dp.xml @@ -21,5 +21,5 @@ - + diff --git a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml index fece111b8..dbfb79d8f 100644 --- a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml +++ b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml @@ -27,7 +27,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="@dimen/rv_item_view_height" - android:layout_margin="@dimen/double_margin_between_elements"> + android:layout_margin="@dimen/double_margin_between_elements" + android:animateLayoutChanges="true"> + + + Start a conversation Configure conversation Leave conversation + Please confirm your intent to leave this conversation. + Leave Rename conversation Set a password Change password @@ -157,10 +159,12 @@ Make conversation private Delete conversation Delete - Please confirm your intent to remove the conversation. - If you delete the conversation, it will also be + Please confirm your intent to delete this + conversation. + If you delete the conversation it will also be deleted for %1$s. - If you delete the conversation, it will also be deleted for all other participants. + If you delete this conversation it will also be + deleted for all other participants. New conversation Join with a link