diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bc47fd647..46a679c06 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,11 +14,17 @@ + + diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java index e398145e0..454630b00 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java @@ -20,6 +20,8 @@ package com.nextcloud.talk.adapters.items; +import android.accounts.Account; +import android.support.annotation.Nullable; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -52,10 +54,12 @@ public class AdvancedUserItem extends AbstractFlexibleItem checkServerAndProceed()); - if (TextUtils.isEmpty(getResources().getString(R.string.nc_providers_url))) { + if (TextUtils.isEmpty(getResources().getString(R.string.nc_providers_url)) && (TextUtils.isEmpty(getResources + ().getString(R.string.nc_import_account_type)))) { providersTextView.setVisibility(View.GONE); } else { - providersTextView.setOnClickListener(view12 -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources() - .getString(R.string.nc_providers_url))); - startActivity(browserIntent); - }); + if ((TextUtils.isEmpty(getResources + ().getString(R.string.nc_import_account_type)) || AccountUtils.findAccounts().size() == 0) && + userUtils.getUsers().size() == 0) { + + providersTextView.setText(R.string.nc_get_from_provider); + providersTextView.setOnClickListener(view12 -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getResources() + .getString(R.string.nc_providers_url))); + startActivity(browserIntent); + }); + } else if (AccountUtils.findAccounts().size() > 0) { + if (!TextUtils.isEmpty(AccountUtils.getAppNameBasedOnPackage(getResources() + .getString(R.string.nc_import_accounts_from)))) { + providersTextView.setText(String.format(getResources().getString(R.string + .nc_server_import_accounts), AccountUtils.getAppNameBasedOnPackage(getResources() + .getString(R.string.nc_import_accounts_from)))); + } else { + providersTextView.setText(getResources().getString(R.string.nc_server_import_accounts_plain_plural)); + } + + providersTextView.setOnClickListener(view13 -> { + Bundle bundle = new Bundle(); + bundle.putBoolean("isAccountImport", true); + getRouter().pushController(RouterTransaction.with( + new SwitchAccountController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + }); + } else { + providersTextView.setVisibility(View.GONE); + } } serverEntry.requestFocus(); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java index 7bd417e73..6fa1d0fce 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java @@ -16,15 +16,27 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Parts related to account import were either copied from or inspired by the great work done by David Luhmer at: + * https://github.com/nextcloud/ownCloud-Account-Importer */ package com.nextcloud.talk.controllers; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.OperationCanceledException; +import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -34,7 +46,9 @@ import com.nextcloud.talk.adapters.items.AdvancedUserItem; import com.nextcloud.talk.api.models.json.participants.Participant; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; +import com.nextcloud.talk.models.ImportAccount; import com.nextcloud.talk.persistence.entities.UserEntity; +import com.nextcloud.talk.utils.AccountUtils; import com.nextcloud.talk.utils.database.user.UserUtils; import java.net.CookieManager; @@ -54,6 +68,7 @@ import io.reactivex.disposables.Disposable; @AutoInjector(NextcloudTalkApplication.class) public class SwitchAccountController extends BaseController { + private static final String TAG = "SwitchAccountController"; @Inject UserUtils userUtils; @@ -68,7 +83,21 @@ public class SwitchAccountController extends BaseController { private FlexibleAdapter adapter; private List userItems = new ArrayList<>(); - private FlexibleAdapter.OnItemClickListener onItemClickListener = + private boolean isAccountImport = false; + + private FlexibleAdapter.OnItemClickListener onImportItemClickListener = new FlexibleAdapter.OnItemClickListener() { + @Override + public boolean onItemClick(int position) { + if (userItems.size() > position) { + Account account = ((AdvancedUserItem) userItems.get(position)).getAccount(); + getAuthTokenForAccount(account); + } + + return true; + } + }; + + private FlexibleAdapter.OnItemClickListener onSwitchItemClickListener = new FlexibleAdapter.OnItemClickListener() { @Override public boolean onItemClick(int position) { @@ -106,6 +135,17 @@ public class SwitchAccountController extends BaseController { } }; + public SwitchAccountController() { + } + + public SwitchAccountController(Bundle args) { + super(args); + + if (args.containsKey("isAccountImport")) { + isAccountImport = true; + } + } + @Override protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { return inflater.inflate(R.layout.controller_generic_rv, container, false); @@ -123,19 +163,39 @@ public class SwitchAccountController extends BaseController { UserEntity userEntity; Participant participant; - for (Object userEntityObject : userUtils.getUsers()) { - userEntity = (UserEntity) userEntityObject; - if (!userEntity.getCurrent()) { - participant = new Participant(); - participant.setName(userEntity.getDisplayName()); - participant.setUserId(userEntity.getUsername()); - userItems.add(new AdvancedUserItem(participant, userEntity)); + if (!isAccountImport) { + for (Object userEntityObject : userUtils.getUsers()) { + userEntity = (UserEntity) userEntityObject; + if (!userEntity.getCurrent()) { + participant = new Participant(); + participant.setName(userEntity.getDisplayName()); + participant.setUserId(userEntity.getUsername()); + userItems.add(new AdvancedUserItem(participant, userEntity, null)); + } } - } - adapter.addListener(onItemClickListener); - adapter.updateDataSet(userItems, false); - } + adapter.addListener(onSwitchItemClickListener); + adapter.updateDataSet(userItems, false); + } else { + getActionBar().show(); + Account account; + ImportAccount importAccount; + for (Object accountObject : AccountUtils.findAccounts(userUtils.getUsers())) { + account = (Account) accountObject; + importAccount = AccountUtils.getInformationFromAccount(account, null); + + participant = new Participant(); + participant.setName(importAccount.getUsername()); + participant.setUserId(importAccount.getUsername()); + userEntity = new UserEntity(); + userEntity.setBaseUrl(importAccount.getServerUrl()); + userItems.add(new AdvancedUserItem(participant, userEntity, account)); + } + } + + adapter.addListener(onSwitchItemClickListener); + adapter.updateDataSet(userItems, false); + } prepareViews(); } @@ -154,10 +214,46 @@ public class SwitchAccountController extends BaseController { swipeRefreshLayout.setEnabled(false); } + private void getAuthTokenForAccount(Account account) { + final AccountManager accMgr = AccountManager.get(getActivity()); + + final AlertDialog alertDialog = new AlertDialog.Builder(getActivity()) + .setTitle(getResources().getString(R.string.nc_server_import_accounts_plain_singular)) + .setMessage(getResources().getString(R.string.nc_server_import_account_notification)) + .create(); + + alertDialog.show(); + + String authTokenType = getResources().getString(R.string.nc_import_account_type) + ".password"; + + final Handler handler = new Handler(); + accMgr.getAuthToken(account, authTokenType, true, + new AccountManagerCallback() { + + @Override + public void run(AccountManagerFuture future) { + + try { + ImportAccount importAccount = AccountUtils.getInformationFromAccount(account, future + .getResult()); + } catch (OperationCanceledException e) { + Log.e(TAG, "Access was denied"); + // TODO: The user has denied you access to the API, handle this later on + } catch (Exception e) { + Log.e(TAG, "Something went wrong while accessing token"); + } + + alertDialog.dismiss(); + + } + }, handler + + ); + + } + @Override protected String getTitle() { return getResources().getString(R.string.nc_select_an_account); } - - } diff --git a/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java b/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java new file mode 100644 index 000000000..d25baae83 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/ImportAccount.java @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.models; + +import android.support.annotation.Nullable; + +import lombok.Data; + +@Data +public class ImportAccount { + public String username; + @Nullable public String token; + public String serverUrl; + + public ImportAccount(String username, @Nullable String token, String serverUrl) { + this.username = username; + this.token = token; + this.serverUrl = serverUrl; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java new file mode 100644 index 000000000..3f4f8e383 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java @@ -0,0 +1,122 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Parts related to account import were either copied from or inspired by the great work done by David Luhmer at: + * https://github.com/nextcloud/ownCloud-Account-Importer + */ + +package com.nextcloud.talk.utils; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.nextcloud.talk.R; +import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.models.ImportAccount; +import com.nextcloud.talk.persistence.entities.UserEntity; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +public class AccountUtils { + + private static final String TAG = "AccountUtils"; + + public static List findAccounts(List userEntitiesList) { + Context context = NextcloudTalkApplication.getSharedApplication().getApplicationContext(); + final AccountManager accMgr = AccountManager.get(context); + final Account[] accounts = accMgr.getAccounts(); + + List accountsAvailable = new ArrayList<>(); + ImportAccount importAccount; + UserEntity internalUserEntity; + boolean accountFound; + for (Account account : accounts) { + accountFound = false; + String accountType = account.type.intern(); + + if (context.getResources().getString(R.string.nc_import_account_type).equals(accountType)) { + for (int i = 0; i < userEntitiesList.size(); i++) { + internalUserEntity = userEntitiesList.get(i); + importAccount = getInformationFromAccount(account, null); + if (internalUserEntity.getUsername().equals(importAccount.getUsername()) && + internalUserEntity.getBaseUrl().equals(importAccount.getServerUrl())) { + accountFound = true; + break; + } + } + + if (!accountFound) { + accountsAvailable.add(account); + } + } + } + + return accountsAvailable; + } + + public static String getAppNameBasedOnPackage(String packageName) { + Context context = NextcloudTalkApplication.getSharedApplication().getApplicationContext(); + PackageManager packageManager = context.getPackageManager(); + String appName = ""; + try { + appName = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Failed to get app name based on package"); + } + + return appName; + } + + public static ImportAccount getInformationFromAccount(Account account, @Nullable Bundle data) { + int lastAtPos = account.name.lastIndexOf("@"); + String urlString = account.name.substring(lastAtPos + 1); + String username = account.name.substring(0, lastAtPos); + + if (!urlString.startsWith("http")) + urlString = "https://" + urlString; + + String password = null; + if (data != null) { + password = data.getString(AccountManager.KEY_AUTHTOKEN); + } + + try { + final String urlStringOrig = urlString; + URL url = new URL(urlStringOrig); + urlString = url.getProtocol() + "://" + url.getHost(); + if (url.getPath().contains("/owncloud")) { + urlString += url.getPath().substring(0, url.getPath().indexOf("/owncloud") + 9); + } else if (url.getPath().contains("/")) { + urlString += url.getPath().substring(0, url.getPath().indexOf("/")); + } + } catch (Exception ex) { + Log.e(TAG, "Something went wrong while trying to create url string"); + } + + return new ImportAccount(username, password, urlString); + } +} + diff --git a/app/src/main/res/layout/controller_server_selection.xml b/app/src/main/res/layout/controller_server_selection.xml index ddad2be0d..1b9a70e86 100644 --- a/app/src/main/res/layout/controller_server_selection.xml +++ b/app/src/main/res/layout/controller_server_selection.xml @@ -80,7 +80,7 @@ android:visibility="invisible"/> https://www.gnu.org/licenses/gpl-3.0.en.html https://github.com/nextcloud/talk-android https://nextcloud.com/providers + + + com.nextcloud.client + nextcloud diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 147f8d454..8d773534d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -11,6 +11,10 @@ Please upgrade your %1$s database Please bring your %1$s out of maintenance %1$s only works with %2$s 13 and up + Import account + Import accounts + Import accounts from the %1$s app + Please grant access to the selected account in the notification bar!" Do you not have a server yet?\nClick here to get one from a provider