/*
* Copyright (C) 2010 Josh Guilfoyle <jasta@devtcg.org>
*
* 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 2, 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.
*/
package org.devtcg.five.activity;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.devtcg.five.Constants;
import org.devtcg.five.R;
import org.devtcg.five.provider.util.SourceItem;
import org.devtcg.five.util.AuthHelper;
import org.devtcg.five.util.streaming.FailfastHttpClient;
import org.devtcg.util.CancelableThread;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class SourceCheckSettings extends Activity
{
private static final String HEADER_FIVE_VERSION = "X-Five-Version";
private TextView mMessage;
private VerifyThread mThread;
private boolean mThreadSent;
public static void actionCheckSettings(Activity context, Uri uri, int requestCode)
{
Intent i = new Intent(context, SourceCheckSettings.class);
i.setData(uri);
context.startActivityForResult(i, requestCode);
}
@Override
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
SourceItem source = SourceItem.getInstance(getContentResolver().query(getIntent().getData(),
null, null, null, null));
if (source == null)
throw new IllegalArgumentException("This activity needs a server source Uri");
setContentView(R.layout.source_check_settings);
Button cancel = (Button)findViewById(R.id.cancel);
cancel.setVisibility(View.VISIBLE);
findViewById(R.id.next).setVisibility(View.GONE);
mMessage = (TextView)findViewById(R.id.message);
mMessage.setText("Verifying Five server settings...");
mThread = (VerifyThread)getLastNonConfigurationInstance();
if (mThread != null)
mThread.setActivity(this);
else
{
mThread = new VerifyThread(this, source);
mThread.start();
}
}
@Override
public Object onRetainNonConfigurationInstance()
{
mThreadSent = true;
mThread.setActivity(null);
return mThread;
}
@Override
protected void onDestroy()
{
if (!mThreadSent)
{
mThread.requestCancel();
mThread.setActivity(null);
}
mThread = null;
super.onDestroy();
}
private void showErrorDialog(final String msg)
{
runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(SourceCheckSettings.this)
.setTitle("Setup failed")
.setMessage(msg)
.setCancelable(true)
.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog)
{
finish();
}
})
.setPositiveButton("OK", new OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
finish();
}
})
.show();
}
});
}
private static class VerifyThread extends CancelableThread
{
private SourceCheckSettings mActivity;
private final HttpGet mRequest;
private static final FailfastHttpClient mClient = FailfastHttpClient.newInstance(null);
public VerifyThread(SourceCheckSettings activity, SourceItem source)
{
mActivity = activity;
try {
mRequest = new HttpGet(source.getServerInfoUrl());
AuthHelper.setCredentials(mClient, source);
} finally {
source.close();
}
}
public void setActivity(SourceCheckSettings activity)
{
synchronized(this) {
mActivity = activity;
if (activity != null)
notify();
}
}
private SourceCheckSettings getActivity()
{
/*
* We assume that anywhere calling getActivity() will be in a
* non-canceled path which means that the activity must be either
* non-null, or the activity is recreating itself so it will be
* non-null very soon.
*/
synchronized(this) {
while (hasCanceled() == false && mActivity == null)
{
try {
wait();
} catch (InterruptedException e) {}
}
return mActivity;
}
}
@Override
protected void onRequestCancel()
{
mRequest.abort();
synchronized(this) {
/* Unblock getActivity() if necessary. */
notify();
}
}
public void run()
{
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
HttpEntity entity = null;
try {
HttpResponse response = mClient.execute(mRequest);
entity = response.getEntity();
if (hasCanceled())
return;
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED)
throw new IOException("invalid password");
Header versionHead = response.getFirstHeader(HEADER_FIVE_VERSION);
if (versionHead == null)
throw new IOException("not a five server");
Log.i(Constants.TAG, mRequest.getRequestLine().getUri() +
" reports five version: " + versionHead.getValue());
final SourceCheckSettings activity = getActivity();
if (activity != null)
{
activity.runOnUiThread(new Runnable() {
public void run() {
activity.setResult(RESULT_OK);
activity.finish();
}
});
}
} catch (IOException e) {
if (!hasCanceled())
{
SourceCheckSettings activity = getActivity();
if (activity != null)
activity.showErrorDialog("Error connecting to server: " + e.getMessage());
}
} finally {
if (entity != null)
{
try {
entity.consumeContent();
} catch (IOException e) {}
}
}
}
}
}