/*
* Copyright (C) 2010 Nullbyte <http://nullbyte.eu>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.liato.bankdroid.banking;
import com.liato.bankdroid.Helpers;
import com.liato.bankdroid.api.configuration.Field;
import com.liato.bankdroid.api.configuration.ProviderConfiguration;
import com.liato.bankdroid.banking.exceptions.BankChoiceException;
import com.liato.bankdroid.banking.exceptions.BankException;
import com.liato.bankdroid.banking.exceptions.LoginException;
import com.liato.bankdroid.legacy.R;
import com.liato.bankdroid.provider.IBankTypes;
import org.apache.commons.io.IOUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.text.InputType;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import eu.nullbyte.android.urllib.Urllib;
import timber.log.Timber;
public abstract class Bank implements Comparable<Bank>, IBankTypes {
private final ProviderConfiguration defaultConfiguration = new LegacyProviderConfiguration(this);
@DrawableRes
private final int logoResource;
/**
* URL for human-accessible web bank.
* <p/>
* Can be set to null to disable. Lots of banks don't have this any more, but have
* apps instead.
* <p/>
* @see #isWebViewEnabled()
*/
@Nullable
protected String url;
protected int inputTypeUsername = InputType.TYPE_CLASS_TEXT;
protected int inputTypePassword = InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD;
private static final int INPUT_TYPE_EXTRAS = InputType.TYPE_CLASS_TEXT;
protected String inputHintUsername = null;
private static final boolean INPUT_HIDDEN_USERNAME = false;
protected boolean inputHiddenPassword = false;
private static final boolean INPUT_HIDDEN_EXTRAS = true;
protected int inputTitletextUsername = R.string.username;
private final int INPUT_TITLETEXT_PASSWORD = R.string.password;
private final int INPUT_TITLETEXT_EXTRAS = R.string.extras_field;
protected boolean staticBalance = false;
private static final boolean BROKEN = false;
protected boolean displayDecimals = true;
/**
* Whether or not we support opening the web version of a bank.
* <p/>
* Lots of banks don't have this any more, but have apps instead.
* @see #isWebViewEnabled()
* @see #url
*/
protected boolean webViewEnabled = true;
protected Context context;
protected Resources res;
protected String customName;
protected String currency = "SEK";
protected ArrayList<Account> accounts = new ArrayList<Account>();
protected HashMap<String, Account> oldAccounts;
protected BigDecimal balance = new BigDecimal(0);
protected boolean disabled = false;
protected long dbid = -1;
protected Urllib urlopen = null;
protected boolean hideAccounts = false;
private Map<String, String> properties;
public Bank(Context context, @DrawableRes int logoResource) {
this.context = context;
this.res = this.context.getResources();
this.logoResource = logoResource;
}
public boolean toggleHideAccounts() {
hideAccounts = !hideAccounts;
return hideAccounts;
}
public boolean getHideAccounts() {
return hideAccounts;
}
public Urllib getUrlopen() {
return urlopen;
}
public void setUrlopen(Urllib urlopen) {
this.urlopen = urlopen;
}
public void setDbid(long dbid) {
this.dbid = dbid;
}
public void update(String username, String password) throws BankException, LoginException,
BankChoiceException, IOException {
setUsername(username);
setPassword(password);
this.update();
}
public void update() throws BankException, LoginException, BankChoiceException, IOException {
balance = new BigDecimal(0);
oldAccounts = new HashMap<String, Account>();
for (Account account : accounts) {
oldAccounts.put(account.getId(), account);
}
accounts = new ArrayList<Account>();
}
public void updateTransactions(Account account, Urllib urlopen) throws LoginException,
BankException, IOException {
}
public void updateAllTransactions() throws LoginException, BankException, IOException {
if (urlopen == null) {
urlopen = login();
}
for (Account account : accounts) {
updateTransactions(account, urlopen);
}
}
public Urllib login() throws LoginException, BankException, IOException {
return null;
}
public void closeConnection() {
if (urlopen != null) {
urlopen.close();
}
}
public ArrayList<Account> getAccounts() {
return this.accounts;
}
public void setAccounts(ArrayList<Account> accounts) {
this.accounts = accounts;
for (Account a : accounts) {
a.setBank(this);
}
}
public String getPassword() {
String password = getProperty(LegacyProviderConfiguration.PASSWORD);
return password == null ? "" : password;
}
public void setPassword(String password) {
getProperties().put(LegacyProviderConfiguration.PASSWORD, password);
}
public String getUsername() {
String username = getProperty(LegacyProviderConfiguration.USERNAME);
return username == null ? "" : username;
}
public void setUsername(String username) {
getProperties().put(LegacyProviderConfiguration.USERNAME, username);
}
public BigDecimal getBalance() {
if (staticBalance) {
return balance;
} else {
BigDecimal bal = new BigDecimal(0);
for (Account account : accounts) {
if (account.getType() == Account.REGULAR || account.getType() == Account.CCARD) {
if (!account.isHidden() && (account.getAliasfor() == null
|| account.getAliasfor().length() == 0)
&& account.getBalance() != null) {
bal = bal.add(account.getBalance());
}
}
}
return bal;
}
}
public abstract int getBanktypeId();
public abstract String getName();
public String getDisplayName() {
if (customName != null && customName.length() > 0) {
return customName;
}
return getUsername();
}
public String getCustomName() {
return customName;
}
public void setCustomName(String customName) {
this.customName = customName;
}
public String getExtras() {
String extras = getProperty(LegacyProviderConfiguration.EXTRAS);
return extras == null ? "" : extras;
}
public void setExtras(String extras) {
getProperties().put(LegacyProviderConfiguration.EXTRAS, extras);
}
public void setData(BigDecimal balance,
boolean disabled, long dbid, String currency, String customName,
int hideAccounts) {
this.balance = balance;
this.disabled = disabled;
this.dbid = dbid;
this.currency = currency;
this.customName = customName;
this.hideAccounts = hideAccounts == 1 ? true : false;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public long getDbId() {
return dbid;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public String getURL() {
return url;
}
public int getInputTypeUsername() {
return inputTypeUsername;
}
public int getInputTypePassword() {
return inputTypePassword;
}
public int getInputTypeExtras() {
return INPUT_TYPE_EXTRAS;
}
public String getInputHintUsername() {
return inputHintUsername;
}
public boolean isInputUsernameHidden() {
return INPUT_HIDDEN_USERNAME;
}
public boolean isInputPasswordHidden() {
return inputHiddenPassword;
}
public boolean isInputExtrasHidden() {
return INPUT_HIDDEN_EXTRAS;
}
public int getInputTitleUsername() {
return inputTitletextUsername;
}
public int getInputTitlePassword() {
return INPUT_TITLETEXT_PASSWORD;
}
public int getInputTitleExtras() {
return INPUT_TITLETEXT_EXTRAS;
}
/**
* Whether or not we support opening the web version of a bank.
* <p/>
* Lots of banks don't have this any more, but have apps instead.
* @see #webViewEnabled
* @see #url
*/
public boolean isWebViewEnabled() {
return url != null && webViewEnabled;
}
public Map<String, String> getProperties() {
if (this.properties == null) {
this.properties = new HashMap<>();
}
return this.properties;
}
public String getProperty(String name) {
return getProperties().get(name);
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
// Returns true if the current implementation of this bank is broken.
public boolean isBroken() {
return BROKEN;
}
@DrawableRes
public final int getImageResource() {
return logoResource;
}
public int compareTo(Bank another) {
return this.getName().compareToIgnoreCase(another.getName());
}
public List<Field> getConnectionConfiguration() {
return defaultConfiguration.getConnectionConfiguration();
}
public void updateComplete() {
for (Account a : this.accounts) {
//Preserve hidden and notify settings from old accounts
if (oldAccounts != null) {
Account oa = oldAccounts.get(a.getId());
if (oa != null) {
a.setHidden(oa.isHidden());
a.setNotify(oa.isNotify());
a.setCurrency(oa.getCurrency());
}
}
a.setBank(this);
}
}
public SessionPackage getSessionPackage(Context context) {
String preloader = "Error...";
try {
preloader = IOUtils.toString(context.getResources().openRawResource(R.raw.loading));
} catch (NotFoundException | IOException e1) {
Timber.w(e1, "Error getting session package");
}
try {
LoginPackage lp = preLogin();
if (lp == null) {
throw new BankException(
"No automatic login for this bank. preLogin() is not implemented or has failed.");
}
//TODO: Skip the form submission. Login using Bank.login(...) and transfer cookies to webview. The user is now logged in
// and can me directed to any page.
String html = "";
if (!lp.isLoggedIn()) {
html = String.format(preloader,
"function go(){document.getElementById('submitform').submit(); }",
// Javascript function
Helpers.renderForm(lp.getLoginTarget(), lp.getPostData())
+ "<script type=\"text/javascript\">setTimeout('go()', 1000);</script>"
// HTML
);
} else {
html = String.format(preloader,
String.format("function go(){window.location=\"%s\" }",
lp.getLoginTarget()), // Javascript function
"<script type=\"text/javascript\">setTimeout('go()', 100);</script>" // HTML
);
}
CookieStore cookies = urlopen.getHttpclient().getCookieStore();
return new SessionPackage(html, cookies);
} catch (IOException | BankException e) {
Timber.e(e, "Error getting session package");
}
String html = String.format(preloader,
String.format("function go(){window.location=\"%s\" }", this.url),
// Javascript function
"<script type=\"text/javascript\">setTimeout('go()', 1000);</script>" // HTML
);
return new SessionPackage(html, null);
}
protected LoginPackage preLogin() throws BankException, IOException {
return null;
}
public boolean getDisplayDecimals() {
return displayDecimals;
}
protected Context getContext() {
return context;
}
public DecimalFormat getDecimalFormatter() {
return null;
}
public static class SessionPackage {
private String html;
private CookieStore cookiestore;
public SessionPackage(String html, CookieStore cookiestore) {
this.html = html;
this.cookiestore = cookiestore;
}
public String getHtml() {
return html;
}
public CookieStore getCookiestore() {
return cookiestore;
}
}
public static class LoginPackage {
private String response;
private Urllib urllib;
private List<NameValuePair> postData;
private String loginTarget;
private boolean isLoggedIn = false;
public LoginPackage(Urllib urllib, List<NameValuePair> postData, String response,
String loginTarget) {
this.urllib = urllib;
this.postData = postData;
this.response = response;
this.loginTarget = loginTarget;
}
public void setIsLoggedIn(boolean loggedIn) {
this.isLoggedIn = loggedIn;
}
public String getResponse() {
return response;
}
public Urllib getUrllib() {
return urllib;
}
public List<NameValuePair> getPostData() {
return postData;
}
public String getLoginTarget() {
return loginTarget;
}
public boolean isLoggedIn() {
return this.isLoggedIn;
}
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Bank bank = (Bank) obj;
return bank.getBanktypeId() == this.getBanktypeId();
}
@Override
public int hashCode() {
return getBanktypeId();
}
}