diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.java b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.java index 4797a0ed9..2c64f1e12 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.java @@ -167,5 +167,4 @@ public final class MainActivity extends BaseActivity implements ActionBarProvide super.onBackPressed(); } } - } 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 cfebc83e6..2dd2e396a 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 @@ -176,7 +176,7 @@ public class ConversationItem extends AbstractFlexibleItem adapter; private List recyclerViewItems = new ArrayList<>(); + private LovelySaveStateHandler saveStateHandler; + public ConversationInfoController(Bundle args) { super(args); setHasOptionsMenu(true); NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY); conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN); - baseUrl = args.getString(BundleKeys.KEY_BASE_URL); credentials = ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()); } @@ -143,6 +153,11 @@ public class ConversationInfoController extends BaseController { @Override protected void onViewBound(@NonNull View view) { super.onViewBound(view); + + if (saveStateHandler == null) { + saveStateHandler = new LovelySaveStateHandler(); + } + materialPreferenceScreen.setStorageModule(new DatabaseStorageModule(conversationUser, conversationToken)); if (adapter == null) { fetchRoomInfo(); @@ -157,6 +172,55 @@ public class ConversationInfoController extends BaseController { } } + private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { + switch (dialogId) { + case ID_DELETE_CONVERSATION_DIALOG: + showDeleteConversationDialog(savedInstanceState); + break; + default: + break; + } + } + + + private void showDeleteConversationDialog(Bundle savedInstanceState) { + if (getActivity() != 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.white)) + .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) { + deleteConversation(); + } + }) + .setNegativeButton(R.string.nc_cancel, null) + .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler) + .setSavedInstanceState(savedInstanceState) + .show(); + } + } + + @Override + protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { + saveStateHandler.saveInstanceState(outState); + super.onSaveViewState(view, outState); + } + + @Override + protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { + super.onRestoreViewState(view, savedViewState); + if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { + //Dialog won't be restarted automatically, so we need to call this method. + //Each dialog knows how to restore its state + showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); + } + } + private void setupAdapter() { Activity activity; @@ -252,8 +316,7 @@ public class ConversationInfoController extends BaseController { } } - @OnClick(R.id.deleteConversationAction) - void deleteConversation() { + private void deleteConversation() { Data data; if ((data = getWorkerData()) != null) { OneTimeWorkRequest deleteConversationWorker = @@ -263,6 +326,11 @@ public class ConversationInfoController extends BaseController { } } + @OnClick(R.id.deleteConversationAction) + void deleteConversationClick() { + showDeleteConversationDialog(null); + } + private Data getWorkerData() { if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) { Data.Builder data = new Data.Builder(); @@ -276,9 +344,8 @@ public class ConversationInfoController extends BaseController { private void popTwoLastControllers() { List backstack = getRouter().getBackstack(); - backstack.remove(backstack.size() - 2); + backstack = backstack.subList(0, backstack.size() - 2); getRouter().setBackstack(backstack, new HorizontalChangeHandler()); - getRouter().popCurrentController(); } private void fetchRoomInfo() { @@ -296,7 +363,14 @@ public class ConversationInfoController extends BaseController { conversation = roomOverall.getOcs().getData(); ownOptionsCategory.setVisibility(View.VISIBLE); - if (!conversation.isDeletable()) { + + if (!conversation.canLeave()) { + leaveConversationAction.setVisibility(View.GONE); + } else { + leaveConversationAction.setVisibility(View.VISIBLE); + } + + if (!conversation.canModerate()) { deleteConversationAction.setVisibility(View.GONE); } else { deleteConversationAction.setVisibility(View.VISIBLE); @@ -372,7 +446,7 @@ public class ConversationInfoController extends BaseController { private void setProperNotificationValue(Conversation conversation) { if (messageNotificationLevel != null) { - if (conversation.getType().equals(Conversation.RoomType.ROOM_TYPE_ONE_TO_ONE_CALL)) { + if (conversation.getType().equals(Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)) { // hack to see if we get mentioned always or just on mention if (conversationUser.hasSpreedCapabilityWithName("mention-flag")) { messageNotificationLevel.setValue("always"); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 68cdeb418..7756f1425 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.os.Handler; import android.text.InputType; import android.text.TextUtils; +import android.util.Log; import android.view.*; import android.view.inputmethod.EditorInfo; import android.widget.ProgressBar; @@ -41,6 +42,9 @@ import androidx.core.view.MenuItemCompat; import androidx.recyclerview.widget.DividerItemDecoration; 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; @@ -68,6 +72,8 @@ 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.participants.Participant; import com.nextcloud.talk.models.json.rooms.Conversation; @@ -79,6 +85,8 @@ import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.utils.glide.GlideApp; import com.nextcloud.talk.utils.preferences.AppPreferences; +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; @@ -101,11 +109,12 @@ import java.util.List; @AutoInjector(NextcloudTalkApplication.class) public class ConversationsListController extends BaseController implements SearchView.OnQueryTextListener, FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, FastScroller - .OnScrollStateChangeListener { + .OnScrollStateChangeListener, ConversationMenuInterface { public static final String TAG = "ConversationsListController"; private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery"; + public static final int ID_DELETE_CONVERSATION_DIALOG = 0; @Inject UserUtils userUtils; @@ -116,6 +125,9 @@ public class ConversationsListController extends BaseController implements Searc @Inject NcApi ncApi; + @Inject + Context context; + @Inject AppPreferences appPreferences; @@ -159,6 +171,10 @@ public class ConversationsListController extends BaseController implements Searc private String lastClickedConversationToken; private int scrollTo = 0; + private LovelySaveStateHandler saveStateHandler; + + private Bundle conversationMenuBundle = null; + public ConversationsListController() { super(); setHasOptionsMenu(true); @@ -178,6 +194,10 @@ public class ConversationsListController extends BaseController implements Searc getActionBar().show(); } + if (saveStateHandler == null) { + saveStateHandler = new LovelySaveStateHandler(); + } + if (adapter == null) { adapter = new FlexibleAdapter<>(callItems, getActivity(), true); } else { @@ -505,16 +525,24 @@ public class ConversationsListController extends BaseController implements Searc @Override public void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - super.onSaveViewState(view, 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 state + showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); + } } @Override @@ -584,7 +612,7 @@ public class ConversationsListController extends BaseController implements Searc if (shouldShowCallMenuController) { getChildRouter((ViewGroup) view).setRoot( - RouterTransaction.with(new CallMenuController(bundle)) + RouterTransaction.with(new CallMenuController(bundle, this)) .popChangeHandler(new VerticalChangeHandler()) .pushChangeHandler(new VerticalChangeHandler())); } else { @@ -677,7 +705,7 @@ public class ConversationsListController extends BaseController implements Searc @Subscribe(sticky = true, threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(EventStatus eventStatus) { - if (currentUser != null && eventStatus.getUserId() == currentUser.getId()){ + if (currentUser != null && eventStatus.getUserId() == currentUser.getId()) { switch (eventStatus.getEventType()) { case CONVERSATION_UPDATE: if (eventStatus.isAllGood() && !isRefreshing) { @@ -689,4 +717,71 @@ public class ConversationsListController extends BaseController implements Searc } } } -} \ No newline at end of file + + private void showDeleteConversationDialog(Bundle savedInstanceState) { + if (getActivity() != null && conversationMenuBundle != null && currentUser != null && conversationMenuBundle.getLong(BundleKeys.KEY_INTERNAL_USER_ID) == currentUser.getId()) { + + Conversation conversation = + Parcels.unwrap(conversationMenuBundle.getParcelable(BundleKeys.KEY_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.white)) + .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.KEY_INTERNAL_USER_ID, + conversationMenuBundle.getLong(BundleKeys.KEY_INTERNAL_USER_ID)); + data.putString(BundleKeys.KEY_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/SettingsController.java b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java index b8ac8b14f..8e9c59c1f 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java @@ -62,6 +62,7 @@ import com.nextcloud.talk.jobs.AccountRemovalWorker; import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.utils.ApiUtils; +import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DoNotDisturbUtils; import com.nextcloud.talk.utils.SecurityUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; @@ -70,6 +71,8 @@ import com.nextcloud.talk.utils.glide.GlideApp; import com.nextcloud.talk.utils.preferences.AppPreferences; import com.nextcloud.talk.utils.preferences.MagicUserInputModule; import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder; +import com.yarolegovich.lovelydialog.LovelySaveStateHandler; +import com.yarolegovich.lovelydialog.LovelyStandardDialog; import com.yarolegovich.mp.*; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -178,6 +181,8 @@ public class SettingsController extends BaseController { @Inject Context context; + private LovelySaveStateHandler saveStateHandler; + private UserEntity currentUser; private String credentials; @@ -190,6 +195,8 @@ public class SettingsController extends BaseController { private Disposable profileQueryDisposable; private Disposable dbQueryDisposable; + private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0; + @Override protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { return inflater.inflate(R.layout.controller_settings, container, false); @@ -210,6 +217,10 @@ public class SettingsController extends BaseController { getCurrentUser(); + if (saveStateHandler == null) { + saveStateHandler = new LovelySaveStateHandler(); + } + appPreferences.registerProxyTypeListener(proxyTypeChangeListener = new ProxyTypeChangeListener()); appPreferences.registerProxyCredentialsListener(proxyCredentialsChangeListener = new ProxyCredentialsChangeListener()); appPreferences.registerScreenSecurityListener(screenSecurityChangeListener = new ScreenSecurityChangeListener()); @@ -302,7 +313,6 @@ public class SettingsController extends BaseController { .popChangeHandler(new VerticalChangeHandler())); }); - String host = null; int port = -1; @@ -338,6 +348,78 @@ public class SettingsController extends BaseController { ())); } + private void showLovelyDialog(int dialogId, Bundle savedInstanceState) { + switch (dialogId) { + case ID_REMOVE_ACCOUNT_WARNING_DIALOG: + showRemoveAccountWarning(savedInstanceState); + break; + default: + break; + } + } + + @Override + protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { + saveStateHandler.saveInstanceState(outState); + super.onSaveViewState(view, outState); + } + + @Override + protected void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { + super.onRestoreViewState(view, savedViewState); + if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { + //Dialog won't be restarted automatically, so we need to call this method. + //Each dialog knows how to restore its state + showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState); + } + } + + private void showRemoveAccountWarning(Bundle savedInstanceState) { + if (getActivity() != null) { + new LovelyStandardDialog(getActivity(), LovelyStandardDialog.ButtonLayout.HORIZONTAL) + .setTopColorRes(R.color.nc_darkRed) + .setIcon(DisplayUtils.getTintedDrawable(getResources(), + R.drawable.ic_delete_black_24dp, R.color.white)) + .setPositiveButtonColor(context.getResources().getColor(R.color.nc_darkRed)) + .setTitle(R.string.nc_settings_remove_account) + .setMessage(R.string.nc_settings_remove_confirmation) + .setPositiveButton(R.string.nc_settings_remove, new View.OnClickListener() { + @Override + public void onClick(View v) { + removeCurrentAccount(); + } + }) + .setNegativeButton(R.string.nc_cancel, null) + .setInstanceStateHandler(ID_REMOVE_ACCOUNT_WARNING_DIALOG, saveStateHandler) + .setSavedInstanceState(savedInstanceState) + .show(); + } + } + + private void removeCurrentAccount() { + boolean otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser.getId()); + + OneTimeWorkRequest accountRemovalWork = new OneTimeWorkRequest.Builder(AccountRemovalWorker.class).build(); + WorkManager.getInstance().enqueue(accountRemovalWork); + + if (otherUserExists && getView() != null) { + onViewBound(getView()); + onAttach(getView()); + } else if (!otherUserExists) { + if (getParentController() == null || getParentController().getRouter() == null) { + if (getActivity() != null) { + // Something went very wrong, finish the app + getActivity().finish(); + } + } else { + getParentController().getRouter().setRoot(RouterTransaction.with( + new ServerSelectionController()) + .pushChangeHandler(new VerticalChangeHandler()) + .popChangeHandler(new VerticalChangeHandler())); + } + } + } + @Override protected void onAttach(@NonNull View view) { super.onAttach(view); @@ -495,28 +577,7 @@ public class SettingsController extends BaseController { removeAccountButton.addPreferenceClickListener(view1 -> { - boolean otherUserExists = userUtils.scheduleUserForDeletionWithId(currentUser.getId()); - - OneTimeWorkRequest accountRemovalWork = new OneTimeWorkRequest.Builder(AccountRemovalWorker.class).build(); - WorkManager.getInstance().enqueue(accountRemovalWork); - - if (otherUserExists && getView() != null) { - onViewBound(getView()); - onAttach(getView()); - } else if (!otherUserExists) { - if (getParentController() == null || getParentController().getRouter() == null) { - if (getActivity() != null) { - // Something went very wrong, finish the app - getActivity().finish(); - } - } else { - getParentController().getRouter().setRoot(RouterTransaction.with( - new ServerSelectionController()) - .pushChangeHandler(new VerticalChangeHandler()) - .popChangeHandler(new VerticalChangeHandler())); - } - } - + showLovelyDialog(ID_REMOVE_ACCOUNT_WARNING_DIALOG, null); }); } 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 index dd10340ab..2fb650978 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java @@ -28,8 +28,12 @@ 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; @@ -39,8 +43,12 @@ 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.DeleteConversationWorker; +import com.nextcloud.talk.jobs.LeaveConversationWorker; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.rooms.Conversation; import com.nextcloud.talk.utils.DisplayUtils; @@ -75,6 +83,9 @@ public class CallMenuController extends BaseController implements FlexibleAdapte private MenuType menuType; private Intent shareIntent; + private UserEntity currentUser; + private ConversationMenuInterface conversationMenuInterface; + public CallMenuController(Bundle args) { super(args); this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_ROOM)); @@ -83,6 +94,15 @@ public class CallMenuController extends BaseController implements FlexibleAdapte } } + public CallMenuController(Bundle args, @Nullable ConversationMenuInterface conversationMenuInterface) { + super(args); + this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_ROOM)); + if (args.containsKey(BundleKeys.KEY_MENU_TYPE)) { + this.menuType = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_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); @@ -128,12 +148,11 @@ public class CallMenuController extends BaseController implements FlexibleAdapte menuItems.add(new MenuItem(getResources().getString(R.string.nc_configure_room), 0, null)); } - UserEntity currentUser; + 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 = userUtils.getCurrentUser()) != null && - currentUser.hasSpreedCapabilityWithName("favorites")) { + } else if (currentUser.hasSpreedCapabilityWithName("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))); } @@ -143,7 +162,7 @@ public class CallMenuController extends BaseController implements FlexibleAdapte .ic_pencil_grey600_24dp))); } - if (conversation.canModerate()) { + if (conversation.canModerate() && !currentUser.hasSpreedCapabilityWithName("locked-one-to-one-rooms")) { if (!conversation.isPublic()) { menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_public), 3, getResources().getDrawable(R.drawable .ic_link_grey600_24px))); @@ -158,6 +177,9 @@ public class CallMenuController extends BaseController implements FlexibleAdapte .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()) { @@ -169,13 +191,11 @@ public class CallMenuController extends BaseController implements FlexibleAdapte } } - if (conversation.isDeletable()) { - menuItems.add(new MenuItem(getResources().getString(R.string.nc_delete_call), 9, getResources().getDrawable(R.drawable - .ic_delete_grey600_24dp))); - } - menuItems.add(new MenuItem(getResources().getString(R.string.nc_leave), 1, getResources().getDrawable(R.drawable - .ic_close_grey600_24dp))); + if (conversation.canLeave()) { + menuItems.add(new MenuItem(getResources().getString(R.string.nc_leave), 1, getResources().getDrawable(R.drawable + .ic_close_grey600_24dp))); + } } else if (menuType.equals(MenuType.SHARE)) { prepareIntent(); List appInfoList = ShareUtils.getShareApps(getActivity(), shareIntent, null, @@ -209,21 +229,38 @@ public class CallMenuController extends BaseController implements FlexibleAdapte } if (tag > 0) { - bundle.putInt(BundleKeys.KEY_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())); + 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.putParcelable(BundleKeys.KEY_MENU_TYPE, Parcels.wrap(MenuType.SHARE)); - getRouter().pushController(RouterTransaction.with(new CallMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); + bundle.putInt(BundleKeys.KEY_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.KEY_MENU_TYPE, Parcels.wrap(MenuType.SHARE)); + getRouter().pushController(RouterTransaction.with(new CallMenuController(bundle, null)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } } } } @@ -257,4 +294,27 @@ public class CallMenuController extends BaseController implements FlexibleAdapte public enum MenuType { REGULAR, SHARE } + + private Data getWorkerData() { + if (!TextUtils.isEmpty(conversation.getToken())) { + Data.Builder data = new Data.Builder(); + data.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.getToken()); + data.putLong(BundleKeys.KEY_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.KEY_INTERNAL_USER_ID, currentUser.getId()); + bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap(conversation)); + return bundle; + } + + return null; + + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index 6e4052d9c..df95dd1bf 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -116,7 +116,7 @@ public class OperationsMenuController extends BaseController { private Disposable disposable; - private Conversation.RoomType conversationType; + private Conversation.ConversationType conversationType; private ArrayList invitedUsers = new ArrayList<>(); private ArrayList invitedGroups = new ArrayList<>(); @@ -184,13 +184,6 @@ public class OperationsMenuController extends BaseController { } switch (operationCode) { - case 1: - ncApi.removeSelfFromRoom(credentials, ApiUtils.getUrlForRemoveSelfFromRoom(currentUser.getBaseUrl(), conversation.getToken())) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; case 2: ncApi.renameRoom(credentials, ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken()), conversation.getName()) @@ -232,14 +225,6 @@ public class OperationsMenuController extends BaseController { .retry(1) .subscribe(operationsObserver); break; - case 9: - ncApi.deleteRoom(credentials, ApiUtils.getRoom(currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; case 10: ncApi.getRoom(credentials, ApiUtils.getRoom(baseUrl, conversationToken)) .subscribeOn(Schedulers.newThread()) @@ -278,7 +263,7 @@ public class OperationsMenuController extends BaseController { invite = invitedGroups.get(0); } - if (conversationType.equals(Conversation.RoomType.ROOM_PUBLIC_CALL) || + if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL) || !currentUser.hasSpreedCapabilityWithName("empty-group-room")) { retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), "3", invite, null); @@ -307,7 +292,7 @@ public class OperationsMenuController extends BaseController { @Override public void onNext(RoomOverall roomOverall) { conversation = roomOverall.getOcs().getData(); - if (conversationType.equals(Conversation.RoomType.ROOM_PUBLIC_CALL) && isGroupCallWorkaroundFinal) { + if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL) && isGroupCallWorkaroundFinal) { performGroupCallWorkaround(credentials); } else { inviteUsersToAConversation(); diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.java b/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.java new file mode 100644 index 000000000..8ef3b89a3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/interfaces/ConversationMenuInterface.java @@ -0,0 +1,27 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 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.interfaces; + +import android.os.Bundle; + +public interface ConversationMenuInterface { + void openLovelyDialogWithIdAndBundle(int dialogId, Bundle bundle); +} diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java index a64140f2f..a79b776e9 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java @@ -140,12 +140,12 @@ public class NotificationWorker extends Worker { Conversation conversation = roomOverall.getOcs().getData(); intent.putExtra(BundleKeys.KEY_ROOM, Parcels.wrap(conversation)); - if (conversation.getType().equals(Conversation.RoomType.ROOM_TYPE_ONE_TO_ONE_CALL) || + if (conversation.getType().equals(Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) || (!TextUtils.isEmpty(conversation.getObjectType()) && "share:password".equals (conversation.getObjectType()))) { context.startActivity(intent); } else { - if (conversation.getType().equals(Conversation.RoomType.ROOM_GROUP_CALL)) { + if (conversation.getType().equals(Conversation.ConversationType.ROOM_GROUP_CALL)) { conversationType = "group"; } else { conversationType = "public"; diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java index 1d429e003..0d4d062d4 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumRoomTypeConverter.java @@ -23,23 +23,23 @@ package com.nextcloud.talk.models.json.converters; import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter; import com.nextcloud.talk.models.json.rooms.Conversation; -public class EnumRoomTypeConverter extends IntBasedTypeConverter { +public class EnumRoomTypeConverter extends IntBasedTypeConverter { @Override - public Conversation.RoomType getFromInt(int i) { + public Conversation.ConversationType getFromInt(int i) { switch (i) { case 1: - return Conversation.RoomType.ROOM_TYPE_ONE_TO_ONE_CALL; + return Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL; case 2: - return Conversation.RoomType.ROOM_GROUP_CALL; + return Conversation.ConversationType.ROOM_GROUP_CALL; case 3: - return Conversation.RoomType.ROOM_PUBLIC_CALL; + return Conversation.ConversationType.ROOM_PUBLIC_CALL; default: - return Conversation.RoomType.DUMMY; + return Conversation.ConversationType.DUMMY; } } @Override - public int convertToInt(Conversation.RoomType object) { + public int convertToInt(Conversation.ConversationType object) { switch (object) { case DUMMY: return 0; diff --git a/app/src/main/java/com/nextcloud/talk/models/json/rooms/Conversation.java b/app/src/main/java/com/nextcloud/talk/models/json/rooms/Conversation.java index e2603814a..04e2f8ea2 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/rooms/Conversation.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/rooms/Conversation.java @@ -20,8 +20,11 @@ */ package com.nextcloud.talk.models.json.rooms; +import android.content.res.Resources; import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.R; +import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter; import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter; @@ -45,7 +48,7 @@ public class Conversation { @JsonField(name = "displayName") public String displayName; @JsonField(name = "type", typeConverter = EnumRoomTypeConverter.class) - public RoomType type; + public ConversationType type; @JsonField(name = "count") public long count; @JsonField(name = "lastPing") @@ -79,7 +82,7 @@ public class Conversation { NotificationLevel notificationLevel; public boolean isPublic() { - return (RoomType.ROOM_PUBLIC_CALL.equals(type)); + return (ConversationType.ROOM_PUBLIC_CALL.equals(type)); } public boolean isGuest() { @@ -93,11 +96,24 @@ public class Conversation { } public boolean isNameEditable() { - return (canModerate() && !RoomType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type)); + return (canModerate() && !ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type)); } - public boolean isDeletable() { - return (canModerate() && ((participants != null && participants.size() > 2) || numberOfGuests > 0)); + public boolean canLeave() { + return !canModerate() || (getType() != ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && getParticipants().size() > 1); + + } + + public String getDeleteWarningMessage() { + Resources resources = NextcloudTalkApplication.getSharedApplication().getResources(); + if (getType() == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { + return String.format(resources.getString(R.string.nc_delete_conversation_one2one), + getDisplayName()); + } else if (getParticipants().size() > 1) { + return resources.getString(R.string.nc_delete_conversation_more); + } + + return resources.getString(R.string.nc_delete_conversation_default); } public enum NotificationLevel { @@ -108,7 +124,7 @@ public class Conversation { } @Parcel - public enum RoomType { + public enum ConversationType { DUMMY, ROOM_TYPE_ONE_TO_ONE_CALL, ROOM_GROUP_CALL, diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java index 2e3fe77f3..5c0b2489c 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomPropertiesWebSocketMessage.java @@ -35,5 +35,5 @@ public class RoomPropertiesWebSocketMessage { String name; @JsonField(name = "type", typeConverter = EnumRoomTypeConverter.class) - Conversation.RoomType roomType; + Conversation.ConversationType roomType; } diff --git a/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java b/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java index f2370639a..df2d0e201 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java @@ -160,6 +160,7 @@ public class PowerManagerUtils { } fullLock.release(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { proximityLock.release(); } @@ -180,6 +181,7 @@ public class PowerManagerUtils { if (!wifiLock.isHeld()) { wifiLock.acquire(); } + fullLock.release( ); diff --git a/app/src/main/res/layout/controller_conversation_info.xml b/app/src/main/res/layout/controller_conversation_info.xml index 9c9c24ad0..e6d204de7 100644 --- a/app/src/main/res/layout/controller_conversation_info.xml +++ b/app/src/main/res/layout/controller_conversation_info.xml @@ -62,8 +62,8 @@ android:id="@+id/avatar_image" android:layout_width="@dimen/avatar_size_big" android:layout_height="@dimen/avatar_size_big" - apc:roundAsCircle="true" - android:layout_centerHorizontal="true" /> + android:layout_centerHorizontal="true" + apc:roundAsCircle="true" /> - + apc:mp_title="@string/nc_add_to_favorites"> + @@ -119,29 +119,29 @@ tools:listitem="@layout/rv_item_contact"> - + + android:visibility="gone"> + apc:mp_title="@string/nc_leave" /> + apc:mp_icon_tint="@color/grey_600" + apc:mp_title="@string/nc_delete_call" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 63867a10f..660a66048 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,6 +69,8 @@ Reauthorize Set up client certificate Change client certificate + Remove + Please confirm your intent to remove the current account. Remove account Add a new account Only current account can be reauthorized @@ -143,6 +145,12 @@ Make conversation public Make conversation private Delete conversation + Delete + Please confirm your intent to remove the 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. + New conversation Join via link Join via web