/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.nextcloud.talk.controllers;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.base.BaseController;
import com.nextcloud.talk.events.EventStatus;
import com.nextcloud.talk.jobs.CapabilitiesWorker;
import com.nextcloud.talk.jobs.PushRegistrationWorker;
import com.nextcloud.talk.jobs.SignalingSettingsJob;
import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.generic.Status;
import com.nextcloud.talk.models.json.rooms.RoomsOverall;
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.ClosedInterfaceImpl;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.net.CookieManager;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import autodagger.AutoInjector;
import butterknife.BindView;
import io.reactivex.CompletableObserver;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
@AutoInjector(NextcloudTalkApplication.class)
public class AccountVerificationController extends BaseController {
public static final String TAG = "AccountVerificationController";
@Inject
NcApi ncApi;
@Inject
UserUtils userUtils;
@Inject
CookieManager cookieManager;
@Inject
AppPreferences appPreferences;
@Inject
EventBus eventBus;
@BindView(R.id.progress_text)
TextView progressText;
private long internalAccountId = -1;
private List disposables = new ArrayList<>();
private String baseUrl;
private String username;
private String token;
private boolean isAccountImport;
private String originalProtocol;
public AccountVerificationController(Bundle args) {
super(args);
if (args != null) {
baseUrl = args.getString(BundleKeys.KEY_BASE_URL);
username = args.getString(BundleKeys.KEY_USERNAME);
token = args.getString(BundleKeys.KEY_TOKEN);
if (args.containsKey(BundleKeys.KEY_IS_ACCOUNT_IMPORT)) {
isAccountImport = true;
}
if (args.containsKey(BundleKeys.KEY_ORIGINAL_PROTOCOL)) {
originalProtocol = args.getString(BundleKeys.KEY_ORIGINAL_PROTOCOL);
}
}
}
@Override
protected void onAttach(@NonNull View view) {
super.onAttach(view);
eventBus.register(this);
}
@Override
protected void onDetach(@NonNull View view) {
super.onDetach(view);
eventBus.unregister(this);
}
@Override
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
return inflater.inflate(R.layout.controller_account_verification, container, false);
}
@Override
protected void onViewBound(@NonNull View view) {
super.onViewBound(view);
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
if (getActivity() != null) {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
if (getActionBar() != null) {
getActionBar().hide();
}
if (isAccountImport && !baseUrl.startsWith("http://") && !baseUrl.startsWith("https://") || (!TextUtils
.isEmpty(originalProtocol) && !baseUrl.startsWith(originalProtocol))) {
determineBaseUrlProtocol(true);
} else {
checkEverything();
}
}
private void checkEverything() {
String credentials = ApiUtils.getCredentials(username, token);
cookieManager.getCookieStore().removeAll();
findServerTalkApp(credentials);
}
private void determineBaseUrlProtocol(boolean checkForcedHttps) {
cookieManager.getCookieStore().removeAll();
String queryUrl;
baseUrl = baseUrl.replace("http://", "").replace("https://", "");
if (checkForcedHttps) {
queryUrl = "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus();
} else {
queryUrl = "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus();
}
ncApi.getServerStatus(queryUrl)
.subscribeOn(Schedulers.newThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onNext(Status status) {
if (checkForcedHttps) {
baseUrl = "https://" + baseUrl;
} else {
baseUrl = "http://" + baseUrl;
}
checkEverything();
}
@Override
public void onError(Throwable e) {
if (checkForcedHttps) {
determineBaseUrlProtocol(false);
} else {
abortVerification();
}
}
@Override
public void onComplete() {
}
});
}
private void findServerTalkApp(String credentials) {
ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(baseUrl))
.subscribeOn(Schedulers.newThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onNext(RoomsOverall roomsOverall) {
fetchProfile(credentials);
}
@Override
public void onError(Throwable e) {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> progressText.setText(String.format(getResources().getString(
R.string.nc_nextcloud_talk_app_not_installed), getResources().getString(R.string.nc_app_name))));
}
ApplicationWideMessageHolder.getInstance().setMessageType(
ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK);
abortVerification();
}
@Override
public void onComplete() {
}
});
}
private void storeProfile(String credentials, String displayName, String userId) {
userUtils.createOrUpdateUser(username, token,
baseUrl, displayName, null, true,
userId, null, null,
appPreferences.getTemporaryClientCertAlias(), null)
.subscribeOn(Schedulers.newThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onNext(UserEntity userEntity) {
internalAccountId = userEntity.getId();
if (new ClosedInterfaceImpl().isGooglePlayServicesAvailable()) {
registerForPush();
} else {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_push_disabled)));
fetchAndStoreCapabilities();
}
}
@Override
public void onError(Throwable e) {
progressText.setText(progressText.getText().toString() +
"\n" +
getResources().getString(
R.string.nc_display_name_not_stored));
abortVerification();
}
@Override
public void onComplete() {
}
});
}
private void fetchProfile(String credentials) {
ncApi.getUserProfile(credentials,
ApiUtils.getUrlForUserProfile(baseUrl))
.subscribeOn(Schedulers.newThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onNext(UserProfileOverall userProfileOverall) {
String displayName = null;
if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData()
.getDisplayName())) {
displayName = userProfileOverall.getOcs().getData()
.getDisplayName();
} else if (!TextUtils.isEmpty(userProfileOverall.getOcs().getData()
.getDisplayNameAlt())) {
displayName = userProfileOverall.getOcs().getData()
.getDisplayNameAlt();
}
if (!TextUtils.isEmpty(displayName)) {
storeProfile(credentials, displayName, userProfileOverall.getOcs().getData().getUserId());
} else {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_display_name_not_fetched)));
}
abortVerification();
}
}
@Override
public void onError(Throwable e) {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_display_name_not_fetched)));
}
abortVerification();
}
@Override
public void onComplete() {
}
});
}
private void registerForPush() {
OneTimeWorkRequest pushRegistrationWork = new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build();
WorkManager.getInstance().enqueue(pushRegistrationWork);
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageEvent(EventStatus eventStatus) {
if (eventStatus.getEventType().equals(EventStatus.EventType.PUSH_REGISTRATION)) {
if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood() && getActivity() != null) {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_push_disabled)));
}
fetchAndStoreCapabilities();
} else if (eventStatus.getEventType().equals(EventStatus.EventType.CAPABILITIES_FETCH)) {
if (internalAccountId == eventStatus.getUserId() && !eventStatus.isAllGood()) {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_capabilities_failed)));
}
abortVerification();
} else if (internalAccountId == eventStatus.getUserId() && eventStatus.isAllGood()) {
fetchAndStoreExternalSignalingSettings();
}
} else if (eventStatus.getEventType().equals(EventStatus.EventType.SIGNALING_SETTINGS)) {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> progressText.setText(progressText.getText().toString() + "\n" +
getResources().getString(R.string.nc_external_server_failed)));
}
proceedWithLogin();
}
}
private void fetchAndStoreCapabilities() {
Data userData = new Data.Builder()
.putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalAccountId)
.build();
OneTimeWorkRequest pushNotificationWork = new OneTimeWorkRequest.Builder(CapabilitiesWorker.class)
.setInputData(userData)
.build();
WorkManager.getInstance().enqueue(pushNotificationWork);
}
private void fetchAndStoreExternalSignalingSettings() {
Data userData = new Data.Builder()
.putLong(BundleKeys.KEY_INTERNAL_USER_ID, internalAccountId)
.build();
OneTimeWorkRequest signalingSettings = new OneTimeWorkRequest.Builder(SignalingSettingsJob.class)
.setInputData(userData)
.build();
WorkManager.getInstance().enqueue(signalingSettings);
}
private void proceedWithLogin() {
cookieManager.getCookieStore().removeAll();
userUtils.disableAllUsersWithoutId(internalAccountId);
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
if (userUtils.getUsers().size() == 1) {
getRouter().setRoot(RouterTransaction.with(new
MagicBottomNavigationController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {
if (isAccountImport) {
ApplicationWideMessageHolder.getInstance().setMessageType(
ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED);
}
getRouter().popToRoot();
}
});
}
}
private void dispose() {
for (int i = 0; i < disposables.size(); i++) {
if (!disposables.get(i).isDisposed()) {
disposables.get(i).dispose();
}
}
}
@Override
protected void onDestroyView(@NonNull View view) {
super.onDestroyView(view);
if (getActivity() != null) {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
}
}
@Override
public void onDestroy() {
dispose();
super.onDestroy();
}
private void abortVerification() {
if (!isAccountImport) {
if (internalAccountId != -1) {
userUtils.deleteUserWithId(internalAccountId).subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
new Handler().postDelayed(() -> getRouter().popToRoot(), 7500);
});
}
}
@Override
public void onError(Throwable e) {
}
});
} else {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
new Handler().postDelayed(() -> getRouter().popToRoot(), 7500);
});
}
}
} else {
ApplicationWideMessageHolder.getInstance().setMessageType(
ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT);
if (getActivity() != null) {
getActivity().runOnUiThread(() -> new Handler().postDelayed(() -> {
if (getRouter().hasRootController()) {
if (getActivity() != null) {
getRouter().popToRoot();
}
} else {
if (userUtils.anyUserExists()) {
getRouter().setRoot(RouterTransaction.with(new MagicBottomNavigationController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {
getRouter().setRoot(RouterTransaction.with(new ServerSelectionController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
}
}
}, 7500));
}
}
}
}