Compiles again

This commit is contained in:
Mario Danic 2019-12-13 12:38:40 +01:00
parent 243fa2aad9
commit ebe96f1f99
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
45 changed files with 1939 additions and 2359 deletions

View File

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "95ddaea30271abd3160e1cdc8ab404d5", "identityHash": "26585a95894baebb7d811a80f3811b85",
"entities": [ "entities": [
{ {
"tableName": "conversations", "tableName": "conversations",
@ -261,7 +261,16 @@
], ],
"autoGenerate": false "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": [ "foreignKeys": [
{ {
"table": "conversations", "table": "conversations",
@ -360,7 +369,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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')"
] ]
} }
} }

View File

@ -26,7 +26,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.Window import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import com.bluelinelabs.conductor.Conductor 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.events.ConfigurationChangeEvent
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
@AutoInjector(NextcloudTalkApplication::class)
class MagicCallActivity : BaseActivity() { class MagicCallActivity : BaseActivity() {
@BindView(R.id.controller_container) @BindView(R.id.controller_container)
@ -50,7 +48,6 @@ class MagicCallActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
requestWindowFeature(Window.FEATURE_NO_TITLE) requestWindowFeature(Window.FEATURE_NO_TITLE)
window.addFlags( window.addFlags(

View File

@ -30,7 +30,6 @@ import coil.api.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication 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.chat.ChatMessage
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION

View File

@ -1,344 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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<String> 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<AbstractFlexibleItem> adapter;
private List<AbstractFlexibleItem> 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<String> iterator = selectedPaths.iterator();
List<String> 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<AbstractFlexibleItem> fileBrowserItems = new ArrayList<>();
if (davResponse.getData() != null) {
final List<BrowserFile> objectList = (List<BrowserFile>) 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,
}
}

View File

@ -0,0 +1,313 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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<String>
@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<AbstractFlexibleItem<*>>? = null
private val recyclerViewItems = ArrayList<AbstractFlexibleItem<*>>()
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<String> = 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<AbstractFlexibleItem<*>>()
if (davResponse.data != null) {
val objectList = davResponse.data as List<BrowserFile>
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
}
}

View File

@ -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.BrowserFile;
import com.nextcloud.talk.components.filebrowser.models.DavResponse; 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.local.models.UserNgEntity;
import com.nextcloud.talk.newarch.utils.NetworkUtils;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import java.io.IOException; import java.io.IOException;
@ -49,7 +49,7 @@ public class ReadFilesystemOperation {
OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder(); OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder();
okHttpClientBuilder.followRedirects(false); okHttpClientBuilder.followRedirects(false);
okHttpClientBuilder.followSslRedirects(false); okHttpClientBuilder.followSslRedirects(false);
okHttpClientBuilder.authenticator(new RestModule.MagicAuthenticator( okHttpClientBuilder.authenticator(new NetworkUtils.MagicAuthenticator(
ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
"Authorization")); "Authorization"));
this.okHttpClient = okHttpClientBuilder.build(); this.okHttpClient = okHttpClientBuilder.build();

View File

@ -151,7 +151,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo
queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus() queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus()
} }
ncApi!!.getServerStatus(queryUrl) ncApi.getServerStatus(queryUrl)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.`as`<ObservableSubscribeProxy<Status>>(AutoDispose.autoDisposable(scopeProvider)) .`as`<ObservableSubscribeProxy<Status>>(AutoDispose.autoDisposable(scopeProvider))
@ -194,7 +194,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo
} }
private fun findServerTalkApp(credentials: String?) { private fun findServerTalkApp(credentials: String?) {
ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.`as`<ObservableSubscribeProxy<RoomsOverall>>(AutoDispose.autoDisposable(scopeProvider)) .`as`<ObservableSubscribeProxy<RoomsOverall>>(AutoDispose.autoDisposable(scopeProvider))
.subscribe(object : Observer<RoomsOverall> { .subscribe(object : Observer<RoomsOverall> {
@ -250,7 +250,7 @@ class AccountVerificationController(args: Bundle?) : BaseController(), KoinCompo
} }
private fun fetchProfile(credentials: String?) { private fun fetchProfile(credentials: String?) {
ncApi!!.getUserProfile(credentials, ncApi.getUserProfile(credentials,
ApiUtils.getUrlForUserProfile(baseUrl)) ApiUtils.getUrlForUserProfile(baseUrl))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.`as`<ObservableSubscribeProxy<UserProfileOverall>>(AutoDispose.autoDisposable(scopeProvider)) .`as`<ObservableSubscribeProxy<UserProfileOverall>>(AutoDispose.autoDisposable(scopeProvider))

View File

@ -46,7 +46,7 @@ import android.widget.ProgressBar
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import butterknife.OnLongClick import butterknife.OnLongClick
@ -110,7 +110,6 @@ import java.util.HashMap
import java.util.HashSet import java.util.HashSet
import java.util.Objects import java.util.Objects
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject
import me.zhanghai.android.effortlesspermissions.AfterPermissionDenied import me.zhanghai.android.effortlesspermissions.AfterPermissionDenied
import me.zhanghai.android.effortlesspermissions.EffortlessPermissions import me.zhanghai.android.effortlesspermissions.EffortlessPermissions
import me.zhanghai.android.effortlesspermissions.OpenAppDetailsDialogFragment import me.zhanghai.android.effortlesspermissions.OpenAppDetailsDialogFragment
@ -393,7 +392,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun handleFromNotification() { private fun handleFromNotification() {
ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl)) ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl))
.retry(3) .retry(3)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -943,7 +942,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun fetchSignalingSettings() { private fun fetchSignalingSettings() {
ncApi!!.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(baseUrl)) ncApi.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(baseUrl))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.retry(3) .retry(3)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -1046,7 +1045,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun checkCapabilities() { private fun checkCapabilities() {
ncApi!!.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl))
.retry(3) .retry(3)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -1092,7 +1091,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun joinRoomAndCall() { private fun joinRoomAndCall() {
ncApi!!.joinRoom( ncApi.joinRoom(
credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant( credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(
baseUrl, baseUrl,
roomToken roomToken
@ -1131,7 +1130,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun performCall() { private fun performCall() {
ncApi!!.joinCall( ncApi.joinCall(
credentials, credentials,
ApiUtils.getUrlForCall(baseUrl, roomToken) ApiUtils.getUrlForCall(baseUrl, roomToken)
) )
@ -1148,7 +1147,7 @@ class CallController(args: Bundle) : BaseController() {
setCallState(CallStatus.ESTABLISHED) setCallState(CallStatus.ESTABLISHED)
if (needsPing) { if (needsPing) {
ncApi!!.pingCall(credentials, ApiUtils.getUrlForCallPing(baseUrl, roomToken)) ncApi.pingCall(credentials, ApiUtils.getUrlForCallPing(baseUrl, roomToken))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) } .repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) }
@ -1193,7 +1192,7 @@ class CallController(args: Bundle) : BaseController() {
} }
if (!hasExternalSignalingServer) { if (!hasExternalSignalingServer) {
ncApi!!.pullSignalingMessages( ncApi.pullSignalingMessages(
credentials, credentials,
ApiUtils.getUrlForSignaling(baseUrl, urlToken) ApiUtils.getUrlForSignaling(baseUrl, urlToken)
) )
@ -1393,7 +1392,7 @@ class CallController(args: Bundle) : BaseController() {
) )
if (magicPeerConnectionWrapper.peerConnection != null) { if (magicPeerConnectionWrapper.peerConnection != null) {
magicPeerConnectionWrapper.peerConnection magicPeerConnectionWrapper.peerConnection!!
.setRemoteDescription( .setRemoteDescription(
magicPeerConnectionWrapper magicPeerConnectionWrapper
.magicSdpObserver, sessionDescriptionWithPreferredCodec .magicSdpObserver, sessionDescriptionWithPreferredCodec
@ -1474,7 +1473,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun hangupNetworkCalls(shutDownView: Boolean) { private fun hangupNetworkCalls(shutDownView: Boolean) {
ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> { .subscribe(object : Observer<GenericOverall> {
@ -1509,7 +1508,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun leaveRoom(shutDownView: Boolean) { private fun leaveRoom(shutDownView: Boolean) {
ncApi!!.leaveRoom( ncApi.leaveRoom(
credentials, credentials,
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, roomToken) ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, roomToken)
) )
@ -1603,7 +1602,7 @@ class CallController(args: Bundle) : BaseController() {
} }
private fun getPeersForCall() { private fun getPeersForCall() {
ncApi!!.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe(object : Observer<ParticipantsOverall> { .subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
@ -1660,25 +1659,25 @@ class CallController(args: Bundle) : BaseController() {
if (hasMCU && publisher) { if (hasMCU && publisher) {
magicPeerConnectionWrapper = MagicPeerConnectionWrapper( magicPeerConnectionWrapper = MagicPeerConnectionWrapper(
peerConnectionFactory!!, peerConnectionFactory!!,
iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, true, true, iceServers, sdpConstraintsForMCU!!, sessionId, callSession, localMediaStream, true, true,
type type
) )
} else if (hasMCU) { } else if (hasMCU) {
magicPeerConnectionWrapper = MagicPeerConnectionWrapper( magicPeerConnectionWrapper = MagicPeerConnectionWrapper(
peerConnectionFactory!!, peerConnectionFactory!!,
iceServers, sdpConstraints, sessionId, callSession, null, false, true, type iceServers, sdpConstraints!!, sessionId, callSession, null, false, true, type
) )
} else { } else {
if ("screen" != type) { if ("screen" != type) {
magicPeerConnectionWrapper = MagicPeerConnectionWrapper( magicPeerConnectionWrapper = MagicPeerConnectionWrapper(
peerConnectionFactory!!, peerConnectionFactory!!,
iceServers, sdpConstraints, sessionId, callSession, localMediaStream, false, false, iceServers, sdpConstraints!!, sessionId, callSession, localMediaStream, false, false,
type type
) )
} else { } else {
magicPeerConnectionWrapper = MagicPeerConnectionWrapper( magicPeerConnectionWrapper = MagicPeerConnectionWrapper(
peerConnectionFactory!!, 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 urlToken = roomToken
} }
ncApi!!.sendSignalingMessages( ncApi.sendSignalingMessages(
credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken), credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken),
strings.toString() strings.toString()
) )
@ -2134,7 +2133,7 @@ class CallController(args: Bundle) : BaseController() {
} else { } else {
gotNick( gotNick(
session, session,
getPeerConnectionWrapperForSessionIdAndType(session, type, false).nick, false, getPeerConnectionWrapperForSessionIdAndType(session, type, false).nick!!, false,
type type
) )
} }

View File

@ -184,7 +184,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
} }
private fun checkIfAnyParticipantsRemainInRoom() { private fun checkIfAnyParticipantsRemainInRoom() {
ncApi!!.getPeersForCall( ncApi.getPeersForCall(
credentials, ApiUtils.getUrlForParticipants( credentials, ApiUtils.getUrlForParticipants(
userBeingCalled!!.baseUrl, userBeingCalled!!.baseUrl,
currentConversation!!.token currentConversation!!.token
@ -232,7 +232,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
} }
private fun handleFromNotification() { private fun handleFromNotification() {
ncApi!!.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled!!.baseUrl)) ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled!!.baseUrl))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.retry(3) .retry(3)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -401,8 +401,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
listener(onSuccess = { data, dataSource -> listener(onSuccess = { data, dataSource ->
GlobalScope.launch { GlobalScope.launch {
if ((AvatarStatusCodeHolder.getInstance().statusCode == 200 || AvatarStatusCodeHolder.getInstance().statusCode == 0) if ((AvatarStatusCodeHolder.getInstance().statusCode == 200 || AvatarStatusCodeHolder.getInstance().statusCode == 0)) {
&& userBeingCalled.hasSpreedFeatureCapability("no-ping")) {
if (activity != null) { if (activity != null) {
val newBitmap = BlurTransformation(activity!!, 5f).transform( val newBitmap = BlurTransformation(activity!!, 5f).transform(

View File

@ -46,7 +46,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.work.Data import androidx.work.Data
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import butterknife.Optional import butterknife.Optional
@ -99,7 +99,6 @@ import java.util.ArrayList
import java.util.Collections import java.util.Collections
import java.util.HashMap import java.util.HashMap
import java.util.HashSet import java.util.HashSet
import javax.inject.Inject
class ContactsController : BaseController, class ContactsController : BaseController,
SearchView.OnQueryTextListener, SearchView.OnQueryTextListener,
@ -222,9 +221,6 @@ class ContactsController : BaseController,
override fun onViewBound(view: View) { override fun onViewBound(view: View) {
super.onViewBound(view) super.onViewBound(view)
NextcloudTalkApplication.sharedApplication!!
.componentApplication
.inject(this)
currentUser = usersRepository.getActiveUser() currentUser = usersRepository.getActiveUser()
@ -278,7 +274,7 @@ class ContactsController : BaseController,
currentUser!!.baseUrl, roomType, currentUser!!.baseUrl, roomType,
userId, null userId, null
) )
ncApi!!.createRoom( ncApi.createRoom(
credentials, credentials,
retrofitBucket.url, retrofitBucket.queryMap retrofitBucket.url, retrofitBucket.queryMap
) )
@ -304,7 +300,7 @@ class ContactsController : BaseController,
) )
if (currentUser!!.hasSpreedFeatureCapability("chat-v2")) { if (currentUser!!.hasSpreedFeatureCapability("chat-v2")) {
ncApi!!.getRoom( ncApi.getRoom(
credentials, credentials,
ApiUtils.getRoom( ApiUtils.getRoom(
currentUser!!.baseUrl, currentUser!!.baseUrl,
@ -509,7 +505,7 @@ class ContactsController : BaseController,
} }
val finalServerIs14OrUp = serverIs14OrUp val finalServerIs14OrUp = serverIs14OrUp
ncApi!!.getContactsWithSearchParam( ncApi.getContactsWithSearchParam(
credentials, credentials,
retrofitBucket.url, shareTypesList, modifiedQueryMap retrofitBucket.url, shareTypesList, modifiedQueryMap
) )
@ -854,7 +850,7 @@ class ContactsController : BaseController,
userItem.model.userId, null userItem.model.userId, null
) )
ncApi!!.createRoom( ncApi.createRoom(
credentials, credentials,
retrofitBucket.url, retrofitBucket.queryMap retrofitBucket.url, retrofitBucket.queryMap
) )

View File

@ -38,7 +38,7 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.work.Data import androidx.work.Data
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import coil.api.load import coil.api.load
@ -99,11 +99,10 @@ import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import org.koin.android.ext.android.inject
import java.util.ArrayList import java.util.ArrayList
import java.util.Calendar import java.util.Calendar
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class ConversationInfoController(args: Bundle) : BaseController(), class ConversationInfoController(args: Bundle) : BaseController(),
FlexibleAdapter.OnItemClickListener, ConversationInfoInterface { FlexibleAdapter.OnItemClickListener, ConversationInfoInterface {
@ -168,10 +167,7 @@ class ConversationInfoController(args: Bundle) : BaseController(),
@BindView(R.id.shareAction) @BindView(R.id.shareAction)
lateinit var shareAction: MaterialStandardPreference lateinit var shareAction: MaterialStandardPreference
@set:Inject val ncApi: NcApi by inject()
lateinit var ncApi: NcApi
@set:Inject
lateinit var userUtils: UserUtils
private val conversationToken: String? private val conversationToken: String?
private val conversationUser: UserNgEntity? private val conversationUser: UserNgEntity?
@ -201,7 +197,6 @@ class ConversationInfoController(args: Bundle) : BaseController(),
init { init {
setHasOptionsMenu(true) setHasOptionsMenu(true)
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY) conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN) conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN)
credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token) credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token)

View File

@ -1,300 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.controllers;
import android.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<AbstractFlexibleItem> 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();
}
}

View File

@ -0,0 +1,281 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.controllers
import android.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<AbstractFlexibleItem<*>>? = null
private var adapterDataObserver: RecyclerView.AdapterDataObserver? = null
private val abstractFlexibleItemList = ArrayList<AbstractFlexibleItem<*>>()
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"
}
}

View File

@ -35,7 +35,7 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
@ -55,13 +55,15 @@ import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
import com.uber.autodispose.AutoDispose import com.uber.autodispose.AutoDispose
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers 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 org.koin.android.ext.android.inject
import studio.carbonylgroup.textfieldboxes.ExtendedEditText import studio.carbonylgroup.textfieldboxes.ExtendedEditText
import studio.carbonylgroup.textfieldboxes.TextFieldBoxes import studio.carbonylgroup.textfieldboxes.TextFieldBoxes
import java.security.cert.CertificateException import java.security.cert.CertificateException
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class ServerSelectionController : BaseController() { class ServerSelectionController : BaseController() {
@JvmField @JvmField
@BindView(R.id.extended_edit_text) @BindView(R.id.extended_edit_text)
@ -119,65 +121,70 @@ class ServerSelectionController : BaseController() {
&& TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type))) { && TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type))) {
providersTextView!!.visibility = View.INVISIBLE providersTextView!!.visibility = View.INVISIBLE
} else { } else {
val users = usersRepository.getUsers() GlobalScope.launch {
val usersSize = users.count() val users = usersRepository.getUsers()
if ((TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type)) || val usersSize = users.count()
findAccounts(users).isEmpty()) &&
usersSize == 0) { withContext(Dispatchers.Main) {
providersTextView!!.setText(R.string.nc_get_from_provider) if ((TextUtils.isEmpty(resources!!.getString(R.string.nc_import_account_type)) ||
providersTextView!!.setOnClickListener { view12: View? -> findAccounts(users).isEmpty()) &&
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(resources!! usersSize == 0) {
.getString(R.string.nc_providers_url))) providersTextView!!.setText(R.string.nc_get_from_provider)
startActivity(browserIntent) providersTextView!!.setOnClickListener { view12: View? ->
} val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(resources!!
} else if (findAccounts(users).isNotEmpty()) { .getString(R.string.nc_providers_url)))
if (!TextUtils.isEmpty(getAppNameBasedOnPackage(resources!! startActivity(browserIntent)
.getString(R.string.nc_import_accounts_from)))) { }
if (findAccounts(users).size > 1) { } else if (findAccounts(users).isNotEmpty()) {
providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_accounts), if (!TextUtils.isEmpty(getAppNameBasedOnPackage(resources!!
getAppNameBasedOnPackage(resources!! .getString(R.string.nc_import_accounts_from)))) {
.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 { } else {
providersTextView!!.text = String.format(resources!!.getString(R.string.nc_server_import_account), providersTextView!!.visibility = View.INVISIBLE
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? -> serverEntry!!.requestFocus()
val bundle = Bundle() serverEntry!!.addTextChangedListener(object : TextWatcher {
bundle.putBoolean(KEY_IS_ACCOUNT_IMPORT, true) override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
router.pushController(RouterTransaction.with( override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
SwitchAccountController(bundle)) override fun afterTextChanged(editable: Editable) {
.pushChangeHandler(HorizontalChangeHandler()) if (!textFieldBoxes!!.isOnError && !TextUtils.isEmpty(serverEntry!!.text)) {
.popChangeHandler(HorizontalChangeHandler())) toggleProceedButton(true)
} } else {
} else { toggleProceedButton(false)
providersTextView!!.visibility = View.INVISIBLE }
} }
} })
serverEntry!!.requestFocus() serverEntry!!.setOnEditorActionListener { textView: TextView?, i: Int, keyEvent: KeyEvent? ->
serverEntry!!.addTextChangedListener(object : TextWatcher { if (i == EditorInfo.IME_ACTION_DONE) {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} checkServerAndProceed()
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} }
override fun afterTextChanged(editable: Editable) { false
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
} }
} }
@ -210,7 +217,7 @@ class ServerSelectionController : BaseController() {
} }
private fun checkServer(queryUrl: String, checkForcedHttps: Boolean) { private fun checkServer(queryUrl: String, checkForcedHttps: Boolean) {
ncApi!!.getServerStatus(queryUrl) ncApi.getServerStatus(queryUrl)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.`as`(AutoDispose.autoDisposable(scopeProvider)) .`as`(AutoDispose.autoDisposable(scopeProvider))

View File

@ -43,7 +43,7 @@ import androidx.core.view.ViewCompat
import androidx.emoji.widget.EmojiTextView import androidx.emoji.widget.EmojiTextView
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import coil.api.load import coil.api.load
@ -97,7 +97,6 @@ import java.util.ArrayList
import java.util.Arrays import java.util.Arrays
import java.util.Locale import java.util.Locale
import java.util.Objects import java.util.Objects
import javax.inject.Inject
class SettingsController : BaseController() { class SettingsController : BaseController() {
@JvmField @JvmField
@ -176,7 +175,6 @@ class SettingsController : BaseController() {
@JvmField @JvmField
@BindView(R.id.message_text) @BindView(R.id.message_text)
var messageText: TextView? = null var messageText: TextView? = null
@JvmField
val ncApi: NcApi by inject() val ncApi: NcApi by inject()
val usersRepository: UsersRepository by inject() val usersRepository: UsersRepository by inject()
private var saveStateHandler: LovelySaveStateHandler? = null private var saveStateHandler: LovelySaveStateHandler? = null
@ -599,7 +597,7 @@ class SettingsController : BaseController() {
loadAvatarImage() loadAvatarImage()
ncApi!!.getUserProfile( ncApi.getUserProfile(
credentials, credentials,
ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)
) )

View File

@ -1,254 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* 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<AbstractFlexibleItem> adapter;
private List<AbstractFlexibleItem> 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<UserEntity>() {
@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);
}
}

View File

@ -0,0 +1,227 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* 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<AbstractFlexibleItem<*>>? = null
private val userItems = ArrayList<AbstractFlexibleItem<*>>()
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<Any>(scopeProvider))
.subscribe(object : Observer<UserEntity> {
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)
}
}

View File

@ -33,7 +33,7 @@ import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.ActionBar import androidx.appcompat.app.ActionBar
import autodagger.AutoInjector
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.controllers.AccountVerificationController import com.nextcloud.talk.controllers.AccountVerificationController
@ -47,7 +47,6 @@ import org.greenrobot.eventbus.EventBus
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.util.ArrayList import java.util.ArrayList
@AutoInjector(NextcloudTalkApplication::class)
abstract class BaseController : ButterKnifeController(), ComponentCallbacks { abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this) val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this)

View File

@ -1,333 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.controllers.bottomsheet;
import android.content.ComponentName;
import android.content.Intent;
import android.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();
}
}

View File

@ -0,0 +1,297 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.controllers.bottomsheet
import android.content.ComponentName
import android.content.Intent
import android.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()
}
}

View File

@ -1,753 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.controllers.bottomsheet;
import android.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<String> invitedUsers = new ArrayList<>();
private ArrayList<String> 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<RoomOverall>() {
@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<RoomOverall>() {
@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<RoomOverall>() {
@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<GenericOverall>() {
@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<CapabilitiesOverall>() {
@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<String> localInvitedUsers = invitedUsers;
final ArrayList<String> 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<AddParticipantOverall>() {
@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<AddParticipantOverall>() {
@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();
}
}
}

View File

@ -0,0 +1,662 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.controllers.bottomsheet
import android.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<String>? = ArrayList()
private var invitedGroups: ArrayList<String>? = 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<RoomOverall> {
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<RoomOverall> {
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<RoomOverall> {
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<GenericOverall> {
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<CapabilitiesOverall> {
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<AddParticipantOverall> {
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<AddParticipantOverall> {
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<Any> {
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()
}
}
}

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.jobs
import android.content.Context import android.content.Context
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.events.EventStatus
@ -43,7 +42,6 @@ import retrofit2.Retrofit
import java.net.CookieManager import java.net.CookieManager
import java.util.* import java.util.*
@AutoInjector(NextcloudTalkApplication::class)
class CapabilitiesWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { class CapabilitiesWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent {
val retrofit: Retrofit by inject() val retrofit: Retrofit by inject()
val eventBus: EventBus by inject() val eventBus: EventBus by inject()

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.jobs
import android.content.Context import android.content.Context
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
@ -45,7 +44,6 @@ import org.koin.core.inject
import retrofit2.Retrofit import retrofit2.Retrofit
import java.net.CookieManager import java.net.CookieManager
@AutoInjector(NextcloudTalkApplication::class)
class DeleteConversationWorker(context: Context, class DeleteConversationWorker(context: Context,
workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent {
val retrofit: Retrofit by inject() val retrofit: Retrofit by inject()
@ -84,10 +82,4 @@ class DeleteConversationWorker(context: Context,
} }
return Result.success() return Result.success()
} }
}
init {
sharedApplication
?.componentApplication
?.inject(this)
}
}

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.jobs
import android.content.Context import android.content.Context
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.events.EventStatus
@ -44,7 +43,6 @@ import org.koin.core.inject
import retrofit2.Retrofit import retrofit2.Retrofit
import java.net.CookieManager import java.net.CookieManager
@AutoInjector(NextcloudTalkApplication::class)
class LeaveConversationWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams), KoinComponent { class LeaveConversationWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams), KoinComponent {
val retrofit: Retrofit by inject() val retrofit: Retrofit by inject()
val okHttpClient: OkHttpClient by inject() val okHttpClient: OkHttpClient by inject()

View File

@ -47,22 +47,16 @@ import androidx.core.app.Person
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.emoji.text.EmojiCompat import androidx.emoji.text.EmojiCompat
import androidx.work.ListenableWorker.Result
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import coil.Coil import coil.Coil
import coil.target.Target import coil.target.Target
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.bluelinelabs.logansquare.LoganSquare import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R.color import com.nextcloud.talk.R.*
import com.nextcloud.talk.R.dimen
import com.nextcloud.talk.R.drawable
import com.nextcloud.talk.R.string
import com.nextcloud.talk.activities.MagicCallActivity import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.api.NcApi 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.RingtoneSettings
import com.nextcloud.talk.models.SignatureVerification import com.nextcloud.talk.models.SignatureVerification
import com.nextcloud.talk.models.database.ArbitraryStorageEntity 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.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.local.models.getCredentials 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.Images
import com.nextcloud.talk.newarch.utils.getCredentials import com.nextcloud.talk.newarch.utils.getCredentials
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
@ -116,7 +109,7 @@ import java.net.CookieManager
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.security.PrivateKey import java.security.PrivateKey
import java.util.HashMap import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
import java.util.zip.CRC32 import java.util.zip.CRC32
import javax.crypto.Cipher import javax.crypto.Cipher

View File

@ -52,7 +52,7 @@ class ShareOperationWorker(
private val baseUrl: String private val baseUrl: String
override fun doWork(): Result { override fun doWork(): Result {
for (i in filesArray.indices) { for (i in filesArray.indices) {
ncApi!!.createRemoteShare( ncApi.createRemoteShare(
credentials, credentials,
ApiUtils.getSharingUrl(baseUrl), ApiUtils.getSharingUrl(baseUrl),
filesArray[i], filesArray[i],

View File

@ -24,7 +24,6 @@ import androidx.work.CoroutineWorker
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication 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.KoinComponent
import org.koin.core.inject import org.koin.core.inject
import java.util.* import java.util.*
import javax.inject.Inject
class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent { class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams), KoinComponent {
val ncApi: NcApi by inject() val ncApi: NcApi by inject()
@ -65,7 +63,7 @@ class SignalingSettingsWorker(context: Context, workerParams: WorkerParameters)
for (i in userEntityList.indices) { for (i in userEntityList.indices) {
userEntity = userEntityList[i] userEntity = userEntityList[i]
val finalUserEntity: UserNgEntity? = userEntity val finalUserEntity: UserNgEntity? = userEntity
ncApi!!.getSignalingSettings( ncApi.getSignalingSettings(
userEntity!!.getCredentials(), userEntity!!.getCredentials(),
ApiUtils.getUrlForSignalingSettings(userEntity.baseUrl)) ApiUtils.getUrlForSignalingSettings(userEntity.baseUrl))
.blockingSubscribe(object : Observer<SignalingSettingsOverall> { .blockingSubscribe(object : Observer<SignalingSettingsOverall> {

View File

@ -27,11 +27,9 @@ import android.util.Log
import androidx.work.ListenableWorker import androidx.work.ListenableWorker
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.bluelinelabs.logansquare.LoganSquare import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.models.ExternalSignalingServer 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.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.utils.database.user.UserUtils 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.KoinComponent
import org.koin.core.inject import org.koin.core.inject
import java.io.IOException import java.io.IOException
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class WebsocketConnectionsWorker( class WebsocketConnectionsWorker(
context: Context, context: Context,
workerParams: WorkerParameters workerParams: WorkerParameters
@ -50,10 +46,6 @@ class WebsocketConnectionsWorker(
val usersRepository: UsersRepository by inject() val usersRepository: UsersRepository by inject()
override fun doWork(): Result { override fun doWork(): Result {
NextcloudTalkApplication.sharedApplication!!
.componentApplication
.inject(this)
val userEntityList = usersRepository.getUsers() val userEntityList = usersRepository.getUsers()
var userEntity: UserNgEntity var userEntity: UserNgEntity
for (i in userEntityList.indices) { for (i in userEntityList.indices) {

View File

@ -60,76 +60,5 @@ public interface User extends Parcelable, Persistable, Serializable {
boolean getCurrent(); boolean getCurrent();
boolean getScheduledForDeletion(); 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<String, String> 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;
}
} }

View File

@ -31,5 +31,5 @@ import org.parceler.Parcel;
@JsonObject @JsonObject
public class NotificationsCapability { public class NotificationsCapability {
@JsonField(name = "ocs-endpoints") @JsonField(name = "ocs-endpoints")
List<String> features; public List<String> features;
} }

View File

@ -22,6 +22,7 @@ package com.nextcloud.talk.newarch.data.repository.online
import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.models.json.conversations.Conversation 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.generic.GenericOverall
import com.nextcloud.talk.newarch.data.source.remote.ApiService import com.nextcloud.talk.newarch.data.source.remote.ApiService
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository 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( override suspend fun setFavoriteValueForConversation(
user: UserNgEntity, user: UserNgEntity,
conversation: Conversation, conversation: Conversation,

View File

@ -62,5 +62,5 @@ interface ApiService {
): GenericOverall ): GenericOverall
@GET @GET
suspend fun getRoom(@Header("Authorization") authorization: String, @Url url: String): RoomOverall suspend fun getConversation(@Header("Authorization") authorization: String, @Url url: String): RoomOverall
} }

View File

@ -54,7 +54,7 @@ val StorageModule = module {
single { createMessagesRepository(get()) } single { createMessagesRepository(get()) }
single { createUsersRepository(get()) } single { createUsersRepository(get()) }
single { createArbitraryStorageUtils(get()) } single { createArbitraryStorageUtils(get()) }
single { createUserUtils(get()) }
single { TalkDatabase.getInstance(androidApplication()) } single { TalkDatabase.getInstance(androidApplication()) }
single { get<TalkDatabase>().conversationsDao() } single { get<TalkDatabase>().conversationsDao() }
single { get<TalkDatabase>().messagesDao() } single { get<TalkDatabase>().messagesDao() }
@ -92,4 +92,8 @@ fun createDataStore(sqlCipherDatabaseSource: SqlCipherDatabaseSource): ReactiveE
fun createArbitraryStorageUtils(dataStore: ReactiveEntityStore<Persistable>): ArbitraryStorageUtils { fun createArbitraryStorageUtils(dataStore: ReactiveEntityStore<Persistable>): ArbitraryStorageUtils {
return ArbitraryStorageUtils(dataStore) return ArbitraryStorageUtils(dataStore)
}
fun createUserUtils(dataStore: ReactiveEntityStore<Persistable>): UserUtils {
return UserUtils(dataStore)
} }

View File

@ -44,8 +44,8 @@ interface NextcloudTalkRepository {
conversation: Conversation conversation: Conversation
): GenericOverall ): GenericOverall
suspend fun getRoomForUser( suspend fun getConversationForUser(
userEntity: UserNgEntity, userEntity: UserNgEntity,
roomToken: String conversationToken: String
): RoomOverall ): RoomOverall
} }

View File

@ -6,12 +6,12 @@ import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkReposito
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
import org.koin.core.parameter.DefinitionParameters import org.koin.core.parameter.DefinitionParameters
class GetRoomUseCase constructor( class GetConversationUseCase constructor(
private val nextcloudTalkRepository: NextcloudTalkRepository, private val nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler? apiErrorHandler: ApiErrorHandler?
) : UseCase<RoomOverall, Any?>(apiErrorHandler) { ) : UseCase<RoomOverall, Any?>(apiErrorHandler) {
override suspend fun run(params: Any?): RoomOverall { override suspend fun run(params: Any?): RoomOverall {
val definitionParameters = params as DefinitionParameters val definitionParameters = params as DefinitionParameters
return nextcloudTalkRepository.getRoomForUser(definitionParameters[0], definitionParameters[1]) return nextcloudTalkRepository.getConversationForUser(definitionParameters[0], definitionParameters[1])
} }
} }

View File

@ -32,7 +32,7 @@ import com.nextcloud.talk.newarch.local.models.MessageEntity
@Dao @Dao
abstract class MessagesDao { abstract class MessagesDao {
@Query("SELECT * FROM messages WHERE conversation = :conversationId") @Query("SELECT * FROM messages WHERE conversation_id = :conversationId")
abstract fun getMessagesWithUserForConversation(conversationId: String): abstract fun getMessagesWithUserForConversation(conversationId: String):
LiveData<List<MessageEntity>> LiveData<List<MessageEntity>>

View File

@ -35,7 +35,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType
@Entity( @Entity(
tableName = "messages", tableName = "messages",
indices = [Index(value = ["conversation"])], indices = [Index(value = ["conversation_id"])],
foreignKeys = [ForeignKey( foreignKeys = [ForeignKey(
entity = ConversationEntity::class, entity = ConversationEntity::class,
parentColumns = arrayOf("id"), parentColumns = arrayOf("id"),

View File

@ -76,7 +76,7 @@ class MentionAutocompletePresenter : RecyclerViewPresenter<Mention?>, FlexibleAd
"" ""
} }
adapter!!.setFilter(queryString) adapter!!.setFilter(queryString)
ncApi!!.getMentionAutocompleteSuggestions( ncApi.getMentionAutocompleteSuggestions(
currentUser!!.getCredentials(), ApiUtils.getUrlForMentionSuggestions(currentUser!!.baseUrl, roomToken), currentUser!!.getCredentials(), ApiUtils.getUrlForMentionSuggestions(currentUser!!.baseUrl, roomToken),
queryString, 5) queryString, 5)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@ -215,7 +215,7 @@ class PushUtils(val usersRepository: UsersRepository): KoinComponent {
userEntity.username, userEntity.token userEntity.username, userEntity.token
) )
val finalCredentials = credentials val finalCredentials = credentials
ncApi!!.registerDeviceForNotificationsWithNextcloud( ncApi.registerDeviceForNotificationsWithNextcloud(
credentials, credentials,
ApiUtils.getUrlNextcloudPush(userEntity.baseUrl), ApiUtils.getUrlNextcloudPush(userEntity.baseUrl),
queryMap queryMap
@ -232,7 +232,7 @@ class PushUtils(val usersRepository: UsersRepository): KoinComponent {
.data.signature .data.signature
proxyMap["userPublicKey"] = pushRegistrationOverall.ocs proxyMap["userPublicKey"] = pushRegistrationOverall.ocs
.data.publicKey .data.publicKey
ncApi!!.registerDeviceForNotificationsWithProxy( ncApi.registerDeviceForNotificationsWithProxy(
ApiUtils.getUrlPushProxy(), proxyMap ApiUtils.getUrlPushProxy(), proxyMap
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@ -22,6 +22,7 @@ package com.nextcloud.talk.utils.database.arbitrarystorage;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.nextcloud.talk.models.database.ArbitraryStorage; import com.nextcloud.talk.models.database.ArbitraryStorage;
import com.nextcloud.talk.models.database.ArbitraryStorageEntity; import com.nextcloud.talk.models.database.ArbitraryStorageEntity;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import io.requery.Persistable; import io.requery.Persistable;

View File

@ -24,9 +24,8 @@ import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.nextcloud.talk.models.database.User; import com.nextcloud.talk.models.database.User;
import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.database.UserEntity;
import io.reactivex.Completable;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import io.requery.Persistable; import io.requery.Persistable;
import io.requery.query.Result; import io.requery.query.Result;
@ -52,29 +51,6 @@ public class UserUtils {
return findUsersQueryResult.toList(); 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() { public UserEntity getCurrentUser() {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(true) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(true)
.and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true))) .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(true)))
@ -83,33 +59,6 @@ public class UserUtils {
return (UserEntity) findUserQueryResult.firstOrNull(); 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) { public UserEntity getUserWithId(long id) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id))
@ -118,72 +67,17 @@ public class UserUtils {
return (UserEntity) findUserQueryResult.firstOrNull(); 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<UserEntity> createOrUpdateUser(@Nullable String username, public Observable<UserEntity> createOrUpdateUser(@Nullable String username,
@Nullable String token, @Nullable String token,
@Nullable String serverUrl, @Nullable String serverUrl,
@Nullable String displayName, @Nullable String displayName,
@Nullable String pushConfigurationState, @Nullable String pushConfigurationState,
@Nullable Boolean currentUser, @Nullable Boolean currentUser,
@Nullable String userId, @Nullable String userId,
@Nullable Long internalId, @Nullable Long internalId,
@Nullable String capabilities, @Nullable String capabilities,
@Nullable String certificateAlias, @Nullable String certificateAlias,
@Nullable String externalSignalingServer) { @Nullable String externalSignalingServer) {
Result findUserQueryResult; Result findUserQueryResult;
if (internalId == null) { if (internalId == null) {
findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username). findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username).

View File

@ -97,7 +97,7 @@ class DatabaseStorageModule(
"always" -> 1 "always" -> 1
else -> 0 else -> 0
} }
ncApi!!.setNotificationLevel( ncApi.setNotificationLevel(
ApiUtils.getCredentials( ApiUtils.getCredentials(
conversationUser.username, conversationUser.username,
conversationUser.token conversationUser.token
@ -124,7 +124,7 @@ class DatabaseStorageModule(
} }
} else if (key == "conversation_password") { } else if (key == "conversation_password") {
if (hasPassword != null) { if (hasPassword != null) {
ncApi!!.setPassword( ncApi.setPassword(
ApiUtils.getCredentials( ApiUtils.getCredentials(
conversationUser.username, conversationUser.username,
conversationUser.token conversationUser.token
@ -153,7 +153,7 @@ class DatabaseStorageModule(
conversationNameValue conversationNameValue
) && conversationNameValue != value ) && conversationNameValue != value
) { ) {
ncApi!!.renameRoom( ncApi.renameRoom(
ApiUtils.getCredentials( ApiUtils.getCredentials(
conversationUser.username, conversationUser.username,
conversationUser.token conversationUser.token

View File

@ -54,7 +54,7 @@ class MagicPeerConnectionWrapper(peerConnectionFactory: PeerConnectionFactory,
var peerConnection: PeerConnection? var peerConnection: PeerConnection?
private set private set
var sessionId: String var sessionId: String
private var nick: String? = null public var nick: String? = null
private val sdpConstraints: MediaConstraints private val sdpConstraints: MediaConstraints
private var magicDataChannel: DataChannel? = null private var magicDataChannel: DataChannel? = null
val magicSdpObserver: MagicSdpObserver 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() { private fun sendInitialMediaStatus() {
if (localMediaStream != null) { if (localMediaStream != null) {
if (localMediaStream.videoTracks.size == 1 && localMediaStream.videoTracks[0] if (localMediaStream.videoTracks.size == 1 && localMediaStream.videoTracks[0]
@ -201,9 +189,9 @@ class MagicPeerConnectionWrapper(peerConnectionFactory: PeerConnectionFactory,
if (dataChannelMessage.payload is String) { if (dataChannelMessage.payload is String) {
internalNick = dataChannelMessage.payload as String internalNick = dataChannelMessage.payload as String
if (internalNick != nick) { if (internalNick != nick) {
setNick(nick) nick = internalNick
EventBus.getDefault() EventBus.getDefault()
.post(PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE, sessionId, getNick(), null, videoStreamType)) .post(PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE, sessionId, nick, null, videoStreamType))
} }
} else { } else {
if (dataChannelMessage.payload != null) { if (dataChannelMessage.payload != null) {

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.webrtc
import android.content.Context import android.content.Context
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import autodagger.AutoInjector
import com.bluelinelabs.logansquare.LoganSquare import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R.string import com.nextcloud.talk.R.string
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
@ -58,9 +57,7 @@ import org.koin.core.inject
import java.io.IOException import java.io.IOException
import java.util.ArrayList import java.util.ArrayList
import java.util.HashMap import java.util.HashMap
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class MagicWebSocketInstance internal constructor( class MagicWebSocketInstance internal constructor(
conversationUser: UserNgEntity, conversationUser: UserNgEntity,
connectionUrl: String, connectionUrl: String,
@ -513,9 +510,6 @@ class MagicWebSocketInstance internal constructor(
} }
init { init {
sharedApplication
?.componentApplication
?.inject(this)
this.connectionUrl = connectionUrl this.connectionUrl = connectionUrl
this.conversationUser = conversationUser this.conversationUser = conversationUser
this.webSocketTicket = webSocketTicket this.webSocketTicket = webSocketTicket