Browse Source

Added barcode scanning and handling of bar code scan results.

pull/11/head
Eric Kok 12 years ago
parent
commit
b0940b62cf
  1. 3
      core/res/values/strings.xml
  2. 71
      core/src/org/transdroid/core/app/search/BarcodeHelper.java
  3. 140
      core/src/org/transdroid/core/app/search/GoogleWebSearchBarcodeResolver.java
  4. 46
      core/src/org/transdroid/core/gui/TorrentsActivity.java

3
core/res/values/strings.xml

@ -86,7 +86,7 @@ @@ -86,7 +86,7 @@
<string name="labels_newlabel">New label</string>
<string name="labels_no_support">Setting a label is not supported by your client</string>
<string name="result_added">Torrent added (refreshing)</string>
<string name="result_added">%1$s added (refreshing)</string>
<string name="result_removed">%1$s removed</string>
<string name="result_removed_with_data">%1$s removed and data deleted</string>
<string name="result_resumed">%1$s resumed (refreshing)</string>
@ -103,6 +103,7 @@ @@ -103,6 +103,7 @@
<string name="search_torrentsearch">Torrent search</string>
<string name="search_hint">Search for torrents</string>
<string name="search_barcodescannernotfound">The Barcode Scanner could not be found. Would you like to install it from the Android Market?</string>
<string name="pref_servers">Servers</string>
<string name="pref_addserver">Add new server</string>

71
core/src/org/transdroid/core/app/search/BarcodeHelper.java

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
package org.transdroid.core.app.search;
import org.transdroid.core.R;
import org.transdroid.core.gui.TorrentsActivity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.DialogFragment;
import android.text.TextUtils;
import com.actionbarsherlock.app.SherlockFragmentActivity;
public class BarcodeHelper {
public static final int ACTIVITY_BARCODE = 0x0000c0de; // A 'random' ID to identify scan intents
public static final Uri SCANNER_MARKET_URI = Uri.parse("market://search?q=pname:com.google.zxing.client.android");
/**
* Call this to start a bar code scanner intent. The calling activity will receive an Intent result with ID
* {@link #ACTIVITY_BARCODE}. From there {@link #handleScanResult(int, Intent)} should be called to parse the result
* into a search query.
* @param activity The calling activity, to which the result is returned or a dialog is bound that asks to install
* the bar code scanner
*/
public static void startBarcodeScanner(final SherlockFragmentActivity activity) {
try {
// Start a bar code scanner that can handle the SCAN intent (specifically ZXing)
activity.startActivityForResult(new Intent("com.google.zxing.client.android.SCAN"), ACTIVITY_BARCODE);
} catch (Exception e) {
// Can't start the bar code scanner, for example with a SecurityException or when ZXing is not present
new DialogFragment() {
public android.app.Dialog onCreateDialog(android.os.Bundle savedInstanceState) {
return new AlertDialog.Builder(activity).setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(activity.getString(R.string.search_barcodescannernotfound))
.setPositiveButton(android.R.string.yes, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
activity.startActivity(new Intent(Intent.ACTION_VIEW, SCANNER_MARKET_URI));
}
}).setNegativeButton(android.R.string.no, null).create();
};
}.show(activity.getSupportFragmentManager(), "installscanner");
}
}
/**
* The activity that called {@link #startBarcodeScanner(SherlockFragmentActivity)} should call this after the scan
* result was returned. This will parse the scan data and return a query search query appropriate to the bar code.
* @param resultCode The raw result code as returned by the bar code scanner
* @param data The raw data as returned from the bar code scanner
* @return A String that can be used as new search query, or null if the bar code could not be scanned or no query
* can be constructed for it
*/
public static String handleScanResult(TorrentsActivity activity, int resultCode, Intent data) {
String contents = data.getStringExtra("SCAN_RESULT");
String formatName = data.getStringExtra("SCAN_RESULT_FORMAT");
if (formatName != null && formatName.equals("QR_CODE")) {
// Scanned barcode was a QR code: return the contents directly
return contents;
} else {
if (TextUtils.isEmpty(contents))
return null;
// Get a meaningful search query based on a Google Search product lookup
return GoogleWebSearchBarcodeResolver.resolveBarcode(contents);
}
}
}

140
core/src/org/transdroid/core/app/search/GoogleWebSearchBarcodeResolver.java

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
/*
* This file is part of Transdroid <http://www.transdroid.org>
*
* Transdroid 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.
*
* Transdroid 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 Transdroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.transdroid.core.app.search;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.daemon.util.HttpHelper;
public class GoogleWebSearchBarcodeResolver {
public static final String apiUrl = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=%s";
public static String resolveBarcode(String barcode) {
try {
// We use the Google AJAX Search API to get a JSON-formatted list of web search results
String callUrl = apiUrl.replace("%s", barcode);
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(callUrl);
HttpResponse response = httpclient.execute(httpget);
InputStream instream = response.getEntity().getContent();
String result = HttpHelper.ConvertStreamToString(instream);
JSONArray results = new JSONObject(result).getJSONObject("responseData").getJSONArray("results");
// We will combine and filter multiple results, if there are any
if (results.length() < 1) {
return null;
}
return stripGarbage(results, barcode);
} catch (Exception e) {
return null;
}
}
private static String stripGarbage(JSONArray results, String barcode) throws JSONException {
String good = " abcdefghijklmnopqrstuvwxyz";
final int MAX_TITLE_CONSIDER = 4;
final int MAX_MISSING = 1;
final int MIN_TITLE_CONSIDER = 2;
// First gather the titles for the first MAX_TITLE_CONSIDER results
List<String> titles = new ArrayList<String>();
for (int i = 0; i < results.length() && i < MAX_TITLE_CONSIDER; i++) {
String title = results.getJSONObject(i).getString("titleNoFormatting");
// Make string lowercase first
title = title.toLowerCase(Locale.US);
// Remove the barcode number if it's there
title = title.replace(barcode, "");
// Remove unwanted words and HTML special chars
for (String rem : new String[] { "dvd", "blu-ray", "bluray", "&amp;", "&quot;", "&apos;", "&lt;", "&gt;" }) {
title = title.replace(rem, "");
}
// Remove all non-alphanumeric (and space) characters
String result = "";
for ( int j = 0; j < title.length(); j++ ) {
if ( good.indexOf(title.charAt(j)) >= 0 )
result += title.charAt(j);
}
// Remove double spaces
while (result.contains(" ")) {
result = result.replace(" ", " ");
}
titles.add(result);
}
// Only retain the words that are missing in at most one of the search result titles
List<String> allWords = new ArrayList<String>();
for (String title : titles) {
for (String word : Arrays.asList(title.split(" "))) {
if (!allWords.contains(word)) {
allWords.add(word);
}
}
}
List<String> remainingWords = new ArrayList<String>();
int allowMissing = Math.min(MAX_MISSING, Math.max(titles.size() - MIN_TITLE_CONSIDER, 0));
for (String word : allWords) {
int missing = 0;
for (String title : titles) {
if (!title.contains(word)) {
// The word is not contained in this result title
missing++;
if (missing > allowMissing) {
// Already misssing more than once, no need to look further
break;
}
}
}
if (missing <= allowMissing) {
// The word was only missing at most once, so we keep it
remainingWords.add(word);
}
}
// Now the query is the concatenation of the words remaining; with spaces in between
String query = "";
for (String word : remainingWords) {
query += " " + word;
}
return query.length() > 0? query.substring(1): null;
}
}

46
core/src/org/transdroid/core/gui/TorrentsActivity.java

@ -9,12 +9,14 @@ import org.androidannotations.annotations.Bean; @@ -9,12 +9,14 @@ import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.FragmentById;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.OnActivityResult;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.SystemService;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;
import org.transdroid.core.R;
import org.transdroid.core.app.search.BarcodeHelper;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.gui.lists.LocalTorrent;
@ -33,6 +35,7 @@ import org.transdroid.core.gui.settings.MainSettingsActivity_; @@ -33,6 +35,7 @@ import org.transdroid.core.gui.settings.MainSettingsActivity_;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.DaemonTaskSuccessResult;
@ -95,11 +98,9 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @@ -95,11 +98,9 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi
@InstanceState
protected boolean turleModeEnabled = false;
// Torrents list components
// Contained torrent and details fragments
@FragmentById(resName = "torrent_list")
protected TorrentsFragment fragmentTorrents;
// Details view components
@FragmentById(resName = "torrent_details")
protected DetailsFragment fragmentDetails;
@ -322,6 +323,30 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @@ -322,6 +323,30 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi
// TODO: Handle start intent
}
@OptionsItem(resName = "action_add_fromurl")
protected void startUrlEntryDialog() {
// TODO: Open URL input dialog
}
@OptionsItem(resName = "action_add_fromfile")
protected void startFilePicker() {
// TODO: Start file picker
}
@OptionsItem(resName = "action_add_frombarcode")
protected void startBarcodeScanner() {
BarcodeHelper.startBarcodeScanner(this);
}
@OnActivityResult(BarcodeHelper.ACTIVITY_BARCODE)
public void onBarcodeScanned(int resultCode, Intent data) {
String query = BarcodeHelper.handleScanResult(this, resultCode, data);
if (query.startsWith("http"))
addTorrentByUrl(query, "QR code result"); // No torrent title known
else
startSearch(query, false, null, false);
}
@OptionsItem(resName = "action_refresh")
protected void refreshScreen() {
fragmentTorrents.updateIsLoading(true);
@ -340,6 +365,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @@ -340,6 +365,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi
updateTurtleMode(false);
}
@OptionsItem(resName = "action_filter")
protected void filterList() {
}
@OptionsItem(resName = "action_settings")
protected void openSettings() {
MainSettingsActivity_.intent(this).start();
@ -382,6 +411,17 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @@ -382,6 +411,17 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi
}
}
@Background
public void addTorrentByUrl(String url, String title) {
DaemonTaskResult result = AddByUrlTask.create(currentConnection, url, title).execute();
if (result instanceof DaemonTaskResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_added, title);
refreshTorrents();
} else {
onCommunicationError((DaemonTaskFailureResult) result);
}
}
@Background
@Override
public void resumeTorrent(Torrent torrent) {

Loading…
Cancel
Save