diff --git a/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json b/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json index 911f82088..acfe41a3f 100644 --- a/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json +++ b/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "95ddaea30271abd3160e1cdc8ab404d5", + "identityHash": "26585a95894baebb7d811a80f3811b85", "entities": [ { "tableName": "conversations", @@ -261,7 +261,16 @@ ], "autoGenerate": false }, - "indices": [], + "indices": [ + { + "name": "index_messages_conversation_id", + "unique": false, + "columnNames": [ + "conversation_id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_messages_conversation_id` ON `${TABLE_NAME}` (`conversation_id`)" + } + ], "foreignKeys": [ { "table": "conversations", @@ -360,7 +369,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '95ddaea30271abd3160e1cdc8ab404d5')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '26585a95894baebb7d811a80f3811b85')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt index 3b7f15836..24e664128 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt @@ -26,7 +26,6 @@ import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager -import autodagger.AutoInjector import butterknife.BindView import butterknife.ButterKnife import com.bluelinelabs.conductor.Conductor @@ -40,7 +39,6 @@ import com.nextcloud.talk.controllers.CallNotificationController import com.nextcloud.talk.events.ConfigurationChangeEvent import com.nextcloud.talk.utils.bundle.BundleKeys -@AutoInjector(NextcloudTalkApplication::class) class MagicCallActivity : BaseActivity() { @BindView(R.id.controller_container) @@ -50,7 +48,6 @@ class MagicCallActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) requestWindowFeature(Window.FEATURE_NO_TITLE) window.addFlags( diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt index c7c4ba2b7..c053ffaa6 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt @@ -30,7 +30,6 @@ import coil.api.load import coil.transform.CircleCropTransformation import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java deleted file mode 100644 index 0be6021c1..000000000 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * 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.components.filebrowser.controllers; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Bundle; -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 androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import autodagger.AutoInjector; -import butterknife.BindView; -import butterknife.OnClick; -import com.google.android.material.bottomnavigation.BottomNavigationItemView; -import com.nextcloud.talk.R; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.components.filebrowser.adapters.items.BrowserFileItem; -import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface; -import com.nextcloud.talk.components.filebrowser.models.BrowserFile; -import com.nextcloud.talk.components.filebrowser.models.DavResponse; -import com.nextcloud.talk.components.filebrowser.operations.DavListing; -import com.nextcloud.talk.components.filebrowser.operations.ListingAbstractClass; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.interfaces.SelectionInterface; -import com.nextcloud.talk.jobs.ShareOperationWorker; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.newarch.local.models.UserNgEntity; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import eu.davidea.fastscroller.FastScroller; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import javax.inject.Inject; -import okhttp3.OkHttpClient; -import org.parceler.Parcel; -import org.parceler.Parcels; - -public class BrowserController extends BaseController implements ListingInterface, - FlexibleAdapter.OnItemClickListener, SelectionInterface { - private final Set selectedPaths; - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - @BindView(R.id.fast_scroller) - FastScroller fastScroller; - @BindView(R.id.action_back) - BottomNavigationItemView backMenuItem; - @BindView(R.id.action_refresh) - BottomNavigationItemView actionRefreshMenuItem; - @Inject - OkHttpClient okHttpClient; - - private MenuItem filesSelectionDoneMenuItem; - private RecyclerView.LayoutManager layoutManager; - - private FlexibleAdapter adapter; - private List recyclerViewItems = new ArrayList<>(); - - private ListingAbstractClass listingAbstractClass; - private BrowserType browserType; - private String currentPath; - private UserNgEntity activeUser; - private String roomToken; - - public BrowserController(Bundle args) { - super(); - setHasOptionsMenu(true); - browserType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_BROWSER_TYPE())); - activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY())); - roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()); - - currentPath = "/"; - if (BrowserType.DAV_BROWSER.equals(browserType)) { - listingAbstractClass = new DavListing(this); - } else { - //listingAbstractClass = new LocalListing(this); - } - - selectedPaths = Collections.synchronizedSet(new TreeSet<>()); - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_browser, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - if (adapter == null) { - adapter = new FlexibleAdapter<>(recyclerViewItems, context, false); - } - - changeEnabledStatusForBarItems(true); - prepareViews(); - } - - private void onFileSelectionDone() { - synchronized (selectedPaths) { - Iterator iterator = selectedPaths.iterator(); - - List paths = new ArrayList<>(); - Data data; - OneTimeWorkRequest shareWorker; - - while (iterator.hasNext()) { - String path = iterator.next(); - paths.add(path); - iterator.remove(); - if (paths.size() == 10 || !iterator.hasNext()) { - data = new Data.Builder() - .putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), activeUser.getId()) - .putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomToken) - .putStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS(), paths.toArray(new String[0])) - .build(); - shareWorker = new OneTimeWorkRequest.Builder(ShareOperationWorker.class) - .setInputData(data) - .build(); - WorkManager.getInstance().enqueue(shareWorker); - paths = new ArrayList<>(); - } - } - } - - getRouter().popCurrentController(); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_share_files, menu); - filesSelectionDoneMenuItem = menu.findItem(R.id.files_selection_done); - filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.files_selection_done: - onFileSelectionDone(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - refreshCurrentPath(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - listingAbstractClass.tearDown(); - } - - @Override - public String getTitle() { - return currentPath; - } - - @OnClick(R.id.action_back) - void goBack() { - fetchPath(new File(currentPath).getParent()); - } - - @OnClick(R.id.action_refresh) - void refreshCurrentPath() { - fetchPath(currentPath); - } - - @SuppressLint("RestrictedApi") - private void changeEnabledStatusForBarItems(boolean shouldBeEnabled) { - if (actionRefreshMenuItem != null) { - actionRefreshMenuItem.setEnabled(shouldBeEnabled); - } - - if (backMenuItem != null) { - backMenuItem.setEnabled(shouldBeEnabled && !currentPath.equals("/")); - } - } - - private void fetchPath(String path) { - listingAbstractClass.cancelAllJobs(); - changeEnabledStatusForBarItems(false); - - listingAbstractClass.getFiles(path, activeUser, - BrowserType.DAV_BROWSER.equals(browserType) ? okHttpClient : null); - } - - @Override - public void listingResult(DavResponse davResponse) { - adapter.clear(); - List fileBrowserItems = new ArrayList<>(); - if (davResponse.getData() != null) { - final List objectList = (List) davResponse.getData(); - - currentPath = objectList.get(0).getPath(); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> setTitle()); - } - - for (int i = 1; i < objectList.size(); i++) { - fileBrowserItems.add(new BrowserFileItem(objectList.get(i), activeUser, this)); - } - } - - adapter.addItems(0, fileBrowserItems); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - adapter.notifyDataSetChanged(); - changeEnabledStatusForBarItems(true); - }); - } - } - - private boolean shouldPathBeSelectedDueToParent(String currentPath) { - if (selectedPaths.size() > 0) { - File file = new File(currentPath); - if (!file.getParent().equals("/")) { - while (file.getParent() != null) { - String parent = file.getParent(); - if (new File(file.getParent()).getParent() != null) { - parent += "/"; - } - - if (selectedPaths.contains(parent)) { - return true; - } - - file = new File(file.getParent()); - } - } - } - - return false; - } - - private void checkAndRemoveAnySelectedParents(String currentPath) { - File file = new File(currentPath); - selectedPaths.remove(currentPath); - while (file.getParent() != null) { - selectedPaths.remove(file.getParent() + "/"); - file = new File(file.getParent()); - } - - adapter.notifyDataSetChanged(); - } - - @Override - public boolean onItemClick(View view, int position) { - BrowserFile browserFile = ((BrowserFileItem) adapter.getItem(position)).getModel(); - if ("inode/directory".equals((browserFile.getMimeType()))) { - fetchPath(browserFile.getPath()); - return true; - } - - return false; - } - - private void prepareViews() { - if (getActivity() != null) { - layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); - - adapter.setFastScroller(fastScroller); - adapter.addListener(this); - - fastScroller.setBubbleTextCreator(position -> { - IFlexible abstractFlexibleItem = adapter.getItem(position); - if (abstractFlexibleItem instanceof BrowserFileItem) { - return String.valueOf( - ((BrowserFileItem) adapter.getItem(position)).getModel().getDisplayName().charAt(0)); - } else { - return ""; - } - }); - } - } - - @SuppressLint("RestrictedApi") - @Override - public void toggleBrowserItemSelection(String path) { - if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) { - checkAndRemoveAnySelectedParents(path); - } else { - // TOOD: if it's a folder, remove all the children we added manually - selectedPaths.add(path); - } - - filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); - } - - @Override - public boolean isPathSelected(String path) { - return (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)); - } - - @Parcel - public enum BrowserType { - FILE_BROWSER, - DAV_BROWSER, - } -} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.kt b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.kt new file mode 100644 index 000000000..19fdcf125 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.kt @@ -0,0 +1,313 @@ +/* + * 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.components.filebrowser.controllers + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.* +import androidx.recyclerview.widget.RecyclerView +import androidx.work.Data +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import butterknife.BindView +import butterknife.OnClick +import com.google.android.material.bottomnavigation.BottomNavigationItemView +import com.nextcloud.talk.R +import com.nextcloud.talk.components.filebrowser.adapters.items.BrowserFileItem +import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface +import com.nextcloud.talk.components.filebrowser.models.BrowserFile +import com.nextcloud.talk.components.filebrowser.models.DavResponse +import com.nextcloud.talk.components.filebrowser.operations.DavListing +import com.nextcloud.talk.components.filebrowser.operations.ListingAbstractClass +import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.interfaces.SelectionInterface +import com.nextcloud.talk.jobs.ShareOperationWorker +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.utils.bundle.BundleKeys +import eu.davidea.fastscroller.FastScroller +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import okhttp3.OkHttpClient +import org.koin.android.ext.android.inject +import org.parceler.Parcel +import org.parceler.Parcels +import java.io.File +import java.util.* + +class BrowserController(args: Bundle) : BaseController(), ListingInterface, FlexibleAdapter.OnItemClickListener, SelectionInterface { + private val selectedPaths: MutableSet + @JvmField @BindView(R.id.recyclerView) + internal var recyclerView: RecyclerView? = null + @JvmField @BindView(R.id.fast_scroller) + internal var fastScroller: FastScroller? = null + @JvmField @BindView(R.id.action_back) + internal var backMenuItem: BottomNavigationItemView? = null + @JvmField @BindView(R.id.action_refresh) + internal var actionRefreshMenuItem: BottomNavigationItemView? = null + + val okHttpClient:OkHttpClient by inject() + + private var filesSelectionDoneMenuItem: MenuItem? = null + private var layoutManager: RecyclerView.LayoutManager? = null + + private var adapter: FlexibleAdapter>? = null + private val recyclerViewItems = ArrayList>() + + private var listingAbstractClass: ListingAbstractClass? = null + private val browserType: BrowserType + private var currentPath: String? = null + private val activeUser: UserNgEntity + private val roomToken: String? + + init { + setHasOptionsMenu(true) + browserType = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_BROWSER_TYPE)) + activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_USER_ENTITY)) + roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN) + + currentPath = "/" + if (BrowserType.DAV_BROWSER == browserType) { + listingAbstractClass = DavListing(this) + } else { + //listingAbstractClass = new LocalListing(this); + } + + selectedPaths = Collections.synchronizedSet(TreeSet()) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.controller_browser, container, false) + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + if (adapter == null) { + adapter = FlexibleAdapter(recyclerViewItems, context, false) + } + + changeEnabledStatusForBarItems(true) + prepareViews() + } + + private fun onFileSelectionDone() { + synchronized(selectedPaths) { + val iterator = selectedPaths.iterator() + + var paths: MutableList = ArrayList() + var data: Data + var shareWorker: OneTimeWorkRequest + + while (iterator.hasNext()) { + val path = iterator.next() + paths.add(path) + iterator.remove() + if (paths.size == 10 || !iterator.hasNext()) { + data = Data.Builder() + .putLong(BundleKeys.KEY_INTERNAL_USER_ID, activeUser.id!!) + .putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + .putStringArray(BundleKeys.KEY_FILE_PATHS, paths.toTypedArray()) + .build() + shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java) + .setInputData(data) + .build() + WorkManager.getInstance().enqueue(shareWorker) + paths = ArrayList() + } + } + } + + router.popCurrentController() + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_share_files, menu) + filesSelectionDoneMenuItem = menu.findItem(R.id.files_selection_done) + filesSelectionDoneMenuItem!!.isVisible = selectedPaths.size > 0 + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.files_selection_done -> { + onFileSelectionDone() + return true + } + else -> return super.onOptionsItemSelected(item) + } + } + + override fun onAttach(view: View) { + super.onAttach(view) + refreshCurrentPath() + } + + public override fun onDestroy() { + super.onDestroy() + listingAbstractClass!!.tearDown() + } + + override fun getTitle(): String? { + return currentPath + } + + @OnClick(R.id.action_back) + internal fun goBack() { + fetchPath(File(currentPath!!).parent) + } + + @OnClick(R.id.action_refresh) + internal fun refreshCurrentPath() { + fetchPath(currentPath) + } + + @SuppressLint("RestrictedApi") + private fun changeEnabledStatusForBarItems(shouldBeEnabled: Boolean) { + if (actionRefreshMenuItem != null) { + actionRefreshMenuItem!!.isEnabled = shouldBeEnabled + } + + if (backMenuItem != null) { + backMenuItem!!.isEnabled = shouldBeEnabled && currentPath != "/" + } + } + + private fun fetchPath(path: String?) { + listingAbstractClass!!.cancelAllJobs() + changeEnabledStatusForBarItems(false) + + listingAbstractClass!!.getFiles(path, activeUser, + if (BrowserType.DAV_BROWSER == browserType) okHttpClient else null) + } + + override fun listingResult(davResponse: DavResponse) { + adapter!!.clear() + val fileBrowserItems = ArrayList>() + if (davResponse.data != null) { + val objectList = davResponse.data as List + + currentPath = objectList[0].path + + if (activity != null) { + activity!!.runOnUiThread { setTitle() } + } + + for (i in 1 until objectList.size) { + fileBrowserItems.add(BrowserFileItem(objectList[i], activeUser, this)) + } + } + + adapter!!.addItems(0, fileBrowserItems) + + if (activity != null) { + activity!!.runOnUiThread { + adapter!!.notifyDataSetChanged() + changeEnabledStatusForBarItems(true) + } + } + } + + private fun shouldPathBeSelectedDueToParent(currentPath: String): Boolean { + if (selectedPaths.size > 0) { + var file = File(currentPath) + if (file.parent != "/") { + while (file.parent != null) { + var parent = file.parent + if (File(file.parent!!).parent != null) { + parent += "/" + } + + if (selectedPaths.contains(parent)) { + return true + } + + file = File(file.parent!!) + } + } + } + + return false + } + + private fun checkAndRemoveAnySelectedParents(currentPath: String) { + var file = File(currentPath) + selectedPaths.remove(currentPath) + while (file.parent != null) { + selectedPaths.remove(file.parent!! + "/") + file = File(file.parent!!) + } + + adapter!!.notifyDataSetChanged() + } + + override fun onItemClick(view: View, position: Int): Boolean { + val browserFile = (adapter!!.getItem(position) as BrowserFileItem).model + if ("inode/directory" == browserFile.mimeType) { + fetchPath(browserFile.path) + return true + } + + return false + } + + private fun prepareViews() { + if (activity != null) { + layoutManager = SmoothScrollLinearLayoutManager(activity!!) + recyclerView!!.layoutManager = layoutManager + recyclerView!!.setHasFixedSize(true) + recyclerView!!.adapter = adapter + + adapter!!.setFastScroller(fastScroller) + adapter!!.addListener(this) + + fastScroller!!.setBubbleTextCreator { position -> + val abstractFlexibleItem = adapter!!.getItem(position) + if (abstractFlexibleItem is BrowserFileItem) { + (adapter!!.getItem(position) as BrowserFileItem).model.displayName.toCharArray()[0].toString() + } else { + "" + } + } + } + } + + @SuppressLint("RestrictedApi") + override fun toggleBrowserItemSelection(path: String) { + if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) { + checkAndRemoveAnySelectedParents(path) + } else { + // TOOD: if it's a folder, remove all the children we added manually + selectedPaths.add(path) + } + + filesSelectionDoneMenuItem!!.isVisible = selectedPaths.size > 0 + } + + override fun isPathSelected(path: String): Boolean { + return selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path) + } + + @Parcel + enum class BrowserType { + FILE_BROWSER, + DAV_BROWSER + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java index bdcb6637c..46b844748 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java @@ -22,8 +22,8 @@ package com.nextcloud.talk.components.filebrowser.webdav; import com.nextcloud.talk.components.filebrowser.models.BrowserFile; import com.nextcloud.talk.components.filebrowser.models.DavResponse; -import com.nextcloud.talk.dagger.modules.RestModule; import com.nextcloud.talk.newarch.local.models.UserNgEntity; +import com.nextcloud.talk.newarch.utils.NetworkUtils; import com.nextcloud.talk.utils.ApiUtils; import java.io.IOException; @@ -49,7 +49,7 @@ public class ReadFilesystemOperation { OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder(); okHttpClientBuilder.followRedirects(false); okHttpClientBuilder.followSslRedirects(false); - okHttpClientBuilder.authenticator(new RestModule.MagicAuthenticator( + okHttpClientBuilder.authenticator(new NetworkUtils.MagicAuthenticator( ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), "Authorization")); this.okHttpClient = okHttpClientBuilder.build(); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt index b895c04a0..44eee0e3f 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt @@ -151,7 +151,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus() } - ncApi!!.getServerStatus(queryUrl) + ncApi.getServerStatus(queryUrl) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .`as`>(AutoDispose.autoDisposable(scopeProvider)) @@ -194,7 +194,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo } private fun findServerTalkApp(credentials: String?) { - ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) .subscribeOn(Schedulers.io()) .`as`>(AutoDispose.autoDisposable(scopeProvider)) .subscribe(object : Observer { @@ -250,7 +250,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo } private fun fetchProfile(credentials: String?) { - ncApi!!.getUserProfile(credentials, + ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(baseUrl)) .subscribeOn(Schedulers.io()) .`as`>(AutoDispose.autoDisposable(scopeProvider)) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallController.kt b/app/src/main/java/com/nextcloud/talk/controllers/CallController.kt index 4c5d7570d..5bd099cd9 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.kt @@ -46,7 +46,7 @@ import android.widget.ProgressBar import android.widget.RelativeLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import autodagger.AutoInjector + import butterknife.BindView import butterknife.OnClick import butterknife.OnLongClick @@ -110,7 +110,6 @@ import java.util.HashMap import java.util.HashSet import java.util.Objects import java.util.concurrent.TimeUnit -import javax.inject.Inject import me.zhanghai.android.effortlesspermissions.AfterPermissionDenied import me.zhanghai.android.effortlesspermissions.EffortlessPermissions import me.zhanghai.android.effortlesspermissions.OpenAppDetailsDialogFragment @@ -393,7 +392,7 @@ class CallController(args: Bundle) : BaseController() { } private fun handleFromNotification() { - ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) .retry(3) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -943,7 +942,7 @@ class CallController(args: Bundle) : BaseController() { } private fun fetchSignalingSettings() { - ncApi!!.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(baseUrl)) + ncApi.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(baseUrl)) .subscribeOn(Schedulers.io()) .retry(3) .observeOn(AndroidSchedulers.mainThread()) @@ -1046,7 +1045,7 @@ class CallController(args: Bundle) : BaseController() { } private fun checkCapabilities() { - ncApi!!.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) + ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) .retry(3) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1092,7 +1091,7 @@ class CallController(args: Bundle) : BaseController() { } private fun joinRoomAndCall() { - ncApi!!.joinRoom( + ncApi.joinRoom( credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant( baseUrl, roomToken @@ -1131,7 +1130,7 @@ class CallController(args: Bundle) : BaseController() { } private fun performCall() { - ncApi!!.joinCall( + ncApi.joinCall( credentials, ApiUtils.getUrlForCall(baseUrl, roomToken) ) @@ -1148,7 +1147,7 @@ class CallController(args: Bundle) : BaseController() { setCallState(CallStatus.ESTABLISHED) if (needsPing) { - ncApi!!.pingCall(credentials, ApiUtils.getUrlForCallPing(baseUrl, roomToken)) + ncApi.pingCall(credentials, ApiUtils.getUrlForCallPing(baseUrl, roomToken)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) } @@ -1193,7 +1192,7 @@ class CallController(args: Bundle) : BaseController() { } if (!hasExternalSignalingServer) { - ncApi!!.pullSignalingMessages( + ncApi.pullSignalingMessages( credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken) ) @@ -1393,7 +1392,7 @@ class CallController(args: Bundle) : BaseController() { ) if (magicPeerConnectionWrapper.peerConnection != null) { - magicPeerConnectionWrapper.peerConnection + magicPeerConnectionWrapper.peerConnection!! .setRemoteDescription( magicPeerConnectionWrapper .magicSdpObserver, sessionDescriptionWithPreferredCodec @@ -1474,7 +1473,7 @@ class CallController(args: Bundle) : BaseController() { } private fun hangupNetworkCalls(shutDownView: Boolean) { - ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) + ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Observer { @@ -1509,7 +1508,7 @@ class CallController(args: Bundle) : BaseController() { } private fun leaveRoom(shutDownView: Boolean) { - ncApi!!.leaveRoom( + ncApi.leaveRoom( credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, roomToken) ) @@ -1603,7 +1602,7 @@ class CallController(args: Bundle) : BaseController() { } private fun getPeersForCall() { - ncApi!!.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) + ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) .subscribeOn(Schedulers.io()) .subscribe(object : Observer { override fun onSubscribe(d: Disposable) { @@ -1660,25 +1659,25 @@ class CallController(args: Bundle) : BaseController() { if (hasMCU && publisher) { magicPeerConnectionWrapper = MagicPeerConnectionWrapper( peerConnectionFactory!!, - iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, true, true, + iceServers, sdpConstraintsForMCU!!, sessionId, callSession, localMediaStream, true, true, type ) } else if (hasMCU) { magicPeerConnectionWrapper = MagicPeerConnectionWrapper( peerConnectionFactory!!, - iceServers, sdpConstraints, sessionId, callSession, null, false, true, type + iceServers, sdpConstraints!!, sessionId, callSession, null, false, true, type ) } else { if ("screen" != type) { magicPeerConnectionWrapper = MagicPeerConnectionWrapper( peerConnectionFactory!!, - iceServers, sdpConstraints, sessionId, callSession, localMediaStream, false, false, + iceServers, sdpConstraints!!, sessionId, callSession, localMediaStream, false, false, type ) } else { magicPeerConnectionWrapper = MagicPeerConnectionWrapper( peerConnectionFactory!!, - iceServers, sdpConstraints, sessionId, callSession, null, false, false, type + iceServers, sdpConstraints!!, sessionId, callSession, null, false, false, type ) } } @@ -1943,7 +1942,7 @@ class CallController(args: Bundle) : BaseController() { urlToken = roomToken } - ncApi!!.sendSignalingMessages( + ncApi.sendSignalingMessages( credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken), strings.toString() ) @@ -2134,7 +2133,7 @@ class CallController(args: Bundle) : BaseController() { } else { gotNick( session, - getPeerConnectionWrapperForSessionIdAndType(session, type, false).nick, false, + getPeerConnectionWrapperForSessionIdAndType(session, type, false).nick!!, false, type ) } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt index f1364d220..f5320fea9 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt @@ -184,7 +184,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr } private fun checkIfAnyParticipantsRemainInRoom() { - ncApi!!.getPeersForCall( + ncApi.getPeersForCall( credentials, ApiUtils.getUrlForParticipants( userBeingCalled!!.baseUrl, currentConversation!!.token @@ -232,7 +232,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr } private fun handleFromNotification() { - ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled!!.baseUrl)) + ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled!!.baseUrl)) .subscribeOn(Schedulers.io()) .retry(3) .observeOn(AndroidSchedulers.mainThread()) @@ -401,8 +401,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr transformations(CircleCropTransformation()) listener(onSuccess = { data, dataSource -> GlobalScope.launch { - if ((AvatarStatusCodeHolder.getInstance().statusCode == 200 || AvatarStatusCodeHolder.getInstance().statusCode == 0) - && userBeingCalled.hasSpreedFeatureCapability("no-ping")) { + if ((AvatarStatusCodeHolder.getInstance().statusCode == 200 || AvatarStatusCodeHolder.getInstance().statusCode == 0)) { if (activity != null) { val newBitmap = BlurTransformation(activity!!, 5f).transform( diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt index 1870e26f0..e3a53e913 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt @@ -46,7 +46,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager -import autodagger.AutoInjector + import butterknife.BindView import butterknife.OnClick import butterknife.Optional @@ -99,7 +99,6 @@ import java.util.ArrayList import java.util.Collections import java.util.HashMap import java.util.HashSet -import javax.inject.Inject class ContactsController : BaseController, SearchView.OnQueryTextListener, @@ -222,9 +221,6 @@ class ContactsController : BaseController, override fun onViewBound(view: View) { super.onViewBound(view) - NextcloudTalkApplication.sharedApplication!! - .componentApplication - .inject(this) currentUser = usersRepository.getActiveUser() @@ -278,7 +274,7 @@ class ContactsController : BaseController, currentUser!!.baseUrl, roomType, userId, null ) - ncApi!!.createRoom( + ncApi.createRoom( credentials, retrofitBucket.url, retrofitBucket.queryMap ) @@ -304,7 +300,7 @@ class ContactsController : BaseController, ) if (currentUser!!.hasSpreedFeatureCapability("chat-v2")) { - ncApi!!.getRoom( + ncApi.getRoom( credentials, ApiUtils.getRoom( currentUser!!.baseUrl, @@ -509,7 +505,7 @@ class ContactsController : BaseController, } val finalServerIs14OrUp = serverIs14OrUp - ncApi!!.getContactsWithSearchParam( + ncApi.getContactsWithSearchParam( credentials, retrofitBucket.url, shareTypesList, modifiedQueryMap ) @@ -854,7 +850,7 @@ class ContactsController : BaseController, userItem.model.userId, null ) - ncApi!!.createRoom( + ncApi.createRoom( credentials, retrofitBucket.url, retrofitBucket.queryMap ) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 525001568..dc27f31f0 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -38,7 +38,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager -import autodagger.AutoInjector + import butterknife.BindView import butterknife.OnClick import coil.api.load @@ -99,11 +99,10 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import org.koin.android.ext.android.inject import java.util.ArrayList import java.util.Calendar -import javax.inject.Inject -@AutoInjector(NextcloudTalkApplication::class) class ConversationInfoController(args: Bundle) : BaseController(), FlexibleAdapter.OnItemClickListener, ConversationInfoInterface { @@ -168,10 +167,7 @@ class ConversationInfoController(args: Bundle) : BaseController(), @BindView(R.id.shareAction) lateinit var shareAction: MaterialStandardPreference - @set:Inject - lateinit var ncApi: NcApi - @set:Inject - lateinit var userUtils: UserUtils + val ncApi: NcApi by inject() private val conversationToken: String? private val conversationUser: UserNgEntity? @@ -201,7 +197,6 @@ class ConversationInfoController(args: Bundle) : BaseController(), init { setHasOptionsMenu(true) - NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this) conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY) conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN) credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java deleted file mode 100644 index 48f1f9e55..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * 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.controllers; - -import android.annotation.SuppressLint; -import android.database.Cursor; -import android.media.MediaPlayer; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.R; -import com.nextcloud.talk.adapters.items.NotificationSoundItem; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.models.RingtoneSettings; -import com.nextcloud.talk.utils.bundle.BundleKeys; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import butterknife.BindView; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.SelectableAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; - -public class RingtoneSelectionController extends BaseController - implements FlexibleAdapter.OnItemClickListener { - - private static final String TAG = "RingtoneSelectionController"; - - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - - @BindView(R.id.swipe_refresh_layout) - SwipeRefreshLayout swipeRefreshLayout; - - private FlexibleAdapter adapter; - private RecyclerView.AdapterDataObserver adapterDataObserver; - private List abstractFlexibleItemList = new ArrayList<>(); - - private boolean callNotificationSounds; - private MediaPlayer mediaPlayer; - private Handler cancelMediaPlayerHandler; - - public RingtoneSelectionController(Bundle args) { - super(); - setHasOptionsMenu(true); - this.callNotificationSounds = - args.getBoolean(BundleKeys.INSTANCE.getKEY_ARE_CALL_SOUNDS(), false); - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_generic_rv, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - - if (adapter == null) { - adapter = new FlexibleAdapter<>(abstractFlexibleItemList, getActivity(), false); - - adapter.setNotifyChangeOfUnfilteredItems(true) - .setMode(SelectableAdapter.Mode.SINGLE); - - adapter.addListener(this); - - cancelMediaPlayerHandler = new Handler(); - } - - adapter.addListener(this); - prepareViews(); - fetchNotificationSounds(); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - return getRouter().popCurrentController(); - default: - return super.onOptionsItemSelected(item); - } - } - - private void prepareViews() { - RecyclerView.LayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); - - adapterDataObserver = new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - findSelectedSound(); - } - }; - - adapter.registerAdapterDataObserver(adapterDataObserver); - swipeRefreshLayout.setEnabled(false); - } - - @SuppressLint("LongLogTag") - private void findSelectedSound() { - boolean foundDefault = false; - - String preferencesString = null; - if ((callNotificationSounds && TextUtils.isEmpty( - (preferencesString = appPreferences.getCallRingtoneUri()))) - || (!callNotificationSounds && TextUtils.isEmpty((preferencesString = appPreferences - .getMessageRingtoneUri())))) { - adapter.toggleSelection(1); - foundDefault = true; - } - - if (!TextUtils.isEmpty(preferencesString) && !foundDefault) { - try { - RingtoneSettings ringtoneSettings = - LoganSquare.parse(preferencesString, RingtoneSettings.class); - if (ringtoneSettings.getRingtoneUri() == null) { - adapter.toggleSelection(0); - } else if (ringtoneSettings.getRingtoneUri().toString().equals(getRingtoneString())) { - adapter.toggleSelection(1); - } else { - NotificationSoundItem notificationSoundItem; - for (int i = 2; i < adapter.getItemCount(); i++) { - notificationSoundItem = (NotificationSoundItem) adapter.getItem(i); - if (notificationSoundItem.getNotificationSoundUri() - .equals(ringtoneSettings.getRingtoneUri().toString())) { - adapter.toggleSelection(i); - break; - } - } - } - } catch (IOException e) { - Log.e(TAG, "Failed to parse ringtone settings"); - } - } - - adapter.unregisterAdapterDataObserver(adapterDataObserver); - adapterDataObserver = null; - } - - private String getRingtoneString() { - if (callNotificationSounds) { - return ("android.resource://" + context.getPackageName() + - "/raw/librem_by_feandesign_call"); - } else { - return ("android.resource://" + context.getPackageName() + "/raw" + - "/librem_by_feandesign_message"); - } - } - - private void fetchNotificationSounds() { - abstractFlexibleItemList.add( - new NotificationSoundItem(getResources().getString(R.string.nc_settings_no_ringtone), - null)); - abstractFlexibleItemList.add(new NotificationSoundItem(getResources() - .getString(R.string.nc_settings_default_ringtone), getRingtoneString())); - - if (getActivity() != null) { - RingtoneManager manager = new RingtoneManager(getActivity()); - - if (callNotificationSounds) { - manager.setType(RingtoneManager.TYPE_RINGTONE); - } else { - manager.setType(RingtoneManager.TYPE_NOTIFICATION); - } - - Cursor cursor = manager.getCursor(); - - NotificationSoundItem notificationSoundItem; - - while (cursor.moveToNext()) { - String notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); - String notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX); - String completeNotificationUri = notificationUri + "/" + cursor.getString(RingtoneManager - .ID_COLUMN_INDEX); - - notificationSoundItem = - new NotificationSoundItem(notificationTitle, completeNotificationUri); - - abstractFlexibleItemList.add(notificationSoundItem); - } - } - - adapter.updateDataSet(abstractFlexibleItemList, false); - } - - @Override - public String getTitle() { - return getResources().getString(R.string.nc_settings_notification_sounds); - } - - @SuppressLint("LongLogTag") - @Override - public boolean onItemClick(View view, int position) { - NotificationSoundItem notificationSoundItem = (NotificationSoundItem) adapter.getItem(position); - - Uri ringtoneUri = null; - - if (!TextUtils.isEmpty(notificationSoundItem.getNotificationSoundUri())) { - ringtoneUri = Uri.parse(notificationSoundItem.getNotificationSoundUri()); - - endMediaPlayer(); - mediaPlayer = MediaPlayer.create(getActivity(), ringtoneUri); - - cancelMediaPlayerHandler = new Handler(); - cancelMediaPlayerHandler.postDelayed(new Runnable() { - @Override - public void run() { - endMediaPlayer(); - } - }, mediaPlayer.getDuration() + 25); - mediaPlayer.start(); - } - - if (adapter.getSelectedPositions().size() == 0 - || adapter.getSelectedPositions().get(0) != position) { - RingtoneSettings ringtoneSettings = new RingtoneSettings(); - ringtoneSettings.setRingtoneName(notificationSoundItem.getNotificationSoundName()); - ringtoneSettings.setRingtoneUri(ringtoneUri); - - if (callNotificationSounds) { - try { - appPreferences.setCallRingtoneUri(LoganSquare.serialize(ringtoneSettings)); - adapter.toggleSelection(position); - adapter.notifyDataSetChanged(); - } catch (IOException e) { - Log.e(TAG, "Failed to store selected ringtone for calls"); - } - } else { - try { - appPreferences.setMessageRingtoneUri(LoganSquare.serialize(ringtoneSettings)); - adapter.toggleSelection(position); - adapter.notifyDataSetChanged(); - } catch (IOException e) { - Log.e(TAG, "Failed to store selected ringtone for calls"); - } - } - } - - return true; - } - - private void endMediaPlayer() { - if (cancelMediaPlayerHandler != null) { - cancelMediaPlayerHandler.removeCallbacksAndMessages(null); - } - - if (mediaPlayer != null) { - if (mediaPlayer.isPlaying()) { - mediaPlayer.stop(); - } - - mediaPlayer.release(); - mediaPlayer = null; - } - } - - @Override - public void onDestroy() { - endMediaPlayer(); - super.onDestroy(); - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt new file mode 100644 index 000000000..8c86a1d12 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.kt @@ -0,0 +1,281 @@ +/* + * 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.controllers + +import android.annotation.SuppressLint +import android.media.MediaPlayer +import android.media.RingtoneManager +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.text.TextUtils +import android.util.Log +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout + +import com.bluelinelabs.logansquare.LoganSquare +import com.nextcloud.talk.R +import com.nextcloud.talk.adapters.items.NotificationSoundItem +import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.models.RingtoneSettings +import com.nextcloud.talk.utils.bundle.BundleKeys + +import java.io.IOException +import java.util.ArrayList + +import butterknife.BindView +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.SelectableAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem + +class RingtoneSelectionController(args: Bundle) : BaseController(), FlexibleAdapter.OnItemClickListener { + + @JvmField @BindView(R.id.recyclerView) + internal var recyclerView: RecyclerView? = null + + @JvmField @BindView(R.id.swipe_refresh_layout) + internal var swipeRefreshLayout: SwipeRefreshLayout? = null + + private var adapter: FlexibleAdapter>? = null + private var adapterDataObserver: RecyclerView.AdapterDataObserver? = null + private val abstractFlexibleItemList = ArrayList>() + + private val callNotificationSounds: Boolean + private var mediaPlayer: MediaPlayer? = null + private var cancelMediaPlayerHandler: Handler? = null + + private val ringtoneString: String + get() = if (callNotificationSounds) { + "android.resource://" + context.packageName + + "/raw/librem_by_feandesign_call" + } else { + "android.resource://" + context.packageName + "/raw" + + "/librem_by_feandesign_message" + } + + init { + setHasOptionsMenu(true) + this.callNotificationSounds = args.getBoolean(BundleKeys.KEY_ARE_CALL_SOUNDS, false) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.controller_generic_rv, container, false) + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + + if (adapter == null) { + adapter = FlexibleAdapter(abstractFlexibleItemList, activity, false) + + adapter!!.setNotifyChangeOfUnfilteredItems(true).mode = SelectableAdapter.Mode.SINGLE + + adapter!!.addListener(this) + + cancelMediaPlayerHandler = Handler() + } + + adapter!!.addListener(this) + prepareViews() + fetchNotificationSounds() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> return router.popCurrentController() + else -> return super.onOptionsItemSelected(item) + } + } + + private fun prepareViews() { + val layoutManager = SmoothScrollLinearLayoutManager(activity!!) + recyclerView!!.layoutManager = layoutManager + recyclerView!!.setHasFixedSize(true) + recyclerView!!.adapter = adapter + + adapterDataObserver = object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + findSelectedSound() + } + } + + adapter!!.registerAdapterDataObserver(adapterDataObserver!!) + swipeRefreshLayout!!.isEnabled = false + } + + @SuppressLint("LongLogTag") + private fun findSelectedSound() { + var foundDefault = false + + var preferencesString: String? + if (callNotificationSounds) { + preferencesString = appPreferences.callRingtoneUri + } else { + preferencesString = appPreferences.messageRingtoneUri + } + + if (preferencesString.isNullOrEmpty()) { + adapter!!.toggleSelection(1) + foundDefault = true + } + + if (!preferencesString.isNullOrEmpty() && !foundDefault) { + try { + val ringtoneSettings = LoganSquare.parse(preferencesString, RingtoneSettings::class.java) + if (ringtoneSettings.ringtoneUri == null) { + adapter!!.toggleSelection(0) + } else if (ringtoneSettings.ringtoneUri.toString().equals(ringtoneString)) { + adapter!!.toggleSelection(1) + } else { + var notificationSoundItem: NotificationSoundItem + for (i in 2 until adapter!!.itemCount) { + notificationSoundItem = adapter!!.getItem(i) as NotificationSoundItem + if (notificationSoundItem.notificationSoundUri == ringtoneSettings.ringtoneUri.toString()) { + adapter!!.toggleSelection(i) + break + } + } + } + } catch (e: IOException) { + Log.e(TAG, "Failed to parse ringtone settings") + } + + } + + adapter!!.unregisterAdapterDataObserver(adapterDataObserver!!) + adapterDataObserver = null + } + + private fun fetchNotificationSounds() { + abstractFlexibleItemList.add( + NotificationSoundItem(resources!!.getString(R.string.nc_settings_no_ringtone), null)) + abstractFlexibleItemList.add(NotificationSoundItem(resources!! + .getString(R.string.nc_settings_default_ringtone), ringtoneString)) + + if (activity != null) { + val manager = RingtoneManager(activity) + + if (callNotificationSounds) { + manager.setType(RingtoneManager.TYPE_RINGTONE) + } else { + manager.setType(RingtoneManager.TYPE_NOTIFICATION) + } + + val cursor = manager.cursor + + var notificationSoundItem: NotificationSoundItem + + while (cursor.moveToNext()) { + val notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX) + val notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX) + val completeNotificationUri = "$notificationUri/" + cursor.getString(RingtoneManager + .ID_COLUMN_INDEX) + + notificationSoundItem = NotificationSoundItem(notificationTitle, completeNotificationUri) + + abstractFlexibleItemList.add(notificationSoundItem) + } + } + + adapter!!.updateDataSet(abstractFlexibleItemList, false) + } + + override fun getTitle(): String? { + return resources!!.getString(R.string.nc_settings_notification_sounds) + } + + @SuppressLint("LongLogTag") + override fun onItemClick(view: View, position: Int): Boolean { + val notificationSoundItem = adapter!!.getItem(position) as NotificationSoundItem? + + var ringtoneUri: Uri? = null + + if (!TextUtils.isEmpty(notificationSoundItem!!.notificationSoundUri)) { + ringtoneUri = Uri.parse(notificationSoundItem.notificationSoundUri) + + endMediaPlayer() + mediaPlayer = MediaPlayer.create(activity, ringtoneUri) + + cancelMediaPlayerHandler = Handler() + cancelMediaPlayerHandler!!.postDelayed({ endMediaPlayer() }, (mediaPlayer!!.duration + 25).toLong()) + mediaPlayer!!.start() + } + + if (adapter!!.selectedPositions.size == 0 || adapter!!.selectedPositions[0] != position) { + val ringtoneSettings = RingtoneSettings() + ringtoneSettings.ringtoneName = notificationSoundItem.notificationSoundName + ringtoneSettings.ringtoneUri = ringtoneUri + + if (callNotificationSounds) { + try { + appPreferences.callRingtoneUri = LoganSquare.serialize(ringtoneSettings) + adapter!!.toggleSelection(position) + adapter!!.notifyDataSetChanged() + } catch (e: IOException) { + Log.e(TAG, "Failed to store selected ringtone for calls") + } + + } else { + try { + appPreferences.messageRingtoneUri = LoganSquare.serialize(ringtoneSettings) + adapter!!.toggleSelection(position) + adapter!!.notifyDataSetChanged() + } catch (e: IOException) { + Log.e(TAG, "Failed to store selected ringtone for calls") + } + + } + } + + return true + } + + private fun endMediaPlayer() { + if (cancelMediaPlayerHandler != null) { + cancelMediaPlayerHandler!!.removeCallbacksAndMessages(null) + } + + if (mediaPlayer != null) { + if (mediaPlayer!!.isPlaying) { + mediaPlayer!!.stop() + } + + mediaPlayer!!.release() + mediaPlayer = null + } + } + + public override fun onDestroy() { + endMediaPlayer() + super.onDestroy() + } + + companion object { + + private val TAG = "RingtoneSelectionController" + } +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt index 3422ee390..de8ea7542 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.kt @@ -35,7 +35,7 @@ import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.ProgressBar import android.widget.TextView -import autodagger.AutoInjector + import butterknife.BindView import butterknife.OnClick import com.bluelinelabs.conductor.RouterTransaction @@ -55,13 +55,15 @@ import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder import com.uber.autodispose.AutoDispose import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import studio.carbonylgroup.textfieldboxes.ExtendedEditText import studio.carbonylgroup.textfieldboxes.TextFieldBoxes import java.security.cert.CertificateException -import javax.inject.Inject -@AutoInjector(NextcloudTalkApplication::class) class ServerSelectionController : BaseController() { @JvmField @BindView(R.id.extended_edit_text) @@ -119,65 +121,70 @@ class ServerSelectionController : BaseController() { && TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type))) { providersTextView!!.visibility = View.INVISIBLE } else { - val users = usersRepository.getUsers() - val usersSize = users.count() - if ((TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type)) || - findAccounts(users).isEmpty()) && - usersSize == 0) { - providersTextView!!.setText(R.string.nc_get_from_provider) - providersTextView!!.setOnClickListener { view12: View? -> - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(resources!! - .getString(R.string.nc_providers_url))) - startActivity(browserIntent) - } - } else if (findAccounts(users).isNotEmpty()) { - if (!TextUtils.isEmpty(getAppNameBasedOnPackage(resources!! - .getString(R.string.nc_import_accounts_from)))) { - if (findAccounts(users).size > 1) { - providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_accounts), - getAppNameBasedOnPackage(resources!! - .getString(R.string.nc_import_accounts_from))) + GlobalScope.launch { + val users = usersRepository.getUsers() + val usersSize = users.count() + + withContext(Dispatchers.Main) { + if ((TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type)) || + findAccounts(users).isEmpty()) && + usersSize == 0) { + providersTextView!!.setText(R.string.nc_get_from_provider) + providersTextView!!.setOnClickListener { view12: View? -> + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(resources!! + .getString(R.string.nc_providers_url))) + startActivity(browserIntent) + } + } else if (findAccounts(users).isNotEmpty()) { + if (!TextUtils.isEmpty(getAppNameBasedOnPackage(resources!! + .getString(R.string.nc_import_accounts_from)))) { + if (findAccounts(users).size > 1) { + providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_accounts), + getAppNameBasedOnPackage(resources!! + .getString(R.string.nc_import_accounts_from))) + } else { + providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_account), + getAppNameBasedOnPackage(resources!! + .getString(R.string.nc_import_accounts_from))) + } + } else { + if (findAccounts(users).size > 1) { + providersTextView!!.text = resources!!.getString(R.string.nc_server_import_accounts_plain) + } else { + providersTextView!!.text = resources!!.getString(R.string.nc_server_import_account_plain) + } + } + providersTextView!!.setOnClickListener { view13: View? -> + val bundle = Bundle() + bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true) + router.pushController(RouterTransaction.with( + SwitchAccountController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } } else { - providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_account), - getAppNameBasedOnPackage(resources!! - .getString(R.string.nc_import_accounts_from))) - } - } else { - if (findAccounts(users).size > 1) { - providersTextView!!.text = resources!!.getString(R.string.nc_server_import_accounts_plain) - } else { - providersTextView!!.text = resources!!.getString(R.string.nc_server_import_account_plain) + providersTextView!!.visibility = View.INVISIBLE } } - providersTextView!!.setOnClickListener { view13: View? -> - val bundle = Bundle() - bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true) - router.pushController(RouterTransaction.with( - SwitchAccountController(bundle)) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler())) - } - } else { - providersTextView!!.visibility = View.INVISIBLE - } - } - serverEntry!!.requestFocus() - serverEntry!!.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} - override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} - override fun afterTextChanged(editable: Editable) { - if (!textFieldBoxes!!.isOnError && !TextUtils.isEmpty(serverEntry!!.text)) { - toggleProceedButton(true) - } else { - toggleProceedButton(false) + serverEntry!!.requestFocus() + serverEntry!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + override fun afterTextChanged(editable: Editable) { + if (!textFieldBoxes!!.isOnError && !TextUtils.isEmpty(serverEntry!!.text)) { + toggleProceedButton(true) + } else { + toggleProceedButton(false) + } + } + }) + serverEntry!!.setOnEditorActionListener { textView: TextView?, i: Int, keyEvent: KeyEvent? -> + if (i == EditorInfo.IME_ACTION_DONE) { + checkServerAndProceed() + } + false } } - }) - serverEntry!!.setOnEditorActionListener { textView: TextView?, i: Int, keyEvent: KeyEvent? -> - if (i == EditorInfo.IME_ACTION_DONE) { - checkServerAndProceed() - } - false } } @@ -210,7 +217,7 @@ class ServerSelectionController : BaseController() { } private fun checkServer(queryUrl: String, checkForcedHttps: Boolean) { - ncApi!!.getServerStatus(queryUrl) + ncApi.getServerStatus(queryUrl) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .`as`(AutoDispose.autoDisposable(scopeProvider)) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt index e2cbcc5f4..d9f49df42 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt @@ -43,7 +43,7 @@ import androidx.core.view.ViewCompat import androidx.emoji.widget.EmojiTextView import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager -import autodagger.AutoInjector + import butterknife.BindView import butterknife.OnClick import coil.api.load @@ -97,7 +97,6 @@ import java.util.ArrayList import java.util.Arrays import java.util.Locale import java.util.Objects -import javax.inject.Inject class SettingsController : BaseController() { @JvmField @@ -176,7 +175,6 @@ class SettingsController : BaseController() { @JvmField @BindView(R.id.message_text) var messageText: TextView? = null - @JvmField val ncApi: NcApi by inject() val usersRepository: UsersRepository by inject() private var saveStateHandler: LovelySaveStateHandler? = null @@ -599,7 +597,7 @@ class SettingsController : BaseController() { loadAvatarImage() - ncApi!!.getUserProfile( + ncApi.getUserProfile( credentials, ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) ) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java deleted file mode 100644 index e32f11169..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Parts related to account import were either copied from or inspired by the great work done by David Luhmer at: - * https://github.com/nextcloud/ownCloud-Account-Importer - */ - -package com.nextcloud.talk.controllers; - -import android.accounts.Account; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -import autodagger.AutoInjector; -import butterknife.BindView; - -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.nextcloud.talk.R; -import com.nextcloud.talk.adapters.items.AdvancedUserItem; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.models.ImportAccount; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.models.json.participants.Participant; -import com.nextcloud.talk.newarch.local.models.UserNgEntity; -import com.nextcloud.talk.utils.AccountUtils; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import com.uber.autodispose.AutoDispose; - -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; -import io.reactivex.Observer; -import io.reactivex.disposables.Disposable; - -import java.net.CookieManager; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -public class SwitchAccountController extends BaseController { - - @Inject - UserUtils userUtils; - - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - - @Inject - CookieManager cookieManager; - - @BindView(R.id.swipe_refresh_layout) - SwipeRefreshLayout swipeRefreshLayout; - private FlexibleAdapter adapter; - private List userItems = new ArrayList<>(); - - private boolean isAccountImport = false; - - private FlexibleAdapter.OnItemClickListener onImportItemClickListener = - new FlexibleAdapter.OnItemClickListener() { - @Override - public boolean onItemClick(View view, int position) { - if (userItems.size() > position) { - Account account = ((AdvancedUserItem) userItems.get(position)).getAccount(); - reauthorizeFromImport(account); - } - - return true; - } - }; - - private FlexibleAdapter.OnItemClickListener onSwitchItemClickListener = - new FlexibleAdapter.OnItemClickListener() { - @Override - public boolean onItemClick(View view, int position) { - if (userItems.size() > position) { - UserEntity userEntity = ((AdvancedUserItem) userItems.get(position)).getEntity(); - userUtils.createOrUpdateUser(null, - null, null, null, - null, true, null, userEntity.getId(), null, null, null) - .as(AutoDispose.autoDisposable(getScopeProvider())) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(UserEntity userEntity) { - cookieManager.getCookieStore().removeAll(); - - userUtils.disableAllUsersWithoutId(userEntity.getId()); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> getRouter().popCurrentController()); - } - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onComplete() { - - } - }); - } - - return true; - } - }; - - public SwitchAccountController() { - setHasOptionsMenu(true); - } - - public SwitchAccountController(Bundle args) { - super(); - setHasOptionsMenu(true); - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT())) { - isAccountImport = true; - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - getRouter().popCurrentController(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_generic_rv, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - swipeRefreshLayout.setEnabled(false); - - if (getActionBar() != null) { - getActionBar().show(); - } - - if (adapter == null) { - adapter = new FlexibleAdapter<>(userItems, getActivity(), false); - - UserNgEntity userEntity; - Participant participant; - - if (!isAccountImport) { - for (Object userEntityObject : userUtils.getUsers()) { - userEntity = (UserEntity) userEntityObject; - if (!userEntity.getCurrent()) { - participant = new Participant(); - participant.setName(userEntity.getDisplayName()); - - String userId; - - if (userEntity.getUserId() != null) { - userId = userEntity.getUserId(); - } else { - userId = userEntity.getUsername(); - } - participant.setUserId(userId); - userItems.add(new AdvancedUserItem(participant, userEntity, null)); - } - } - - adapter.addListener(onSwitchItemClickListener); - adapter.updateDataSet(userItems, false); - } else { - Account account; - ImportAccount importAccount; - for (Object accountObject : AccountUtils.INSTANCE.findAccounts(userUtils.getUsers())) { - account = (Account) accountObject; - importAccount = AccountUtils.INSTANCE.getInformationFromAccount(account); - - participant = new Participant(); - participant.name = importAccount.username; - participant.userId = importAccount.username; - userEntity = new UserEntity(); - userEntity.setBaseUrl(importAccount.getBaseUrl()); - userItems.add(new AdvancedUserItem(participant, userEntity, account)); - } - - adapter.addListener(onImportItemClickListener); - adapter.updateDataSet(userItems, false); - } - } - - prepareViews(); - } - - private void prepareViews() { - LinearLayoutManager layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setHasFixedSize(true); - recyclerView.setAdapter(adapter); - - swipeRefreshLayout.setEnabled(false); - } - - private void reauthorizeFromImport(Account account) { - ImportAccount importAccount = AccountUtils.INSTANCE.getInformationFromAccount(account); - Bundle bundle = new Bundle(); - bundle.putString(BundleKeys.INSTANCE.getKEY_BASE_URL(), importAccount.baseUrl); - bundle.putString(BundleKeys.INSTANCE.getKEY_USERNAME(), importAccount.username); - bundle.putString(BundleKeys.INSTANCE.getKEY_TOKEN(), importAccount.token); - bundle.putBoolean(BundleKeys.INSTANCE.getKEY_IS_ACCOUNT_IMPORT(), true); - getRouter().pushController(RouterTransaction.with(new AccountVerificationController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } - - @Override - public String getTitle() { - return getResources().getString(R.string.nc_select_an_account); - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt new file mode 100644 index 000000000..e80f91f0b --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt @@ -0,0 +1,227 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Parts related to account import were either copied from or inspired by the great work done by David Luhmer at: + * https://github.com/nextcloud/ownCloud-Account-Importer + */ + +package com.nextcloud.talk.controllers + +import android.accounts.Account +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout + +import butterknife.BindView + +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler +import com.nextcloud.talk.R +import com.nextcloud.talk.adapters.items.AdvancedUserItem +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.models.ImportAccount +import com.nextcloud.talk.models.json.participants.Participant +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.utils.AccountUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.database.user.UserUtils +import com.uber.autodispose.AutoDispose + +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.reactivex.Observer +import io.reactivex.disposables.Disposable +import org.koin.android.ext.android.inject + +import java.net.CookieManager +import java.util.ArrayList + +class SwitchAccountController : BaseController { + @JvmField @BindView(R.id.recyclerView) + internal var recyclerView: RecyclerView? = null + + val cookieManager: CookieManager by inject() + val userUtils: UserUtils by inject() + + @JvmField @BindView(R.id.swipe_refresh_layout) + internal var swipeRefreshLayout: SwipeRefreshLayout? = null + private var adapter: FlexibleAdapter>? = null + private val userItems = ArrayList>() + + private var isAccountImport = false + + private val onImportItemClickListener = FlexibleAdapter.OnItemClickListener { view, position -> + if (userItems.size > position) { + val account = (userItems[position] as AdvancedUserItem).account + reauthorizeFromImport(account) + } + + true + } + + private val onSwitchItemClickListener = FlexibleAdapter.OnItemClickListener { view, position -> + if (userItems.size > position) { + val userEntity = (userItems[position] as AdvancedUserItem).entity + /*userUtils!!.createOrUpdateUser(null, null, null, null, null, true, null, userEntity!!.getId(), null, null, null) + .`as`(AutoDispose.autoDisposable(scopeProvider)) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(userEntity: UserEntity) { + cookieManager!!.cookieStore.removeAll() + + userUtils!!.disableAllUsersWithoutId(userEntity.getId()) + if (activity != null) { + activity!!.runOnUiThread { router.popCurrentController() } + } + } + + override fun onError(e: Throwable) { + + } + + override fun onComplete() { + + } + })*/ + } + + true + } + + constructor() { + setHasOptionsMenu(true) + } + + constructor(args: Bundle) : super() { + setHasOptionsMenu(true) + + if (args.containsKey(BundleKeys.KEY_IS_ACCOUNT_IMPORT)) { + isAccountImport = true + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + router.popCurrentController() + return true + } + else -> return super.onOptionsItemSelected(item) + } + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.controller_generic_rv, container, false) + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + swipeRefreshLayout!!.isEnabled = false + + if (actionBar != null) { + actionBar!!.show() + } + + /* + + if (adapter == null) { + adapter = FlexibleAdapter(userItems, activity, false) + + var userEntity: UserNgEntity + var participant: Participant + + if (!isAccountImport) { + for (userEntityObject in userUtils!!.users) { + userEntity = userEntityObject as UserEntity + if (!userEntity.getCurrent()) { + participant = Participant() + participant.setName(userEntity.displayName) + + val userId: String + + if (userEntity.userId != null) { + userId = userEntity.userId + } else { + userId = userEntity.username + } + participant.setUserId(userId) + userItems.add(AdvancedUserItem(participant, userEntity, null)) + } + } + + adapter!!.addListener(onSwitchItemClickListener) + adapter!!.updateDataSet(userItems, false) + } else { + var account: Account + var importAccount: ImportAccount + for (accountObject in AccountUtils.findAccounts(userUtils!!.users)) { + account = accountObject + importAccount = AccountUtils.getInformationFromAccount(account) + + participant = Participant() + participant.name = importAccount.username + participant.userId = importAccount.username + userEntity = UserEntity() + userEntity.baseUrl = importAccount.getBaseUrl() + userItems.add(AdvancedUserItem(participant, userEntity, account)) + } + + adapter!!.addListener(onImportItemClickListener) + adapter!!.updateDataSet(userItems, false) + } + }*/ + + prepareViews() + } + + private fun prepareViews() { + val layoutManager = SmoothScrollLinearLayoutManager(activity!!) + recyclerView!!.layoutManager = layoutManager + recyclerView!!.setHasFixedSize(true) + recyclerView!!.adapter = adapter + + swipeRefreshLayout!!.isEnabled = false + } + + private fun reauthorizeFromImport(account: Account?) { + val importAccount = AccountUtils.getInformationFromAccount(account!!) + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_BASE_URL, importAccount.baseUrl) + bundle.putString(BundleKeys.KEY_USERNAME, importAccount.username) + bundle.putString(BundleKeys.KEY_TOKEN, importAccount.token) + bundle.putBoolean(BundleKeys.KEY_IS_ACCOUNT_IMPORT, true) + router.pushController(RouterTransaction.with(AccountVerificationController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } + + override fun getTitle(): String? { + return resources!!.getString(R.string.nc_select_an_account) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt index 826b87faf..5cce137c5 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.kt @@ -33,7 +33,7 @@ import android.view.inputmethod.InputMethodManager import android.widget.EditText import androidx.annotation.RequiresApi import androidx.appcompat.app.ActionBar -import autodagger.AutoInjector + import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.controllers.AccountVerificationController @@ -47,7 +47,6 @@ import org.greenrobot.eventbus.EventBus import org.koin.android.ext.android.inject import java.util.ArrayList -@AutoInjector(NextcloudTalkApplication::class) abstract class BaseController : ButterKnifeController(), ComponentCallbacks { val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java deleted file mode 100644 index a06905cf8..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.controllers.bottomsheet; - -import android.content.ComponentName; -import android.content.Intent; -import android.graphics.PorterDuff; -import android.os.Bundle; -import android.text.Editable; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.ImageView; -import androidx.annotation.NonNull; -import butterknife.BindView; -import butterknife.OnClick; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.google.android.material.textfield.TextInputLayout; -import com.nextcloud.talk.R; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.events.BottomSheetLockEvent; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.utils.EmojiTextInputEditText; -import com.nextcloud.talk.utils.ShareUtils; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder; -import com.vanniktech.emoji.EmojiImageView; -import com.vanniktech.emoji.EmojiPopup; -import com.vanniktech.emoji.emoji.Emoji; -import com.vanniktech.emoji.listeners.OnEmojiClickListener; -import com.vanniktech.emoji.listeners.OnEmojiPopupDismissListener; -import com.vanniktech.emoji.listeners.OnEmojiPopupShownListener; -import org.greenrobot.eventbus.EventBus; -import org.parceler.Parcels; - -public class EntryMenuController extends BaseController { - - @BindView(R.id.ok_button) - Button proceedButton; - - @BindView(R.id.text_edit) - EmojiTextInputEditText editText; - - @BindView(R.id.text_input_layout) - TextInputLayout textInputLayout; - - @BindView(R.id.smileyButton) - ImageView smileyButton; - - private int operationCode; - private Conversation conversation; - private Intent shareIntent; - private String packageName; - private String name; - private String callUrl; - - private EmojiPopup emojiPopup; - - private Bundle originalBundle; - - public EntryMenuController(Bundle args) { - super(); - originalBundle = args; - - this.operationCode = args.getInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_SHARE_INTENT())) { - this.shareIntent = - Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SHARE_INTENT())); - } - - this.name = args.getString(BundleKeys.INSTANCE.getKEY_APP_ITEM_NAME(), ""); - this.packageName = args.getString(BundleKeys.INSTANCE.getKEY_APP_ITEM_PACKAGE_NAME(), ""); - this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_entry_menu, container, false); - } - - @OnClick(R.id.smileyButton) - void onSmileyClick() { - emojiPopup.toggle(); - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - if (ApplicationWideMessageHolder.getInstance().getMessageType() != null && - ApplicationWideMessageHolder.getInstance() - .getMessageType() - .equals(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG)) { - textInputLayout.setError(getResources().getString(R.string.nc_wrong_password)); - ApplicationWideMessageHolder.getInstance().setMessageType(null); - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.7f); - } - } - } - - @OnClick(R.id.ok_button) - public void onProceedButtonClick() { - Bundle bundle; - if (operationCode == 99) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), - editText.getText().toString()); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); - if (originalBundle.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), - originalBundle.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); - } - - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (operationCode != 7 && operationCode != 10 && operationCode != 11) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - bundle = new Bundle(); - if (operationCode == 4 || operationCode == 6) { - conversation.setPassword(editText.getText().toString()); - } else { - conversation.setName(editText.getText().toString()); - } - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (operationCode == 7) { - if (getActivity() != null) { - shareIntent.putExtra(Intent.EXTRA_TEXT, ShareUtils.getStringForIntent(getActivity(), - editText.getText().toString(), conversation)); - Intent intent = new Intent(shareIntent); - intent.setComponent(new ComponentName(packageName, name)); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getActivity().startActivity(intent); - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); - } - } else if (operationCode != 11) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - bundle = new Bundle(); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), operationCode); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), editText.getText().toString()); - getRouter().pushController(RouterTransaction.with(new OperationsMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (operationCode == 11) { - eventBus.post(new BottomSheetLockEvent(false, 0, false, false)); - originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), - editText.getText().toString()); - getRouter().pushController( - RouterTransaction.with(new OperationsMenuController(originalBundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication() - .getComponentApplication() - .inject(this); - - if (conversation != null && operationCode == 2) { - editText.setText(conversation.getName()); - } - - editText.setOnEditorActionListener((v, actionId, event) -> { - if (actionId == EditorInfo.IME_ACTION_DONE - && proceedButton != null - && proceedButton.isEnabled()) { - proceedButton.callOnClick(); - return true; - } - return false; - }); - - editText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable s) { - if (!TextUtils.isEmpty(s)) { - if (operationCode == 2) { - if (conversation.getName() == null || !conversation.getName().equals(s.toString())) { - if (!proceedButton.isEnabled()) { - proceedButton.setEnabled(true); - proceedButton.setAlpha(1.0f); - } - textInputLayout.setErrorEnabled(false); - } else { - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.38f); - } - textInputLayout.setError(getResources().getString(R.string.nc_call_name_is_same)); - } - } else if (operationCode != 10) { - if (!proceedButton.isEnabled()) { - proceedButton.setEnabled(true); - proceedButton.setAlpha(1.0f); - } - textInputLayout.setErrorEnabled(false); - } else if ((editText.getText().toString().startsWith("http://") || - editText.getText().toString().startsWith("https://")) && - editText.getText().toString().contains("/call/")) { - // operation code 10 - if (!proceedButton.isEnabled()) { - proceedButton.setEnabled(true); - proceedButton.setAlpha(1.0f); - } - textInputLayout.setErrorEnabled(false); - } else { - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.38f); - } - textInputLayout.setError(getResources().getString(R.string.nc_wrong_link)); - } - } else { - if (proceedButton.isEnabled()) { - proceedButton.setEnabled(false); - proceedButton.setAlpha(0.38f); - } - textInputLayout.setErrorEnabled(false); - } - } - }); - - String labelText = ""; - switch (operationCode) { - case 11: - case 2: - labelText = getResources().getString(R.string.nc_call_name); - editText.setInputType(InputType.TYPE_CLASS_TEXT); - smileyButton.setVisibility(View.VISIBLE); - emojiPopup = EmojiPopup.Builder.fromRootView(view) - .setOnEmojiPopupShownListener(new OnEmojiPopupShownListener() { - @Override - public void onEmojiPopupShown() { - if (getResources() != null) { - smileyButton.setColorFilter(getResources().getColor(R.color.colorPrimary), - PorterDuff.Mode.SRC_IN); - } - } - }) - .setOnEmojiPopupDismissListener(new OnEmojiPopupDismissListener() { - @Override - public void onEmojiPopupDismiss() { - if (smileyButton != null) { - smileyButton.setColorFilter(getResources().getColor(R.color.emoji_icons), - PorterDuff.Mode.SRC_IN); - } - } - }) - .setOnEmojiClickListener(new OnEmojiClickListener() { - @Override - public void onEmojiClick(@NonNull EmojiImageView emoji, @NonNull Emoji imageView) { - editText.getEditableText().append(" "); - } - }) - .build(editText); - - break; - case 4: - labelText = getResources().getString(R.string.nc_new_password); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - break; - case 6: - case 7: - case 99: - // 99 is joining a conversation via password - labelText = getResources().getString(R.string.nc_password); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - break; - case 10: - labelText = getResources().getString(R.string.nc_conversation_link); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); - break; - default: - break; - } - - textInputLayout.setPasswordVisibilityToggleEnabled( - operationCode == 99 || operationCode == 4 || operationCode == 6 || operationCode == 7); - textInputLayout.setHint(labelText); - textInputLayout.requestFocus(); - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.kt new file mode 100644 index 000000000..5a086f596 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/EntryMenuController.kt @@ -0,0 +1,297 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.controllers.bottomsheet + +import android.content.ComponentName +import android.content.Intent +import android.graphics.PorterDuff +import android.os.Bundle +import android.text.Editable +import android.text.InputType +import android.text.TextUtils +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.widget.Button +import android.widget.ImageView +import butterknife.BindView +import butterknife.OnClick +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler +import com.google.android.material.textfield.TextInputLayout +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.events.BottomSheetLockEvent +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.utils.EmojiTextInputEditText +import com.nextcloud.talk.utils.ShareUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.database.user.UserUtils +import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder +import com.vanniktech.emoji.EmojiImageView +import com.vanniktech.emoji.EmojiPopup +import com.vanniktech.emoji.emoji.Emoji +import com.vanniktech.emoji.listeners.OnEmojiClickListener +import com.vanniktech.emoji.listeners.OnEmojiPopupDismissListener +import com.vanniktech.emoji.listeners.OnEmojiPopupShownListener +import org.greenrobot.eventbus.EventBus +import org.parceler.Parcels + +class EntryMenuController(private val originalBundle: Bundle) : BaseController() { + + @JvmField @BindView(R.id.ok_button) + internal var proceedButton: Button? = null + + @JvmField @BindView(R.id.text_edit) + internal var editText: EmojiTextInputEditText? = null + + @JvmField @BindView(R.id.text_input_layout) + internal var textInputLayout: TextInputLayout? = null + + @JvmField @BindView(R.id.smileyButton) + internal var smileyButton: ImageView? = null + + private val operationCode: Int + private var conversation: Conversation? = null + private var shareIntent: Intent? = null + private val packageName: String + private val name: String + private val callUrl: String + + private var emojiPopup: EmojiPopup? = null + + init { + + this.operationCode = originalBundle.getInt(BundleKeys.KEY_OPERATION_CODE) + if (originalBundle.containsKey(BundleKeys.KEY_ROOM)) { + this.conversation = Parcels.unwrap(originalBundle.getParcelable(BundleKeys.KEY_ROOM)) + } + + if (originalBundle.containsKey(BundleKeys.KEY_SHARE_INTENT)) { + this.shareIntent = Parcels.unwrap(originalBundle.getParcelable(BundleKeys.KEY_SHARE_INTENT)) + } + + this.name = originalBundle.getString(BundleKeys.KEY_APP_ITEM_NAME, "") + this.packageName = originalBundle.getString(BundleKeys.KEY_APP_ITEM_PACKAGE_NAME, "") + this.callUrl = originalBundle.getString(BundleKeys.KEY_CALL_URL, "") + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.controller_entry_menu, container, false) + } + + @OnClick(R.id.smileyButton) + internal fun onSmileyClick() { + emojiPopup!!.toggle() + } + + override fun onAttach(view: View) { + super.onAttach(view) + if (ApplicationWideMessageHolder.getInstance().messageType != null && ApplicationWideMessageHolder.getInstance() + .messageType == ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG) { + textInputLayout!!.error = resources!!.getString(R.string.nc_wrong_password) + ApplicationWideMessageHolder.getInstance().messageType = null + if (proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = false + proceedButton!!.alpha = 0.7f + } + } + } + + @OnClick(R.id.ok_button) + fun onProceedButtonClick() { + val bundle: Bundle + if (operationCode == 99) { + eventBus.post(BottomSheetLockEvent(false, 0, false, false)) + bundle = Bundle() + bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap(conversation)) + bundle.putString(BundleKeys.KEY_CALL_URL, callUrl) + bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, + editText!!.text!!.toString()) + bundle.putInt(BundleKeys.KEY_OPERATION_CODE, operationCode) + if (originalBundle.containsKey(BundleKeys.KEY_SERVER_CAPABILITIES)) { + bundle.putParcelable(BundleKeys.KEY_SERVER_CAPABILITIES, + originalBundle.getParcelable(BundleKeys.KEY_SERVER_CAPABILITIES)) + } + + router.pushController(RouterTransaction.with(OperationsMenuController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } else if (operationCode != 7 && operationCode != 10 && operationCode != 11) { + eventBus.post(BottomSheetLockEvent(false, 0, false, false)) + bundle = Bundle() + if (operationCode == 4 || operationCode == 6) { + conversation!!.password = editText!!.text!!.toString() + } else { + conversation!!.name = editText!!.text!!.toString() + } + bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap(conversation)) + bundle.putInt(BundleKeys.KEY_OPERATION_CODE, operationCode) + router.pushController(RouterTransaction.with(OperationsMenuController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } else if (operationCode == 7) { + if (activity != null) { + shareIntent!!.putExtra(Intent.EXTRA_TEXT, ShareUtils.getStringForIntent(activity, + editText!!.text!!.toString(), conversation!!)) + val intent = Intent(shareIntent) + intent.component = ComponentName(packageName, name) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + activity!!.startActivity(intent) + eventBus.post(BottomSheetLockEvent(true, 0, false, true)) + } + } else if (operationCode != 11) { + eventBus.post(BottomSheetLockEvent(false, 0, false, false)) + bundle = Bundle() + bundle.putInt(BundleKeys.KEY_OPERATION_CODE, operationCode) + bundle.putString(BundleKeys.KEY_CALL_URL, editText!!.text!!.toString()) + router.pushController(RouterTransaction.with(OperationsMenuController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } else if (operationCode == 11) { + eventBus.post(BottomSheetLockEvent(false, 0, false, false)) + originalBundle.putString(BundleKeys.KEY_CONVERSATION_NAME, + editText!!.text!!.toString()) + router.pushController( + RouterTransaction.with(OperationsMenuController(originalBundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + if (conversation != null && operationCode == 2) { + editText!!.setText(conversation!!.name) + } + + editText!!.setOnEditorActionListener { v, actionId, event -> + if (actionId == EditorInfo.IME_ACTION_DONE + && proceedButton != null + && proceedButton!!.isEnabled) { + proceedButton!!.callOnClick() + true + } + false + } + + editText!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable) { + if (!TextUtils.isEmpty(s)) { + if (operationCode == 2) { + if (conversation!!.name == null || conversation!!.name != s.toString()) { + if (!proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = true + proceedButton!!.alpha = 1.0f + } + textInputLayout!!.isErrorEnabled = false + } else { + if (proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = false + proceedButton!!.alpha = 0.38f + } + textInputLayout!!.error = resources!!.getString(R.string.nc_call_name_is_same) + } + } else if (operationCode != 10) { + if (!proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = true + proceedButton!!.alpha = 1.0f + } + textInputLayout!!.isErrorEnabled = false + } else if ((editText!!.text!!.toString().startsWith("http://") || editText!!.text!!.toString().startsWith("https://")) && editText!!.text!!.toString().contains("/call/")) { + // operation code 10 + if (!proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = true + proceedButton!!.alpha = 1.0f + } + textInputLayout!!.isErrorEnabled = false + } else { + if (proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = false + proceedButton!!.alpha = 0.38f + } + textInputLayout!!.error = resources!!.getString(R.string.nc_wrong_link) + } + } else { + if (proceedButton!!.isEnabled) { + proceedButton!!.isEnabled = false + proceedButton!!.alpha = 0.38f + } + textInputLayout!!.isErrorEnabled = false + } + } + }) + + var labelText = "" + when (operationCode) { + 11, 2 -> { + labelText = resources!!.getString(R.string.nc_call_name) + editText!!.inputType = InputType.TYPE_CLASS_TEXT + smileyButton!!.visibility = View.VISIBLE + emojiPopup = EmojiPopup.Builder.fromRootView(view) + .setOnEmojiPopupShownListener { + if (resources != null) { + smileyButton!!.setColorFilter(resources!!.getColor(R.color.colorPrimary), + PorterDuff.Mode.SRC_IN) + } + } + .setOnEmojiPopupDismissListener { + if (smileyButton != null) { + smileyButton!!.setColorFilter(resources!!.getColor(R.color.emoji_icons), + PorterDuff.Mode.SRC_IN) + } + } + .setOnEmojiClickListener { emoji, imageView -> editText!!.editableText.append(" ") } + .build(editText!!) + } + 4 -> { + labelText = resources!!.getString(R.string.nc_new_password) + editText!!.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + } + 6, 7, 99 -> { + // 99 is joining a conversation via password + labelText = resources!!.getString(R.string.nc_password) + editText!!.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + } + 10 -> { + labelText = resources!!.getString(R.string.nc_conversation_link) + editText!!.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI + } + else -> { + } + } + + textInputLayout!!.isPasswordVisibilityToggleEnabled = operationCode == 99 || operationCode == 4 || operationCode == 6 || operationCode == 7 + textInputLayout!!.hint = labelText + textInputLayout!!.requestFocus() + } +} 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 deleted file mode 100644 index d12378a00..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.controllers.bottomsheet; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import autodagger.AutoInjector; -import butterknife.BindView; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.R; -import com.nextcloud.talk.activities.MagicCallActivity; -import com.nextcloud.talk.api.NcApi; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.events.BottomSheetLockEvent; -import com.nextcloud.talk.models.RetrofitBucket; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.models.json.capabilities.Capabilities; -import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.conversations.RoomOverall; -import com.nextcloud.talk.models.json.generic.GenericOverall; -import com.nextcloud.talk.models.json.participants.AddParticipantOverall; -import com.nextcloud.talk.utils.ApiUtils; -import com.nextcloud.talk.utils.ConductorRemapping; -import com.nextcloud.talk.utils.DisplayUtils; -import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder; -import io.reactivex.Observer; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import java.io.IOException; -import java.util.ArrayList; -import javax.inject.Inject; -import org.greenrobot.eventbus.EventBus; -import org.parceler.Parcels; -import retrofit2.HttpException; - -public class OperationsMenuController extends BaseController { - - @BindView(R.id.progress_bar) - ProgressBar progressBar; - - @BindView(R.id.result_image_view) - ImageView resultImageView; - - @BindView(R.id.result_text_view) - TextView resultsTextView; - - @BindView(R.id.ok_button) - Button okButton; - - @BindView(R.id.web_button) - Button webButton; - - @Inject - NcApi ncApi; - - @Inject - UserUtils userUtils; - - @Inject - EventBus eventBus; - - private int operationCode; - private Conversation conversation; - - private UserEntity currentUser; - private String callPassword; - private String callUrl; - - private String baseUrl; - private String conversationToken; - - private Disposable disposable; - - private Conversation.ConversationType conversationType; - private ArrayList invitedUsers = new ArrayList<>(); - private ArrayList invitedGroups = new ArrayList<>(); - - private Capabilities serverCapabilities; - private String credentials; - private String conversationName; - - public OperationsMenuController(Bundle args) { - super(); - this.operationCode = args.getInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - } - - this.callPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), ""); - this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS())) { - this.invitedUsers = - args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS()); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_GROUP())) { - this.invitedGroups = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_GROUP()); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())) { - this.conversationType = - Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { - this.serverCapabilities = - Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); - } - - this.conversationName = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), ""); - } - - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_operations_menu, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - processOperation(); - } - - private void processOperation() { - currentUser = userUtils.getCurrentUser(); - OperationsObserver operationsObserver = new OperationsObserver(); - - if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { - conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1); - if (callUrl.contains("/index.php")) { - baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php")); - } else { - baseUrl = callUrl.substring(0, callUrl.indexOf("/call")); - } - } - - if (currentUser != null) { - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); - - if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { - credentials = null; - } - - switch (operationCode) { - case 2: - ncApi.renameRoom(credentials, - ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken()), - conversation.getName()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 3: - ncApi.makeRoomPublic(credentials, - ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation - .getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 4: - case 5: - case 6: - String pass = ""; - if (conversation.getPassword() != null) { - pass = conversation.getPassword(); - } - ncApi.setPassword(credentials, ApiUtils.getUrlForPassword(currentUser.getBaseUrl(), - conversation.getToken()), pass) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 7: - // Operation 7 is sharing, so we handle this differently - break; - case 8: - ncApi.makeRoomPrivate(credentials, - ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation - .getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 10: - ncApi.getRoom(credentials, ApiUtils.getRoom(baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - disposable = d; - } - - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - fetchCapabilities(credentials); - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - break; - case 11: - RetrofitBucket retrofitBucket; - boolean isGroupCallWorkaround = false; - String invite = null; - - if (invitedGroups.size() > 0) { - invite = invitedGroups.get(0); - } - - if (conversationType.equals(Conversation.ConversationType.PUBLIC_CONVERSATION) || - !currentUser.hasSpreedFeatureCapability("empty-group-room")) { - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), - "3", invite, conversationName); - } else { - String roomType = "2"; - if (!currentUser.hasSpreedFeatureCapability("empty-group-room")) { - isGroupCallWorkaround = true; - roomType = "3"; - } - - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.getBaseUrl(), - roomType, invite, conversationName); - } - - final boolean isGroupCallWorkaroundFinal = isGroupCallWorkaround; - ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - - ncApi.getRoom(credentials, - ApiUtils.getRoom(currentUser.getBaseUrl(), conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - if (conversationType.equals( - Conversation.ConversationType.PUBLIC_CONVERSATION) - && isGroupCallWorkaroundFinal) { - performGroupCallWorkaround(credentials); - } else { - inviteUsersToAConversation(); - } - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - - } - }); - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - - break; - case 97: - case 98: - if (operationCode == 97) { - ncApi.removeConversationFromFavorites(credentials, - ApiUtils.getUrlForConversationFavorites(currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - } else { - ncApi.addConversationToFavorites(credentials, - ApiUtils.getUrlForConversationFavorites(currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - } - break; - case 99: - ncApi.joinRoom(credentials, - ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, conversationToken), - callPassword) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - default: - break; - } - } - } - - private void performGroupCallWorkaround(String credentials) { - ncApi.makeRoomPrivate(credentials, - ApiUtils.getUrlForRoomVisibility(currentUser.getBaseUrl(), conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(GenericOverall genericOverall) { - inviteUsersToAConversation(); - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - } - - private void showResultImage(boolean everythingOK, boolean isGuestSupportError) { - progressBar.setVisibility(View.GONE); - - if (everythingOK) { - resultImageView.setImageDrawable(DisplayUtils.INSTANCE.getTintedDrawable(getResources(), R.drawable - .ic_check_circle_black_24dp, R.color.nc_darkGreen)); - } else { - resultImageView.setImageDrawable(DisplayUtils.INSTANCE.getTintedDrawable(getResources(), R.drawable - .ic_cancel_black_24dp, R.color.nc_darkRed)); - } - - resultImageView.setVisibility(View.VISIBLE); - - if (everythingOK) { - resultsTextView.setText(R.string.nc_all_ok_operation); - } else { - resultsTextView.setTextColor(getResources().getColor(R.color.nc_darkRed)); - if (!isGuestSupportError) { - resultsTextView.setText(R.string.nc_failed_to_perform_operation); - } else { - resultsTextView.setText(R.string.nc_failed_signaling_settings); - webButton.setOnClickListener(v -> { - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(callUrl)); - startActivity(browserIntent); - new BottomSheetLockEvent(true, 0, false, true); - }); - webButton.setVisibility(View.VISIBLE); - } - } - - resultsTextView.setVisibility(View.VISIBLE); - if (everythingOK) { - eventBus.post(new BottomSheetLockEvent(true, 2500, true, true)); - } else { - resultImageView.setImageDrawable(DisplayUtils.INSTANCE.getTintedDrawable(getResources(), R.drawable - .ic_cancel_black_24dp, R.color.nc_darkRed)); - okButton.setOnClickListener( - v -> eventBus.post(new BottomSheetLockEvent(true, 0, operationCode != 99 - && operationCode != 10, true))); - okButton.setVisibility(View.VISIBLE); - } - } - - private void dispose() { - if (disposable != null && !disposable.isDisposed()) { - disposable.dispose(); - } - - disposable = null; - } - - @Override - public void onDestroy() { - super.onDestroy(); - dispose(); - } - - private void fetchCapabilities(String credentials) { - ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(CapabilitiesOverall capabilitiesOverall) { - if (capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("chat-v2")) { - if (conversation.getHasPassword() && conversation.isGuest()) { - eventBus.post(new BottomSheetLockEvent(true, 0, - true, false)); - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), - Parcels.wrap(capabilitiesOverall.getOcs().getData().getCapabilities())); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); - getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - initiateConversation(false, capabilitiesOverall.getOcs().getData() - .getCapabilities()); - } - } else if (capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("guest-signaling")) { - initiateCall(); - } else { - showResultImage(false, true); - } - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - } - - @Override - public void onComplete() { - - } - }); - } - - private void inviteUsersToAConversation() { - RetrofitBucket retrofitBucket; - final ArrayList localInvitedUsers = invitedUsers; - final ArrayList localInvitedGroups = invitedGroups; - if (localInvitedGroups.size() > 0) { - localInvitedGroups.remove(0); - } - - if (localInvitedUsers.size() > 0 || (localInvitedGroups.size() > 0 - && currentUser.hasSpreedFeatureCapability("invite-groups-and-mails"))) { - if ((localInvitedGroups.size() > 0 && currentUser.hasSpreedFeatureCapability( - "invite-groups-and-mails"))) { - for (int i = 0; i < localInvitedGroups.size(); i++) { - final String groupId = localInvitedGroups.get(i); - retrofitBucket = - ApiUtils.getRetrofitBucketForAddGroupParticipant(currentUser.getBaseUrl(), - conversation.getToken(), - groupId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(AddParticipantOverall addParticipantOverall) { - } - - @Override - public void onError(Throwable e) { - dispose(); - } - - @Override - public void onComplete() { - synchronized (localInvitedGroups) { - localInvitedGroups.remove(groupId); - } - - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true, null); - } - dispose(); - } - }); - } - } - - for (int i = 0; i < localInvitedUsers.size(); i++) { - final String userId = invitedUsers.get(i); - retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(currentUser.getBaseUrl(), - conversation.getToken(), - userId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(AddParticipantOverall addParticipantOverall) { - } - - @Override - public void onError(Throwable e) { - dispose(); - } - - @Override - public void onComplete() { - synchronized (localInvitedUsers) { - localInvitedUsers.remove(userId); - } - - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true, null); - } - dispose(); - } - }); - } - } else { - if (!currentUser.hasSpreedFeatureCapability("chat-v2")) { - showResultImage(true, false); - } else { - initiateConversation(true, null); - } - } - } - - private void initiateConversation(boolean dismissView, @Nullable Capabilities capabilities) { - Bundle bundle = new Bundle(); - boolean isGuestUser = false; - boolean hasChatCapability; - - if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { - isGuestUser = true; - hasChatCapability = capabilities != null - && capabilities.getSpreedCapability() != null - && capabilities.getSpreedCapability().getFeatures() != null - && capabilities.getSpreedCapability().getFeatures().contains("chat-v2"); - } else { - hasChatCapability = currentUser.hasSpreedFeatureCapability("chat-v2"); - } - - if (hasChatCapability) { - eventBus.post(new BottomSheetLockEvent(true, 0, - true, true, dismissView)); - - Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getConversationId()); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), - conversation.getDisplayName()); - UserEntity conversationUser; - if (isGuestUser) { - conversationUser = new UserEntity(); - conversationUser.setBaseUrl(baseUrl); - conversationUser.setUserId("?"); - try { - conversationUser.setCapabilities(LoganSquare.serialize(capabilities)); - } catch (IOException e) { - Log.e("OperationsMenu", "Failed to serialize capabilities"); - } - } else { - conversationUser = currentUser; - } - - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), conversationUser); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), - Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), callPassword); - - conversationIntent.putExtras(bundle); - - if (getParentController() != null) { - ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), - conversationUser.getId(), - conversation.getToken(), bundle, true); - } - } else { - initiateCall(); - } - } - - private void initiateCall() { - eventBus.post(new BottomSheetLockEvent(true, 0, true, true)); - Bundle bundle = new Bundle(); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { - bundle.putString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), baseUrl); - } - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), - Parcels.wrap(conversation)); - - if (getActivity() != null) { - - Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); - callIntent.putExtras(bundle); - - InputMethodManager imm = - (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE); - if (imm != null) { - imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); - } - - new Handler().postDelayed(() -> getParentController().getRouter().popCurrentController(), - 100); - startActivity(callIntent); - } - } - - private class OperationsObserver implements Observer { - - @Override - public void onSubscribe(Disposable d) { - disposable = d; - } - - @Override - public void onNext(Object o) { - if (operationCode != 99) { - showResultImage(true, false); - } else { - RoomOverall roomOverall = (RoomOverall) o; - conversation = roomOverall.getOcs().getData(); - initiateConversation(true, serverCapabilities); - } - } - - @Override - public void onError(Throwable e) { - if (operationCode != 99 || !(e instanceof HttpException)) { - showResultImage(false, false); - } else { - if (((HttpException) e).response().code() == 403) { - eventBus.post(new BottomSheetLockEvent(true, 0, false, - false)); - ApplicationWideMessageHolder.getInstance() - .setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG); - getRouter().popCurrentController(); - } else { - showResultImage(false, false); - } - } - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt new file mode 100644 index 000000000..b650678f7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt @@ -0,0 +1,662 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.controllers.bottomsheet + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.text.TextUtils +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.Button +import android.widget.ImageView +import android.widget.ProgressBar +import android.widget.TextView +import butterknife.BindView +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler +import com.bluelinelabs.logansquare.LoganSquare +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.MagicCallActivity +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.events.BottomSheetLockEvent +import com.nextcloud.talk.models.RetrofitBucket +import com.nextcloud.talk.models.json.capabilities.Capabilities +import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall +import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.newarch.local.models.getCredentials +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.ConductorRemapping +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.database.user.UserUtils +import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import java.io.IOException +import java.util.ArrayList +import org.greenrobot.eventbus.EventBus +import org.koin.android.ext.android.inject +import org.parceler.Parcels +import retrofit2.HttpException + +class OperationsMenuController(args: Bundle) : BaseController() { + + @JvmField @BindView(R.id.progress_bar) + internal var progressBar: ProgressBar? = null + + @JvmField @BindView(R.id.result_image_view) + internal var resultImageView: ImageView? = null + + @JvmField @BindView(R.id.result_text_view) + internal var resultsTextView: TextView? = null + + @JvmField @BindView(R.id.ok_button) + internal var okButton: Button? = null + + @JvmField @BindView(R.id.web_button) + internal var webButton: Button? = null + + val ncApi: NcApi by inject() + val usersRepository: UsersRepository by inject() + + private val operationCode: Int + private var conversation: Conversation? = null + + private var currentUser: UserNgEntity? = null + private val callPassword: String + private val callUrl: String + + private var baseUrl: String? = null + private var conversationToken: String? = null + + private var disposable: Disposable? = null + + private var conversationType: Conversation.ConversationType? = null + private var invitedUsers: ArrayList? = ArrayList() + private var invitedGroups: ArrayList? = ArrayList() + + private var serverCapabilities: Capabilities? = null + private var credentials: String? = null + private val conversationName: String + + + init { + this.operationCode = args.getInt(BundleKeys.KEY_OPERATION_CODE) + if (args.containsKey(BundleKeys.KEY_ROOM)) { + this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_ROOM)) + } + + this.callPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "") + this.callUrl = args.getString(BundleKeys.KEY_CALL_URL, "") + + if (args.containsKey(BundleKeys.KEY_INVITED_PARTICIPANTS)) { + this.invitedUsers = args.getStringArrayList(BundleKeys.KEY_INVITED_PARTICIPANTS) + } + + if (args.containsKey(BundleKeys.KEY_INVITED_GROUP)) { + this.invitedGroups = args.getStringArrayList(BundleKeys.KEY_INVITED_GROUP) + } + + if (args.containsKey(BundleKeys.KEY_CONVERSATION_TYPE)) { + this.conversationType = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_CONVERSATION_TYPE)) + } + + if (args.containsKey(BundleKeys.KEY_SERVER_CAPABILITIES)) { + this.serverCapabilities = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_SERVER_CAPABILITIES)) + } + + this.conversationName = args.getString(BundleKeys.KEY_CONVERSATION_NAME, "") + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.controller_operations_menu, container, false) + } + + override fun onViewBound(view: View) { + super.onViewBound(view) + processOperation() + } + + private fun processOperation() { + currentUser = usersRepository.getActiveUser() + val operationsObserver = OperationsObserver() + + if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { + conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1) + if (callUrl.contains("/index.php")) { + baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php")) + } else { + baseUrl = callUrl.substring(0, callUrl.indexOf("/call")) + } + } + + if (currentUser != null) { + credentials = currentUser!!.getCredentials() + + if (!TextUtils.isEmpty(baseUrl) && baseUrl != currentUser!!.baseUrl) { + credentials = null + } + + when (operationCode) { + 2 -> ncApi.renameRoom(credentials, + ApiUtils.getRoom(currentUser!!.baseUrl, conversation!!.token), + conversation!!.name) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + 3 -> ncApi.makeRoomPublic(credentials, + ApiUtils.getUrlForRoomVisibility(currentUser!!.baseUrl, conversation!! + .token)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + 4, 5, 6 -> { + var pass: String? = "" + if (conversation!!.password != null) { + pass = conversation!!.password + } + ncApi.setPassword(credentials, ApiUtils.getUrlForPassword(currentUser!!.baseUrl, + conversation!!.token), pass) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + } + 7 -> { + } + 8 -> ncApi.makeRoomPrivate(credentials, + ApiUtils.getUrlForRoomVisibility(currentUser!!.baseUrl, conversation!! + .token)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + 10 -> ncApi.getRoom(credentials, ApiUtils.getRoom(baseUrl, conversationToken)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + disposable = d + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.ocs.data + fetchCapabilities(credentials) + } + + override fun onError(e: Throwable) { + showResultImage(false, false) + dispose() + } + + override fun onComplete() { + dispose() + } + }) + 11 -> { + val retrofitBucket: RetrofitBucket + var isGroupCallWorkaround = false + var invite: String? = null + + if (invitedGroups!!.size > 0) { + invite = invitedGroups!![0] + } + + if (conversationType == Conversation.ConversationType.PUBLIC_CONVERSATION || !currentUser!!.hasSpreedFeatureCapability("empty-group-room")) { + retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser!!.baseUrl, + "3", invite, conversationName) + } else { + var roomType = "2" + if (!currentUser!!.hasSpreedFeatureCapability("empty-group-room")) { + isGroupCallWorkaround = true + roomType = "3" + } + + retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser!!.baseUrl, + roomType, invite, conversationName) + } + + val isGroupCallWorkaroundFinal = isGroupCallWorkaround + ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.ocs.data + + ncApi.getRoom(credentials, + ApiUtils.getRoom(currentUser!!.baseUrl, conversation!!.token)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.ocs.data + if (conversationType == Conversation.ConversationType.PUBLIC_CONVERSATION && isGroupCallWorkaroundFinal) { + performGroupCallWorkaround(credentials) + } else { + inviteUsersToAConversation() + } + } + + override fun onError(e: Throwable) { + showResultImage(false, false) + dispose() + } + + override fun onComplete() { + + } + }) + } + + override fun onError(e: Throwable) { + showResultImage(false, false) + dispose() + } + + override fun onComplete() { + dispose() + } + }) + } + 97, 98 -> if (operationCode == 97) { + ncApi.removeConversationFromFavorites(credentials, + ApiUtils.getUrlForConversationFavorites(currentUser!!.baseUrl, + conversation!!.token)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + } else { + ncApi.addConversationToFavorites(credentials, + ApiUtils.getUrlForConversationFavorites(currentUser!!.baseUrl, + conversation!!.token)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + } + 99 -> ncApi.joinRoom(credentials, + ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, conversationToken), + callPassword) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver) + else -> { + } + }// Operation 7 is sharing, so we handle this differently + } + } + + private fun performGroupCallWorkaround(credentials: String?) { + ncApi.makeRoomPrivate(credentials, + ApiUtils.getUrlForRoomVisibility(currentUser!!.baseUrl, conversation!!.token)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(genericOverall: GenericOverall) { + inviteUsersToAConversation() + } + + override fun onError(e: Throwable) { + showResultImage(false, false) + dispose() + } + + override fun onComplete() { + dispose() + } + }) + } + + private fun showResultImage(everythingOK: Boolean, isGuestSupportError: Boolean) { + progressBar!!.visibility = View.GONE + + if (everythingOK) { + resultImageView!!.setImageDrawable(DisplayUtils.getTintedDrawable(resources!!, R.drawable + .ic_check_circle_black_24dp, R.color.nc_darkGreen)) + } else { + resultImageView!!.setImageDrawable(DisplayUtils.getTintedDrawable(resources!!, R.drawable + .ic_cancel_black_24dp, R.color.nc_darkRed)) + } + + resultImageView!!.visibility = View.VISIBLE + + if (everythingOK) { + resultsTextView!!.setText(R.string.nc_all_ok_operation) + } else { + resultsTextView!!.setTextColor(resources!!.getColor(R.color.nc_darkRed)) + if (!isGuestSupportError) { + resultsTextView!!.setText(R.string.nc_failed_to_perform_operation) + } else { + resultsTextView!!.setText(R.string.nc_failed_signaling_settings) + webButton!!.setOnClickListener { v -> + eventBus.post(BottomSheetLockEvent(true, 0, false, true)) + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(callUrl)) + startActivity(browserIntent) + BottomSheetLockEvent(true, 0, false, true) + } + webButton!!.visibility = View.VISIBLE + } + } + + resultsTextView!!.visibility = View.VISIBLE + if (everythingOK) { + eventBus.post(BottomSheetLockEvent(true, 2500, true, true)) + } else { + resultImageView!!.setImageDrawable(DisplayUtils.getTintedDrawable(resources!!, R.drawable + .ic_cancel_black_24dp, R.color.nc_darkRed)) + okButton!!.setOnClickListener { v -> eventBus.post(BottomSheetLockEvent(true, 0, operationCode != 99 && operationCode != 10, true)) } + okButton!!.visibility = View.VISIBLE + } + } + + private fun dispose() { + if (disposable != null && !disposable!!.isDisposed) { + disposable!!.dispose() + } + + disposable = null + } + + public override fun onDestroy() { + super.onDestroy() + dispose() + } + + private fun fetchCapabilities(credentials: String?) { + ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(capabilitiesOverall: CapabilitiesOverall) { + val hasChatV2Capability = capabilitiesOverall.ocs?.data?.capabilities?.spreedCapability?.features?.contains("chat-v2") == true + val hasGuestSignaling = capabilitiesOverall.ocs?.data?.capabilities?.spreedCapability?.features?.contains("guest-signaling") == true + + if (hasChatV2Capability) { + if (conversation!!.hasPassword && conversation!!.isGuest) { + eventBus.post(BottomSheetLockEvent(true, 0, + true, false)) + val bundle = Bundle() + bundle.putParcelable(BundleKeys.KEY_ROOM, Parcels.wrap(conversation)) + bundle.putString(BundleKeys.KEY_CALL_URL, callUrl) + bundle.putParcelable(BundleKeys.KEY_SERVER_CAPABILITIES, + Parcels.wrap(capabilitiesOverall.ocs.data.capabilities)) + bundle.putInt(BundleKeys.KEY_OPERATION_CODE, 99) + router.pushController(RouterTransaction.with(EntryMenuController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler())) + } else { + initiateConversation(false, capabilitiesOverall.ocs.data.capabilities) + } + + } else if (hasGuestSignaling) { + initiateCall() + } else { + showResultImage(false, true) + + } + } + + override fun onError(e: Throwable) { + showResultImage(false, false) + } + + override fun onComplete() { + + } + }) + } + + private fun inviteUsersToAConversation() { + var retrofitBucket: RetrofitBucket + val localInvitedUsers = invitedUsers + val localInvitedGroups = invitedGroups + if (localInvitedGroups!!.size > 0) { + localInvitedGroups.removeAt(0) + } + + if (localInvitedUsers!!.size > 0 || localInvitedGroups.size > 0 && currentUser!!.hasSpreedFeatureCapability("invite-groups-and-mails")) { + if (localInvitedGroups.size > 0 && currentUser!!.hasSpreedFeatureCapability( + "invite-groups-and-mails")) { + for (i in localInvitedGroups.indices) { + val groupId = localInvitedGroups[i] + retrofitBucket = ApiUtils.getRetrofitBucketForAddGroupParticipant(currentUser!!.baseUrl, + conversation!!.token, + groupId) + + ncApi.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(addParticipantOverall: AddParticipantOverall) {} + + override fun onError(e: Throwable) { + dispose() + } + + override fun onComplete() { + synchronized(localInvitedGroups) { + localInvitedGroups.remove(groupId) + } + + if (localInvitedGroups.size == 0 && localInvitedUsers.size == 0) { + initiateConversation(true, null) + } + dispose() + } + }) + } + } + + for (i in localInvitedUsers.indices) { + val userId = invitedUsers!![i] + retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(currentUser!!.baseUrl, + conversation!!.token, + userId) + + ncApi.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + + } + + override fun onNext(addParticipantOverall: AddParticipantOverall) {} + + override fun onError(e: Throwable) { + dispose() + } + + override fun onComplete() { + synchronized(localInvitedUsers) { + localInvitedUsers.remove(userId) + } + + if (localInvitedGroups.size == 0 && localInvitedUsers.size == 0) { + initiateConversation(true, null) + } + dispose() + } + }) + } + } else { + if (!currentUser!!.hasSpreedFeatureCapability("chat-v2")) { + showResultImage(true, false) + } else { + initiateConversation(true, null) + } + } + } + + private fun initiateConversation(dismissView: Boolean, capabilities: Capabilities?) { + val bundle = Bundle() + var isGuestUser = false + val hasChatCapability: Boolean + + if (baseUrl != null && baseUrl != currentUser!!.baseUrl) { + isGuestUser = true + hasChatCapability = capabilities?.spreedCapability?.features?.contains("chat-v2") == true + } else { + hasChatCapability = currentUser!!.hasSpreedFeatureCapability("chat-v2") + } + + if (hasChatCapability) { + eventBus.post(BottomSheetLockEvent(true, 0, + true, true, dismissView)) + + val conversationIntent = Intent(activity, MagicCallActivity::class.java) + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation!!.token) + bundle.putString(BundleKeys.KEY_ROOM_ID, conversation!!.conversationId) + bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, + conversation!!.displayName) + val conversationUser: UserNgEntity + if (isGuestUser) { + conversationUser = UserNgEntity(-1, "?", "?", baseUrl!!) + conversationUser.capabilities = capabilities!! + } else { + conversationUser = currentUser!! + } + + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser) + bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, + Parcels.wrap(conversation)) + bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, callPassword) + + conversationIntent.putExtras(bundle) + + if (parentController != null) { + ConductorRemapping.remapChatController(parentController!!.router, + conversationUser.id!!, + conversation!!.token!!, bundle, true) + } + } else { + initiateCall() + } + } + + private fun initiateCall() { + eventBus.post(BottomSheetLockEvent(true, 0, true, true)) + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation!!.token) + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser) + if (baseUrl != null && baseUrl != currentUser!!.baseUrl) { + bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, baseUrl) + } + bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, + Parcels.wrap(conversation)) + + if (activity != null) { + + val callIntent = Intent(activity, MagicCallActivity::class.java) + callIntent.putExtras(bundle) + + val imm = activity!!.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + imm?.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0) + + Handler().postDelayed({ parentController!!.router.popCurrentController() }, + 100) + startActivity(callIntent) + } + } + + private inner class OperationsObserver : Observer { + + override fun onSubscribe(d: Disposable) { + disposable = d + } + + override fun onNext(o: Any) { + if (operationCode != 99) { + showResultImage(true, false) + } else { + val roomOverall = o as RoomOverall + conversation = roomOverall.ocs.data + initiateConversation(true, serverCapabilities) + } + } + + override fun onError(e: Throwable) { + if (operationCode != 99 || e !is HttpException) { + showResultImage(false, false) + } else { + if (e.response()!!.code() == 403) { + eventBus.post(BottomSheetLockEvent(true, 0, false, + false)) + ApplicationWideMessageHolder.getInstance() + .setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG) + router.popCurrentController() + } else { + showResultImage(false, false) + } + } + dispose() + } + + override fun onComplete() { + dispose() + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt index 69276fc0e..ecca93dd7 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/CapabilitiesWorker.kt @@ -22,7 +22,6 @@ package com.nextcloud.talk.jobs import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import autodagger.AutoInjector import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.events.EventStatus @@ -43,7 +42,6 @@ import retrofit2.Retrofit import java.net.CookieManager import java.util.* -@AutoInjector(NextcloudTalkApplication::class) class CapabilitiesWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { val retrofit: Retrofit by inject() val eventBus: EventBus by inject() diff --git a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.kt index f531d50c5..9ef2ddb3a 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.kt @@ -22,7 +22,6 @@ package com.nextcloud.talk.jobs import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import autodagger.AutoInjector import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication @@ -45,7 +44,6 @@ import org.koin.core.inject import retrofit2.Retrofit import java.net.CookieManager -@AutoInjector(NextcloudTalkApplication::class) class DeleteConversationWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { val retrofit: Retrofit by inject() @@ -84,10 +82,4 @@ class DeleteConversationWorker(context: Context, } return Result.success() } - - init { - sharedApplication - ?.componentApplication - ?.inject(this) - } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt index cfacfcbee..4c8e0747b 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.kt @@ -22,7 +22,6 @@ package com.nextcloud.talk.jobs import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters -import autodagger.AutoInjector import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.events.EventStatus @@ -44,7 +43,6 @@ import org.koin.core.inject import retrofit2.Retrofit import java.net.CookieManager -@AutoInjector(NextcloudTalkApplication::class) class LeaveConversationWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams), KoinComponent { val retrofit: Retrofit by inject() val okHttpClient: OkHttpClient by inject() diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 926104821..c2c4d00af 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -47,22 +47,16 @@ import androidx.core.app.Person import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap import androidx.emoji.text.EmojiCompat -import androidx.work.ListenableWorker.Result import androidx.work.Worker import androidx.work.WorkerParameters import coil.Coil import coil.target.Target import coil.transform.CircleCropTransformation import com.bluelinelabs.logansquare.LoganSquare -import com.nextcloud.talk.R.color -import com.nextcloud.talk.R.dimen -import com.nextcloud.talk.R.drawable -import com.nextcloud.talk.R.string +import com.nextcloud.talk.R.* import com.nextcloud.talk.activities.MagicCallActivity import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.api.NcApi -import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.models.RingtoneSettings import com.nextcloud.talk.models.SignatureVerification import com.nextcloud.talk.models.database.ArbitraryStorageEntity @@ -77,7 +71,6 @@ import com.nextcloud.talk.models.json.push.NotificationUser import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.getCredentials -import com.nextcloud.talk.newarch.local.models.hasSpreedFeatureCapability import com.nextcloud.talk.newarch.utils.Images import com.nextcloud.talk.newarch.utils.getCredentials import com.nextcloud.talk.utils.ApiUtils @@ -116,7 +109,7 @@ import java.net.CookieManager import java.security.InvalidKeyException import java.security.NoSuchAlgorithmException import java.security.PrivateKey -import java.util.HashMap +import java.util.* import java.util.function.Consumer import java.util.zip.CRC32 import javax.crypto.Cipher diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt index 9830a2971..b19acb3ab 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt @@ -52,7 +52,7 @@ class ShareOperationWorker( private val baseUrl: String override fun doWork(): Result { for (i in filesArray.indices) { - ncApi!!.createRemoteShare( + ncApi.createRemoteShare( credentials, ApiUtils.getSharingUrl(baseUrl), filesArray[i], diff --git a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt index fd8d6371d..f5ae1a586 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.kt @@ -24,7 +24,6 @@ import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager import androidx.work.WorkerParameters -import autodagger.AutoInjector import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication @@ -44,7 +43,6 @@ import org.greenrobot.eventbus.EventBus import org.koin.core.KoinComponent import org.koin.core.inject import java.util.* -import javax.inject.Inject class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { val ncApi: NcApi by inject() @@ -65,7 +63,7 @@ class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters) for (i in userEntityList.indices) { userEntity = userEntityList[i] val finalUserEntity: UserNgEntity? = userEntity - ncApi!!.getSignalingSettings( + ncApi.getSignalingSettings( userEntity!!.getCredentials(), ApiUtils.getUrlForSignalingSettings(userEntity.baseUrl)) .blockingSubscribe(object : Observer { diff --git a/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.kt index c3dd56272..7a6867955 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.kt @@ -27,11 +27,9 @@ import android.util.Log import androidx.work.ListenableWorker import androidx.work.Worker import androidx.work.WorkerParameters -import autodagger.AutoInjector import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.models.ExternalSignalingServer -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.utils.database.user.UserUtils @@ -39,9 +37,7 @@ import com.nextcloud.talk.webrtc.WebSocketConnectionHelper import org.koin.core.KoinComponent import org.koin.core.inject import java.io.IOException -import javax.inject.Inject -@AutoInjector(NextcloudTalkApplication::class) class WebsocketConnectionsWorker( context: Context, workerParams: WorkerParameters @@ -50,10 +46,6 @@ class WebsocketConnectionsWorker( val usersRepository: UsersRepository by inject() override fun doWork(): Result { - NextcloudTalkApplication.sharedApplication!! - .componentApplication - .inject(this) - val userEntityList = usersRepository.getUsers() var userEntity: UserNgEntity for (i in userEntityList.indices) { diff --git a/app/src/main/java/com/nextcloud/talk/models/database/User.java b/app/src/main/java/com/nextcloud/talk/models/database/User.java index 98b3411d7..f6b4f3b56 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/User.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/User.java @@ -60,76 +60,5 @@ public interface User extends Parcelable, Persistable, Serializable { boolean getCurrent(); boolean getScheduledForDeletion(); - - default boolean hasNotificationsCapability(String capabilityName) { - if (getCapabilities() != null) { - try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities.getNotificationsCapability() != null - && capabilities.getNotificationsCapability().getFeatures() != null) { - return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); - } - } catch (IOException e) { - Log.e(TAG, "Failed to get capabilities for the user"); - } - } - return false; - } - - default boolean hasExternalCapability(String capabilityName) { - if (getCapabilities() != null) { - try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities.getExternalCapability() != null && capabilities.getExternalCapability() - .containsKey("v1")) { - return capabilities.getExternalCapability().get("v1").contains("capabilityName"); - } - } catch (IOException e) { - Log.e(TAG, "Failed to get capabilities for the user"); - } - } - return false; - } - - default boolean hasSpreedFeatureCapability(String capabilityName) { - if (getCapabilities() != null) { - try { - Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities != null && capabilities.getSpreedCapability() != null && - capabilities.getSpreedCapability().getFeatures() != null) { - return capabilities.getSpreedCapability().getFeatures().contains(capabilityName); - } - } catch (IOException e) { - Log.e(TAG, "Failed to get capabilities for the user"); - } - } - return false; - } - - default int getMessageMaxLength() { - if (getCapabilities() != null) { - Capabilities capabilities = null; - try { - capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities != null - && capabilities.getSpreedCapability() != null - && capabilities.getSpreedCapability().getConfig() != null - && capabilities.getSpreedCapability().getConfig().containsKey("chat")) { - HashMap chatConfigHashMap = - capabilities.getSpreedCapability().getConfig().get("chat"); - if (chatConfigHashMap != null && chatConfigHashMap.containsKey("max-length")) { - int chatSize = Integer.parseInt(chatConfigHashMap.get("max-length")); - if (chatSize > 0) { - return chatSize; - } else { - return 1000; - } - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - return 1000; - } } + diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java index 4788f0ab2..6df8bf102 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/NotificationsCapability.java @@ -31,5 +31,5 @@ import org.parceler.Parcel; @JsonObject public class NotificationsCapability { @JsonField(name = "ocs-endpoints") - List features; + public List features; } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt index f7a5b70c0..62b9d00fd 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt @@ -22,6 +22,7 @@ package com.nextcloud.talk.newarch.data.repository.online import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.newarch.data.source.remote.ApiService import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository @@ -52,6 +53,10 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou ) } + override suspend fun getConversationForUser(userEntity: UserNgEntity, conversationToken: String): RoomOverall { + return apiService.getConversation(userEntity.getCredentials(), conversationToken) + } + override suspend fun setFavoriteValueForConversation( user: UserNgEntity, conversation: Conversation, diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt index 9e2b8de99..0c0dc7492 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt @@ -62,5 +62,5 @@ interface ApiService { ): GenericOverall @GET - suspend fun getRoom(@Header("Authorization") authorization: String, @Url url: String): RoomOverall + suspend fun getConversation(@Header("Authorization") authorization: String, @Url url: String): RoomOverall } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/di/module/StorageModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/di/module/StorageModule.kt index 220ce535b..640d34cff 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/di/module/StorageModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/di/module/StorageModule.kt @@ -54,7 +54,7 @@ val StorageModule = module { single { createMessagesRepository(get()) } single { createUsersRepository(get()) } single { createArbitraryStorageUtils(get()) } - + single { createUserUtils(get()) } single { TalkDatabase.getInstance(androidApplication()) } single { get().conversationsDao() } single { get().messagesDao() } @@ -92,4 +92,8 @@ fun createDataStore(sqlCipherDatabaseSource: SqlCipherDatabaseSource): ReactiveE fun createArbitraryStorageUtils(dataStore: ReactiveEntityStore): ArbitraryStorageUtils { return ArbitraryStorageUtils(dataStore) +} + +fun createUserUtils(dataStore: ReactiveEntityStore): UserUtils { + return UserUtils(dataStore) } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt index 33c17804e..2f630d7a7 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt @@ -44,8 +44,8 @@ interface NextcloudTalkRepository { conversation: Conversation ): GenericOverall - suspend fun getRoomForUser( + suspend fun getConversationForUser( userEntity: UserNgEntity, - roomToken: String + conversationToken: String ): RoomOverall } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetRoomUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetConversationUseCase.kt similarity index 81% rename from app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetRoomUseCase.kt rename to app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetConversationUseCase.kt index a8abe65f7..0a7caade4 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetRoomUseCase.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetConversationUseCase.kt @@ -6,12 +6,12 @@ import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkReposito import com.nextcloud.talk.newarch.domain.usecases.base.UseCase import org.koin.core.parameter.DefinitionParameters -class GetRoomUseCase constructor( +class GetConversationUseCase constructor( private val nextcloudTalkRepository: NextcloudTalkRepository, apiErrorHandler: ApiErrorHandler? ) : UseCase(apiErrorHandler) { override suspend fun run(params: Any?): RoomOverall { val definitionParameters = params as DefinitionParameters - return nextcloudTalkRepository.getRoomForUser(definitionParameters[0], definitionParameters[1]) + return nextcloudTalkRepository.getConversationForUser(definitionParameters[0], definitionParameters[1]) } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt index 6e090daad..b85dc19f7 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/MessagesDao.kt @@ -32,7 +32,7 @@ import com.nextcloud.talk.newarch.local.models.MessageEntity @Dao abstract class MessagesDao { - @Query("SELECT * FROM messages WHERE conversation = :conversationId") + @Query("SELECT * FROM messages WHERE conversation_id = :conversationId") abstract fun getMessagesWithUserForConversation(conversationId: String): LiveData> diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt index a76d96e8c..205d37c04 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt @@ -35,7 +35,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType @Entity( tableName = "messages", - indices = [Index(value = ["conversation"])], + indices = [Index(value = ["conversation_id"])], foreignKeys = [ForeignKey( entity = ConversationEntity::class, parentColumns = arrayOf("id"), diff --git a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.kt b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.kt index 57ec2e11f..c8657af5b 100644 --- a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.kt +++ b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.kt @@ -76,7 +76,7 @@ class MentionAutocompletePresenter : RecyclerViewPresenter, FlexibleAd "" } adapter!!.setFilter(queryString) - ncApi!!.getMentionAutocompleteSuggestions( + ncApi.getMentionAutocompleteSuggestions( currentUser!!.getCredentials(), ApiUtils.getUrlForMentionSuggestions(currentUser!!.baseUrl, roomToken), queryString, 5) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt index c4207da2b..f121f356d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt @@ -215,7 +215,7 @@ class PushUtils(val usersRepository: UsersRepository): KoinComponent { userEntity.username, userEntity.token ) val finalCredentials = credentials - ncApi!!.registerDeviceForNotificationsWithNextcloud( + ncApi.registerDeviceForNotificationsWithNextcloud( credentials, ApiUtils.getUrlNextcloudPush(userEntity.baseUrl), queryMap @@ -232,7 +232,7 @@ class PushUtils(val usersRepository: UsersRepository): KoinComponent { .data.signature proxyMap["userPublicKey"] = pushRegistrationOverall.ocs .data.publicKey - ncApi!!.registerDeviceForNotificationsWithProxy( + ncApi.registerDeviceForNotificationsWithProxy( ApiUtils.getUrlPushProxy(), proxyMap ) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java b/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java index 1c7b46eab..8dae76553 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/database/arbitrarystorage/ArbitraryStorageUtils.java @@ -22,6 +22,7 @@ package com.nextcloud.talk.utils.database.arbitrarystorage; import androidx.annotation.Nullable; import com.nextcloud.talk.models.database.ArbitraryStorage; import com.nextcloud.talk.models.database.ArbitraryStorageEntity; + import io.reactivex.Observable; import io.reactivex.schedulers.Schedulers; import io.requery.Persistable; diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java b/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java index 7378fccc2..28119c48e 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/UserUtils.java @@ -24,9 +24,8 @@ import android.text.TextUtils; import androidx.annotation.Nullable; import com.nextcloud.talk.models.database.User; import com.nextcloud.talk.models.database.UserEntity; -import io.reactivex.Completable; + import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import io.requery.Persistable; import io.requery.query.Result; @@ -52,29 +51,6 @@ public class UserUtils { return findUsersQueryResult.toList(); } - public List getUsersScheduledForDeletion() { - Result findUsersQueryResult = - dataStore.select(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.eq(true)) - .get(); - - return findUsersQueryResult.toList(); - } - - public UserEntity getAnyUserAndSetAsActive() { - Result findUserQueryResult = dataStore.select(User.class) - .where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)) - .limit(1).get(); - - UserEntity userEntity; - if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { - userEntity.setCurrent(true); - dataStore.update(userEntity).blockingGet(); - return userEntity; - } - - return null; - } - public UserEntity getCurrentUser() { Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(true) .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) @@ -83,33 +59,6 @@ public class UserUtils { return (UserEntity) findUserQueryResult.firstOrNull(); } - public Completable deleteUser(long internalId) { - Result findUserQueryResult = - dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).limit(1).get(); - - UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); - - return dataStore.delete(user) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - public Completable deleteUserWithId(long id) { - Result findUserQueryResult = - dataStore.select(User.class).where(UserEntity.ID.eq(id)).limit(1).get(); - - UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); - - return dataStore.delete(user) - .subscribeOn(Schedulers.io()); - } - - public UserEntity getUserById(String id) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USER_ID.eq(id)) - .limit(1).get(); - - return (UserEntity) findUserQueryResult.firstOrNull(); - } public UserEntity getUserWithId(long id) { Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) @@ -118,72 +67,17 @@ public class UserUtils { return (UserEntity) findUserQueryResult.firstOrNull(); } - public void disableAllUsersWithoutId(long userId) { - Result findUserQueryResult = - dataStore.select(User.class).where(UserEntity.ID.notEqual(userId)).get(); - - for (Object object : findUserQueryResult) { - UserEntity userEntity = (UserEntity) object; - userEntity.setCurrent(false); - dataStore.update(userEntity).blockingGet(); - } - } - - public boolean checkIfUserIsScheduledForDeletion(String username, String server) { - Result findUserQueryResult = - dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)) - .and(UserEntity.BASE_URL.eq(server)) - .limit(1).get(); - - UserEntity userEntity; - if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { - return userEntity.getScheduledForDeletion(); - } - - return false; - } - - public UserEntity getUserWithInternalId(long internalId) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId) - .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) - .limit(1).get(); - - return (UserEntity) findUserQueryResult.firstOrNull(); - } - - public boolean getIfUserWithUsernameAndServer(String username, String server) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username) - .and(UserEntity.BASE_URL.eq(server))) - .limit(1).get(); - - return findUserQueryResult.firstOrNull() != null; - } - - public boolean scheduleUserForDeletionWithId(long id) { - Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) - .limit(1).get(); - - UserEntity userEntity; - if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { - userEntity.setScheduledForDeletion(true); - userEntity.setCurrent(false); - dataStore.update(userEntity).blockingGet(); - } - - return getAnyUserAndSetAsActive() != null; - } - public Observable createOrUpdateUser(@Nullable String username, - @Nullable String token, - @Nullable String serverUrl, - @Nullable String displayName, - @Nullable String pushConfigurationState, - @Nullable Boolean currentUser, - @Nullable String userId, - @Nullable Long internalId, - @Nullable String capabilities, - @Nullable String certificateAlias, - @Nullable String externalSignalingServer) { + @Nullable String token, + @Nullable String serverUrl, + @Nullable String displayName, + @Nullable String pushConfigurationState, + @Nullable Boolean currentUser, + @Nullable String userId, + @Nullable Long internalId, + @Nullable String capabilities, + @Nullable String certificateAlias, + @Nullable String externalSignalingServer) { Result findUserQueryResult; if (internalId == null) { findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username). diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt index 7c551b3d5..5f597ef22 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.kt @@ -97,7 +97,7 @@ class DatabaseStorageModule( "always" -> 1 else -> 0 } - ncApi!!.setNotificationLevel( + ncApi.setNotificationLevel( ApiUtils.getCredentials( conversationUser.username, conversationUser.token @@ -124,7 +124,7 @@ class DatabaseStorageModule( } } else if (key == "conversation_password") { if (hasPassword != null) { - ncApi!!.setPassword( + ncApi.setPassword( ApiUtils.getCredentials( conversationUser.username, conversationUser.token @@ -153,7 +153,7 @@ class DatabaseStorageModule( conversationNameValue ) && conversationNameValue != value ) { - ncApi!!.renameRoom( + ncApi.renameRoom( ApiUtils.getCredentials( conversationUser.username, conversationUser.token diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.kt b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.kt index 00ce7fc12..28777e193 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.kt @@ -54,7 +54,7 @@ class MagicPeerConnectionWrapper(peerConnectionFactory: PeerConnectionFactory, var peerConnection: PeerConnection? private set var sessionId: String - private var nick: String? = null + public var nick: String? = null private val sdpConstraints: MediaConstraints private var magicDataChannel: DataChannel? = null val magicSdpObserver: MagicSdpObserver @@ -129,18 +129,6 @@ class MagicPeerConnectionWrapper(peerConnectionFactory: PeerConnectionFactory, } } - fun getNick(): String? { - return if (!TextUtils.isEmpty(nick)) { - nick - } else { - context.resources.getString(R.string.nc_nick_guest) - } - } - - fun setNick(nick: String?) { - this.nick = nick - } - private fun sendInitialMediaStatus() { if (localMediaStream != null) { if (localMediaStream.videoTracks.size == 1 && localMediaStream.videoTracks[0] @@ -201,9 +189,9 @@ class MagicPeerConnectionWrapper(peerConnectionFactory: PeerConnectionFactory, if (dataChannelMessage.payload is String) { internalNick = dataChannelMessage.payload as String if (internalNick != nick) { - setNick(nick) + nick = internalNick EventBus.getDefault() - .post(PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE, sessionId, getNick(), null, videoStreamType)) + .post(PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE, sessionId, nick, null, videoStreamType)) } } else { if (dataChannelMessage.payload != null) { diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt index 9390c316c..c83d43cb0 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.kt @@ -22,7 +22,6 @@ package com.nextcloud.talk.webrtc import android.content.Context import android.text.TextUtils import android.util.Log -import autodagger.AutoInjector import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.R.string import com.nextcloud.talk.application.NextcloudTalkApplication @@ -58,9 +57,7 @@ import org.koin.core.inject import java.io.IOException import java.util.ArrayList import java.util.HashMap -import javax.inject.Inject -@AutoInjector(NextcloudTalkApplication::class) class MagicWebSocketInstance internal constructor( conversationUser: UserNgEntity, connectionUrl: String, @@ -513,9 +510,6 @@ class MagicWebSocketInstance internal constructor( } init { - sharedApplication - ?.componentApplication - ?.inject(this) this.connectionUrl = connectionUrl this.conversationUser = conversationUser this.webSocketTicket = webSocketTicket