package se.alexanderblom.delicious.helpers; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import se.alexanderblom.delicious.R; import se.alexanderblom.delicious.http.Request; import se.alexanderblom.delicious.http.Response; import se.alexanderblom.delicious.http.resource.StringResource; import se.alexanderblom.delicious.util.AbstractTextWatcher; import se.alexanderblom.delicious.util.AsyncLoader; import android.app.Activity; import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; import android.content.Loader; import android.os.Bundle; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.util.Patterns; import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; public class TitleFetcher implements LoaderCallbacks<String> { private static final String TAG = "TitleFetcher"; private Activity activity; private EditText urlView; private EditText titleView; private TextWatcher titleWatcher = new AbstractTextWatcher() { @Override public void afterTextChanged(Editable s) { // If title is changed when we are fetching it, abort activity.getLoaderManager().destroyLoader(0); Log.d(TAG, "Got input, cancelling title fetch"); } }; public TitleFetcher(Activity activity) { this.activity = activity; urlView = (EditText) activity.findViewById(R.id.url); titleView = (EditText) activity.findViewById(R.id.title); urlView.setOnEditorActionListener(new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_NEXT) { maybeFetchTitle(); } // Return false to have the keyboard to the the next field return false; } }); } @Override public Loader<String> onCreateLoader(int id, Bundle args) { titleView.setHint(R.string.field_title_fetching); titleView.addTextChangedListener(titleWatcher); String url = urlView.getText().toString(); return new TitleLoader(activity, url); } @Override public void onLoadFinished(Loader<String> loader, String title) { restoreView(); titleView.setText(title); // Select text so the user can easily change it titleView.selectAll(); if (title == null) { Toast.makeText(activity, R.string.field_title_fetching_failed, Toast.LENGTH_SHORT).show(); } } @Override public void onLoaderReset(Loader<String> loader) { restoreView(); } private void restoreView() { // We don't need to watch the title anymore titleView.removeTextChangedListener(titleWatcher); titleView.setHint(R.string.field_title); } public void maybeFetchTitle() { if (TextUtils.isEmpty(titleView.getText().toString()) && Patterns.WEB_URL.matcher(urlView.getText().toString()).find()) { activity.getLoaderManager().restartLoader(0, null, this); } } private static class TitleLoader extends AsyncLoader<String> { private static final String TAG = "TitleLoader"; private static final Pattern PATTERN_TITLE = Pattern.compile("<title>(.*?)</title>"); private String url; public TitleLoader(Context context, String url) { super(context); // Ensure url is valid if (!url.startsWith("http://") && !url.startsWith("https://")) { url = "http://" + url; } this.url = url; } @Override public String loadInBackground() { try { Response response = Request.get(url).execute(); try { String html = response.as(StringResource.class); Matcher m = PATTERN_TITLE.matcher(html); if (m.find()) { return m.group(1).trim(); } else { return null; } } finally { response.disconnect(); } } catch (IOException e) { Log.e(TAG, "Failed to fetch title for: " + url, e); return null; } } } }