mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-20 20:19:42 +01:00
Rework conversation menu & bug fixes and improvements
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
978be1afe1
commit
d66134c663
@ -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);
|
||||
|
@ -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<GenericOverall> addConversationToFavorites(
|
||||
@Header("Authorization") String authorization,
|
||||
@Url String url);
|
||||
|
||||
// Url is: /api/{apiVersion}/room/{token}/favorites
|
||||
// Url is: /api/{apiVersion}/room/{token}/favorite
|
||||
@DELETE
|
||||
Observable<GenericOverall> removeConversationFromFavorites(
|
||||
@Header("Authorization") String authorization,
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<AbstractFlexibleItem> adapter;
|
||||
private List<AbstractFlexibleItem> 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<CloseableReference<CloseableImage>> 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<CloseableReference<CloseableImage>> 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<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,347 +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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<AbstractFlexibleItem> menuItems;
|
||||
private FlexibleAdapter<AbstractFlexibleItem> 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<AppAdapter.AppInfo> 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
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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<Conversation> {
|
||||
return apiService.getConversations(
|
||||
ApiUtils.getCredentials(user.username, user.token),
|
||||
user.getCredentials(),
|
||||
ApiUtils.getUrlForGetRooms(user.baseUrl)
|
||||
)
|
||||
.ocs.data
|
||||
).ocs.data
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
}
|
@ -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()) }
|
||||
}
|
||||
|
||||
|
@ -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<Conversation>
|
||||
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
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<GenericOverall, Any?>(apiErrorHandler) {
|
||||
|
||||
override suspend fun run(params: Any?): GenericOverall {
|
||||
val definitionParameters = params as DefinitionParameters
|
||||
return nextcloudTalkRepository.deleteConversationForUser(user, definitionParameters.get(0))
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<GenericOverall, Any?>(apiErrorHandler) {
|
||||
|
||||
override suspend fun run(params: Any?): GenericOverall {
|
||||
val definitionParameters = params as DefinitionParameters
|
||||
return nextcloudTalkRepository.leaveConversationForUser(user, definitionParameters.get(0))
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<GenericOverall, Any?>(apiErrorHandler) {
|
||||
|
||||
override suspend fun run(params: Any?): GenericOverall {
|
||||
val definitionParameters = params as DefinitionParameters
|
||||
return nextcloudTalkRepository.setFavoriteValueForConversation(user, definitionParameters.get(0), definitionParameters.get(1))
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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 <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return ConversationsListViewModel(application, conversationsUseCase, userUtils) as T
|
||||
return ConversationsListViewModel(application, conversationsUseCase,
|
||||
setConversationFavoriteValueUseCase, leaveConversationUseCase, deleteConversationUseCase,
|
||||
userUtils) as T
|
||||
}
|
||||
}
|
||||
|
@ -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<ConversationItem>()
|
||||
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>(conversation))
|
||||
bundle.putParcelable(BundleKeys.KEY_MENU_TYPE, Parcels.wrap<MenuType>(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 -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<ConversationsListView>(application) {
|
||||
|
||||
val conversationsListData = MutableLiveData<List<Conversation>>()
|
||||
private var conversations: MutableList<Conversation> = mutableListOf()
|
||||
val conversationsLiveListData = MutableLiveData<List<Conversation>>()
|
||||
val viewState = MutableLiveData<ViewState>(LOADING)
|
||||
var messageData: String? = null
|
||||
val searchQuery = MutableLiveData<String>()
|
||||
@ -63,23 +77,91 @@ class ConversationsListViewModel constructor(
|
||||
var currentUserAvatar: MutableLiveData<Drawable> = 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<GenericOverall> {
|
||||
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<GenericOverall> {
|
||||
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<GenericOverall> {
|
||||
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<List<Conversation>> {
|
||||
override fun onSuccess(result: List<Conversation>) {
|
||||
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<BasicListItemWithImage> {
|
||||
val items = mutableListOf<BasicListItemWithImage>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2019 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.newarch.mvvm
|
||||
package com.nextcloud.talk.newarch.utils
|
||||
|
||||
enum class ViewState {
|
||||
LOADING,
|
@ -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,
|
||||
|
@ -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 = "";
|
||||
|
@ -21,5 +21,6 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||
<path android:fillColor="@color/grey_600"
|
||||
android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
||||
|
@ -21,5 +21,6 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||
<path android:fillColor="@color/grey_600"
|
||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||
</vector>
|
||||
|
@ -21,5 +21,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
<path android:fillColor="@color/grey_600" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
||||
|
@ -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">
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
@ -78,7 +79,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toStartOf="@id/dialogUnreadBubble"
|
||||
android:layout_toEndOf="@id/dialogLastMessageUserAvatar"
|
||||
android:ellipsize="end"
|
||||
android:gravity="top"
|
||||
android:lines="1"
|
||||
@ -112,13 +112,25 @@
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/conversation_date" />
|
||||
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="16sp"
|
||||
android:layout_height="16sp"
|
||||
android:id="@+id/actionProgressBar"
|
||||
android:layout_alignBottom="@id/dialogName"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toEndOf="@+id/dialogAvatarFrameLayout"
|
||||
android:indeterminateTint="@color/colorPrimary"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@id/dialogName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/dialogAvatarFrameLayout"
|
||||
android:layout_toStartOf="@id/dialogDate"
|
||||
android:layout_toEndOf="@id/dialogAvatarFrameLayout"
|
||||
android:layout_toEndOf="@id/actionProgressBar"
|
||||
android:ellipsize="end"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
|
@ -147,6 +147,8 @@
|
||||
<string name="nc_start_conversation">Start a conversation</string>
|
||||
<string name="nc_configure_room">Configure conversation</string>
|
||||
<string name="nc_leave">Leave conversation</string>
|
||||
<string name="nc_leave_message">Please confirm your intent to leave this conversation.</string>
|
||||
<string name="nc_simple_leave">Leave</string>
|
||||
<string name="nc_rename">Rename conversation</string>
|
||||
<string name="nc_set_password">Set a password</string>
|
||||
<string name="nc_change_password">Change password</string>
|
||||
@ -157,10 +159,12 @@
|
||||
<string name="nc_make_call_private">Make conversation private</string>
|
||||
<string name="nc_delete_call">Delete conversation</string>
|
||||
<string name="nc_delete">Delete</string>
|
||||
<string name="nc_delete_conversation_default">Please confirm your intent to remove the conversation.</string>
|
||||
<string name="nc_delete_conversation_one2one">If you delete the conversation, it will also be
|
||||
<string name="nc_delete_conversation_default">Please confirm your intent to delete this
|
||||
conversation.</string>
|
||||
<string name="nc_delete_conversation_one2one">If you delete the conversation it will also be
|
||||
deleted for %1$s.</string>
|
||||
<string name="nc_delete_conversation_more">If you delete the conversation, it will also be deleted for all other participants.</string>
|
||||
<string name="nc_delete_conversation_more">If you delete this conversation it will also be
|
||||
deleted for all other participants.</string>
|
||||
|
||||
<string name="nc_new_conversation">New conversation</string>
|
||||
<string name="nc_join_via_link">Join with a link</string>
|
||||
|
Loading…
Reference in New Issue
Block a user