package org.pyload.android.client;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import javax.net.ssl.*;
import android.annotation.TargetApi;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSSLTransportFactory;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.pyload.android.client.components.TabHandler;
import org.pyload.android.client.exceptions.WrongLogin;
import org.pyload.android.client.exceptions.WrongServer;
import org.pyload.android.client.module.AllTrustManager;
import org.pyload.android.client.module.GuiTask;
import org.pyload.android.client.module.TaskQueue;
import org.pyload.thrift.Pyload.Client;
import android.app.Application;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.Toast;
public class pyLoadApp extends Application {
private Client client;
// setted by main activity
private TaskQueue taskQueue;
private Throwable lastException;
public SharedPreferences prefs;
public ConnectivityManager cm;
private pyLoad main;
private boolean captchaNotificationShown;
private static final String[] clientVersion = {"0.4.8", "0.4.9"};
public void init(pyLoad main) {
this.main = main;
HashMap<Throwable, Runnable> map = new HashMap<Throwable, Runnable>();
map.put(new TException(), handleException);
map.put(new WrongLogin(), handleException);
map.put(new TTransportException(), handleException);
map.put(new WrongServer(), handleException);
taskQueue = new TaskQueue(this, new Handler(), map);
startTaskQueue();
}
public String verboseBool(boolean state) {
if (state)
return getString(R.string.on);
else
return getString(R.string.off);
}
private boolean login() throws TException {
// replace protocol, some user also enter it
String host = prefs.getString("host", "10.0.2.2").replaceFirst("^[a-zA-z]+://", "");
int port = Integer.parseInt(prefs.getString("port", "7227"));
String username = prefs.getString("username", "User");
String password = prefs.getString("password", "pwhere");
// TODO: better exception handling
TTransport trans;
try {
if (prefs.getBoolean("ssl", false)) {
SSLContext ctx;
TrustManager[] trustManagers;
try {
if (prefs.getBoolean("ssl_validate", true)) {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
trustManagers = tmf.getTrustManagers();
} else {
trustManagers = new TrustManager[1];
trustManagers[0] = new AllTrustManager();
}
ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustManagers, null);
Log.d("pyLoad", "SSL Context created");
} catch (NoSuchAlgorithmException e) {
throw new TException(e);
} catch (KeyStoreException e) {
throw new TException(e);
} catch (KeyManagementException e) {
throw new TException(e);
}
// timeout 8000ms
trans = TSSLTransportFactory.createClient(ctx.getSocketFactory(), host, port, 8000);
if (prefs.getBoolean("ssl_validate", true)) {
X509HostnameVerifier verifier = new BrowserCompatHostnameVerifier();
try {
verifier.verify(host, (SSLSocket) ((TSocket) trans).getSocket());
} catch (IOException e) {
throw new TException(e);
}
// TODO: check OCSP/CRL
}
} else {
trans = new TSocket(host, port, 8000);
trans.open();
}
} catch (TTransportException e) {
throw new TException(e);
}
TProtocol iprot = new TBinaryProtocol(trans);
client = new Client(iprot);
return client.login(username, password);
}
public Client getClient() throws TException, WrongLogin {
if (client == null) {
Log.d("pyLoad", "Creating new Client");
boolean loggedin = login();
if (!loggedin) {
client = null;
throw new WrongLogin();
}
String server = client.getServerVersion();
boolean match = false;
for (String version : clientVersion)
if(server.equals(version))
match = true;
if (!match)
throw new WrongServer();
}
return client;
}
public void addTask(GuiTask task) {
taskQueue.addTask(task);
}
public void startTaskQueue() {
taskQueue.start();
}
final public Runnable handleException = new Runnable() {
public void run() {
onException();
}
};
public void onException() {
client = null;
// The task queue will log an error with exception
if (lastException instanceof TTransportException) {
Toast t = Toast.makeText(this, R.string.lost_connection,
Toast.LENGTH_SHORT);
t.show();
} else if (lastException instanceof WrongLogin) {
Toast t = Toast.makeText(this, R.string.bad_login,
Toast.LENGTH_SHORT);
t.show();
} else if (lastException instanceof TException) {
Throwable tr = findException(lastException);
Toast t;
if (tr instanceof SSLHandshakeException)
t = Toast.makeText(this, R.string.certificate_error, Toast.LENGTH_SHORT);
else if(tr instanceof SocketTimeoutException)
t = Toast.makeText(this, R.string.connect_timeout, Toast.LENGTH_SHORT);
else if(tr instanceof ConnectException)
t = Toast.makeText(this, R.string.connect_error, Toast.LENGTH_SHORT);
else if(tr instanceof SocketException)
t = Toast.makeText(this, R.string.socket_error, Toast.LENGTH_SHORT);
else
t = Toast.makeText(this, getString(R.string.no_connection)+ " " + tr.getMessage(), Toast.LENGTH_SHORT);
t.show();
} else if (lastException instanceof WrongServer) {
Toast t = Toast.makeText(this, String.format(
getString(R.string.old_server), clientVersion[clientVersion.length-1]),
Toast.LENGTH_SHORT);
t.show();
}
setProgress(false);
}
/**
* Retrieves first root exception on stack of several TExceptions.
* @return the first exception not a TException or the last TException
*/
private Throwable findException(Throwable e) {
// will not terminate when cycles occur, hopefully nobody cycle exception causes
while (e instanceof TException) {
if (e.getCause() == null) break;
if (e.getCause() == e) break; // just to avoid loop
e = e.getCause();
}
return e;
}
final public Runnable handleSuccess = new Runnable() {
@Override
public void run() {
onSuccess();
}
};
public void onSuccess() {
Toast t = Toast.makeText(this, R.string.success, Toast.LENGTH_SHORT);
t.show();
refreshTab();
}
public void refreshTab() {
Fragment frag = main.getCurrentFragment();
Log.d("pyLoad", "Refreshing Tab: " + frag);
if (frag != null)
((TabHandler) frag).onSelected();
}
public boolean isCurrentTab(int pos) {
return main.getCurrentTab() == pos;
}
public pyLoad getMain() {
return main;
}
public boolean hasConnection() {
NetworkInfo info = cm.getActiveNetworkInfo();
// TODO investigate network states, info etc
return info != null;
}
public void clearTasks() {
taskQueue.clear();
}
public void setLastException(Throwable t) {
lastException = t;
}
public void resetClient() {
Log.d("pyLoad", "Client resetted");
client = null;
}
/**
* Enables and disables the progress indicator.
*
* The indicator depends on the user's Android version.
* pre-actionBar devices: Window.FEATURE_INDETERMINATE_PROGRESS
* actionBar devices: set refreshAction's view to a progress wheel (Gmail like)
*
* @param state
*/
public void setProgress(boolean state) {
if (isActionBarAvailable()) {
setIndeterminateProgress(main.getRefreshItem(), state);
} else {
setIndeterminateProgress(state);
}
}
@TargetApi(5)
private void setIndeterminateProgress(boolean state) {
main.setProgressBarIndeterminateVisibility(state);
}
@TargetApi(11)
private void setIndeterminateProgress(MenuItem item, boolean state) {
if (item == null) {
return;
}
if (state) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View progress = inflater.inflate(R.layout.progress_wheel, null);
main.getRefreshItem().setActionView(progress);
} else {
item.setActionView(null);
}
}
public static boolean isActionBarAvailable() {
return android.os.Build.VERSION.SDK_INT >= 11;
}
public void setCaptchaNotificationShown(boolean val)
{
captchaNotificationShown = val;
}
public boolean getCaptchaNotificationShown()
{
return captchaNotificationShown;
}
}