]> rtime.felk.cvut.cz Git - hornmich/skoda-qr-demo.git/blob - BarCodeScanner/mobile/src/main/java/cz/cvut/fel/dce/barcodescanner/book/SearchBookContentsActivity.java
Initial commit
[hornmich/skoda-qr-demo.git] / BarCodeScanner / mobile / src / main / java / cz / cvut / fel / dce / barcodescanner / book / SearchBookContentsActivity.java
1 /*
2  * Copyright (C) 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package cz.cvut.fel.dce.barcodescanner.book;
18
19 import android.app.Activity;
20 import android.content.Intent;
21 import android.os.AsyncTask;
22 import android.os.Bundle;
23 import android.util.Log;
24 import android.view.KeyEvent;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.webkit.CookieManager;
28 import android.webkit.CookieSyncManager;
29 import android.widget.EditText;
30 import android.widget.ListView;
31 import android.widget.TextView;
32
33 import cz.cvut.fel.dce.barcodescanner.HttpHelper;
34 import cz.cvut.fel.dce.barcodescanner.Intents;
35 import cz.cvut.fel.dce.barcodescanner.LocaleManager;
36 import cz.cvut.fel.dce.barcodescanner.R;
37
38 import org.json.JSONArray;
39 import org.json.JSONException;
40 import org.json.JSONObject;
41
42 import java.io.IOException;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.regex.Pattern;
46
47 /**
48  * Uses Google Book Search to find a word or phrase in the requested book.
49  *
50  * @author dswitkin@google.com (Daniel Switkin)
51  */
52 public final class SearchBookContentsActivity extends Activity {
53
54   private static final String TAG = SearchBookContentsActivity.class.getSimpleName();
55
56   private static final Pattern TAG_PATTERN = Pattern.compile("\\<.*?\\>");
57   private static final Pattern LT_ENTITY_PATTERN = Pattern.compile("&lt;");
58   private static final Pattern GT_ENTITY_PATTERN = Pattern.compile("&gt;");
59   private static final Pattern QUOTE_ENTITY_PATTERN = Pattern.compile("&#39;");
60   private static final Pattern QUOT_ENTITY_PATTERN = Pattern.compile("&quot;");
61
62   private String isbn;
63   private EditText queryTextView;
64   private View queryButton;
65   private ListView resultListView;
66   private TextView headerView;
67   private AsyncTask<String,?,?> networkTask;
68
69   private final View.OnClickListener buttonListener = new View.OnClickListener() {
70     @Override
71     public void onClick(View view) {
72       launchSearch();
73     }
74   };
75
76   private final View.OnKeyListener keyListener = new View.OnKeyListener() {
77     @Override
78     public boolean onKey(View view, int keyCode, KeyEvent event) {
79       if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
80         launchSearch();
81         return true;
82       }
83       return false;
84     }
85   };
86
87   String getISBN() {
88     return isbn;
89   }
90
91   @Override
92   public void onCreate(Bundle icicle) {
93     super.onCreate(icicle);
94
95     // Make sure that expired cookies are removed on launch.
96     CookieSyncManager.createInstance(this);
97     CookieManager.getInstance().removeExpiredCookie();
98
99     Intent intent = getIntent();
100     if (intent == null || !intent.getAction().equals(Intents.SearchBookContents.ACTION)) {
101       finish();
102       return;
103     }
104
105     isbn = intent.getStringExtra(Intents.SearchBookContents.ISBN);
106     if (LocaleManager.isBookSearchUrl(isbn)) {
107       setTitle(getString(R.string.sbc_name));
108     } else {
109       setTitle(getString(R.string.sbc_name) + ": ISBN " + isbn);
110     }
111
112     setContentView(R.layout.search_book_contents);
113     queryTextView = (EditText) findViewById(R.id.query_text_view);
114
115     String initialQuery = intent.getStringExtra(Intents.SearchBookContents.QUERY);
116     if (initialQuery != null && !initialQuery.isEmpty()) {
117       // Populate the search box but don't trigger the search
118       queryTextView.setText(initialQuery);
119     }
120     queryTextView.setOnKeyListener(keyListener);
121
122     queryButton = findViewById(R.id.query_button);
123     queryButton.setOnClickListener(buttonListener);
124
125     resultListView = (ListView) findViewById(R.id.result_list_view);
126     LayoutInflater factory = LayoutInflater.from(this);
127     headerView = (TextView) factory.inflate(R.layout.search_book_contents_header,
128         resultListView, false);
129     resultListView.addHeaderView(headerView);
130   }
131
132   @Override
133   protected void onResume() {
134     super.onResume();
135     queryTextView.selectAll();
136   }
137
138   @Override
139   protected void onPause() {
140     AsyncTask<?,?,?> oldTask = networkTask;
141     if (oldTask != null) {
142       oldTask.cancel(true);
143       networkTask = null;
144     }
145     super.onPause();
146   }
147
148   private void launchSearch() {
149     String query = queryTextView.getText().toString();
150     if (query != null && !query.isEmpty()) {
151       AsyncTask<?,?,?> oldTask = networkTask;
152       if (oldTask != null) {
153         oldTask.cancel(true);
154       }
155       networkTask = new NetworkTask();
156       networkTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, query, isbn);
157       headerView.setText(R.string.msg_sbc_searching_book);
158       resultListView.setAdapter(null);
159       queryTextView.setEnabled(false);
160       queryButton.setEnabled(false);
161     }
162   }
163
164   private final class NetworkTask extends AsyncTask<String,Object,JSONObject> {
165
166     @Override
167     protected JSONObject doInBackground(String... args) {
168       try {
169         // These return a JSON result which describes if and where the query was found. This API may
170         // break or disappear at any time in the future. Since this is an API call rather than a
171         // website, we don't use LocaleManager to change the TLD.
172         String theQuery = args[0];
173         String theIsbn = args[1];
174         String uri;
175         if (LocaleManager.isBookSearchUrl(theIsbn)) {
176           int equals = theIsbn.indexOf('=');
177           String volumeId = theIsbn.substring(equals + 1);
178           uri = "http://www.google.com/books?id=" + volumeId + "&jscmd=SearchWithinVolume2&q=" + theQuery;
179         } else {
180           uri = "http://www.google.com/books?vid=isbn" + theIsbn + "&jscmd=SearchWithinVolume2&q=" + theQuery;
181         }
182         CharSequence content = HttpHelper.downloadViaHttp(uri, HttpHelper.ContentType.JSON);
183         return new JSONObject(content.toString());
184       } catch (IOException ioe) {
185         Log.w(TAG, "Error accessing book search", ioe);
186         return null;
187       } catch (JSONException je) {
188         Log.w(TAG, "Error accessing book search", je);
189         return null;
190       }
191     }
192
193     @Override
194     protected void onPostExecute(JSONObject result) {
195       if (result == null) {
196         headerView.setText(R.string.msg_sbc_failed);
197       } else {
198         handleSearchResults(result);
199       }
200       queryTextView.setEnabled(true);
201       queryTextView.selectAll();
202       queryButton.setEnabled(true);
203     }
204
205     // Currently there is no way to distinguish between a query which had no results and a book
206     // which is not searchable - both return zero results.
207     private void handleSearchResults(JSONObject json) {
208       try {
209         int count = json.getInt("number_of_results");
210         headerView.setText(getString(R.string.msg_sbc_results) + " : " + count);
211         if (count > 0) {
212           JSONArray results = json.getJSONArray("search_results");
213           SearchBookContentsResult.setQuery(queryTextView.getText().toString());
214           List<SearchBookContentsResult> items = new ArrayList<>(count);
215           for (int x = 0; x < count; x++) {
216             items.add(parseResult(results.getJSONObject(x)));
217           }
218           resultListView.setOnItemClickListener(new BrowseBookListener(SearchBookContentsActivity.this, items));
219           resultListView.setAdapter(new SearchBookContentsAdapter(SearchBookContentsActivity.this, items));
220         } else {
221           String searchable = json.optString("searchable");
222           if ("false".equals(searchable)) {
223             headerView.setText(R.string.msg_sbc_book_not_searchable);
224           }
225           resultListView.setAdapter(null);
226         }
227       } catch (JSONException e) {
228         Log.w(TAG, "Bad JSON from book search", e);
229         resultListView.setAdapter(null);
230         headerView.setText(R.string.msg_sbc_failed);
231       }
232     }
233
234     // Available fields: page_id, page_number, snippet_text
235     private SearchBookContentsResult parseResult(JSONObject json) {
236
237       String pageId;
238       String pageNumber;
239       String snippet;
240       try {
241         pageId = json.getString("page_id");
242         pageNumber = json.optString("page_number");
243         snippet = json.optString("snippet_text");        
244       } catch (JSONException e) {
245         Log.w(TAG, e);
246         // Never seen in the wild, just being complete.
247         return new SearchBookContentsResult(getString(R.string.msg_sbc_no_page_returned), "", "", false);
248       }
249       
250       if (pageNumber == null || pageNumber.isEmpty()) {
251         // This can happen for text on the jacket, and possibly other reasons.
252         pageNumber = "";
253       } else {
254         pageNumber = getString(R.string.msg_sbc_page) + ' ' + pageNumber;
255       }
256       
257       boolean valid = snippet != null && !snippet.isEmpty();
258       if (valid) {
259         // Remove all HTML tags and encoded characters.          
260         snippet = TAG_PATTERN.matcher(snippet).replaceAll("");
261         snippet = LT_ENTITY_PATTERN.matcher(snippet).replaceAll("<");
262         snippet = GT_ENTITY_PATTERN.matcher(snippet).replaceAll(">");
263         snippet = QUOTE_ENTITY_PATTERN.matcher(snippet).replaceAll("'");
264         snippet = QUOT_ENTITY_PATTERN.matcher(snippet).replaceAll("\"");
265       } else {
266         snippet = '(' + getString(R.string.msg_sbc_snippet_unavailable) + ')';        
267       }
268
269       return new SearchBookContentsResult(pageId, pageNumber, snippet, valid);
270     }
271
272   }
273
274 }