diff --git a/AIS.iml b/AIS.iml index 7951f97..da10a04 100644 --- a/AIS.iml +++ b/AIS.iml @@ -24,9 +24,12 @@ + + + diff --git a/pom.xml b/pom.xml index ca53b68..a7cfd34 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 4.0.0 ca.dracode ais - 0.4.0 + 0.5.1 apk AndroidIndexingService diff --git a/src/ca/dracode/ais/alarm/Alarm.java b/src/ca/dracode/ais/alarm/Alarm.java index 96fa368..46a7dd7 100644 --- a/src/ca/dracode/ais/alarm/Alarm.java +++ b/src/ca/dracode/ais/alarm/Alarm.java @@ -32,49 +32,65 @@ public class Alarm extends BroadcastReceiver { - public static void SetAlarm(Context context) { - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - Intent i = new Intent(context, Alarm.class); - PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); - // sets the alarm to repeat every 10 minutes - // TODO - Make alarm time change according to a user preference - if(am != null) - am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), getMinutes(10), pi); - } + /** + * Starts the wakeup that calls for the device to be re-indexed after a certain period + * @param context + */ + public static void SetAlarm(Context context) { + AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent i = new Intent(context, Alarm.class); + PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); + // sets the alarm to repeat every 10 minutes + // TODO - Make alarm time change according to a user preference + if(am != null) + am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), + 10 * 60000, pi); + } - public static void CancelAlarm(Context context) { - Intent i = new Intent(context, Alarm.class); - PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); - AlarmManager am = (AlarmManager) context.getSystemService("Context.ALARM_SERVICE"); - if(am != null) { + /** + * Cancels the alerm started in SetAlarm(context) if it has been set + * @param context + */ + public static void CancelAlarm(Context context) { + Intent i = new Intent(context, Alarm.class); + PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); + AlarmManager am = (AlarmManager) context.getSystemService("Context.ALARM_SERVICE"); + if(am != null) { am.cancel(pi); } - } + } - private static int getMinutes(int minutes) { - return minutes * 60000; - } - - public void onReceive(Context context, Intent intent) { - // Starts the indexService + /** + * Starts the indexer if it is not already running + * @param context + * @param intent + */ + public void onReceive(Context context, Intent intent) { + // Starts the indexService SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - if (!this.isMyServiceRunning(context) && prefs.getBoolean("enabled", true)) { - Intent serviceIntent = new Intent(context, IndexService.class); + if(!this.isMyServiceRunning(context) && prefs.getBoolean("enabled", true)) { + Intent serviceIntent = new Intent(context, IndexService.class); serviceIntent.putExtra("crawl", true); - context.startService(serviceIntent); - } - } + context.startService(serviceIntent); + } + } - private boolean isMyServiceRunning(Context c) { - ActivityManager manager = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningServiceInfo service : manager - .getRunningServices(Integer.MAX_VALUE)) { - if (IndexService.class.getName().equals( - service.service.getClassName())) { - return true; - } - } - return false; - } + /** + * Checks if the indexing service is running + * @param context + * @return true if the service was found; false otherwise + */ + private boolean isMyServiceRunning(Context context) { + ActivityManager manager = (ActivityManager) context.getSystemService(Context + .ACTIVITY_SERVICE); + for(ActivityManager.RunningServiceInfo service : manager + .getRunningServices(Integer.MAX_VALUE)) { + if(IndexService.class.getName().equals( + service.service.getClassName())) { + return true; + } + } + return false; + } } diff --git a/src/ca/dracode/ais/alarm/AutoStart.java b/src/ca/dracode/ais/alarm/AutoStart.java index 708c3fb..c7efdcd 100644 --- a/src/ca/dracode/ais/alarm/AutoStart.java +++ b/src/ca/dracode/ais/alarm/AutoStart.java @@ -29,13 +29,18 @@ public class AutoStart extends BroadcastReceiver { - public void onReceive(Context context, Intent intent) { + /** + * Checks if the received action is BOOT_COMPLETED and if so, sets the alarm for the Indexer + * @param context + * @param intent + */ + public void onReceive(Context context, Intent intent) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED") && prefs.getBoolean("enabled", true)) { - Alarm.SetAlarm(context); + if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED") && prefs.getBoolean("enabled", true)) { + Alarm.SetAlarm(context); Intent serviceIntent = new Intent(context, FileListener.class); context.startService(serviceIntent); - } - } + } + } } diff --git a/src/ca/dracode/ais/indexclient/MClientService.aidl b/src/ca/dracode/ais/indexclient/MClientService.aidl index 5598a36..2d59a18 100644 --- a/src/ca/dracode/ais/indexclient/MClientService.aidl +++ b/src/ca/dracode/ais/indexclient/MClientService.aidl @@ -26,7 +26,7 @@ interface MClientService { String getWordsForPage(int page); /** - * + * Gets the number of pages in the file * @return - the number of pages in the file specified at loadFile(String path) */ int getPageCount(); diff --git a/src/ca/dracode/ais/indexdata/PageResult.java b/src/ca/dracode/ais/indexdata/PageResult.java index 35d7263..cb36840 100644 --- a/src/ca/dracode/ais/indexdata/PageResult.java +++ b/src/ca/dracode/ais/indexdata/PageResult.java @@ -43,29 +43,26 @@ public PageResult[] newArray(int size) { return new PageResult[size]; } }; - /** - * - */ private static final long serialVersionUID = -2204743540787327738L; public List text; public int page; - public String document; + public String document; public PageResult() { - this.text = new ArrayList(); + this.text = new ArrayList(); } private PageResult(Parcel in) { - this(); + this(); in.readList(this.text, null); - this.page = in.readInt(); - this.document = in.readString(); + this.page = in.readInt(); + this.document = in.readString(); } public PageResult(List text, int page, String document) { this.text = text; this.page = page; - this.document = document; + this.document = document; } public int describeContents() { @@ -74,8 +71,8 @@ public int describeContents() { public void writeToParcel(Parcel out, int arg1) { out.writeList(text); - out.writeInt(this.page); - out.writeString(this.document); + out.writeInt(this.page); + out.writeString(this.document); } } diff --git a/src/ca/dracode/ais/indexer/FileIndexer.java b/src/ca/dracode/ais/indexer/FileIndexer.java index e691c47..0d91506 100644 --- a/src/ca/dracode/ais/indexer/FileIndexer.java +++ b/src/ca/dracode/ais/indexer/FileIndexer.java @@ -50,97 +50,119 @@ import java.util.List; public class FileIndexer { - private static String TAG = "ca.dracode.ais.androidindexer.FileIndexer"; - private IndexWriter writer; - private FileSearcher searcher; - - public FileIndexer() { - super(); - this.searcher = new FileSearcher(); - Directory dir; - try { - dir = FSDirectory.open(new File(FileIndexer.getRootStorageDir())); - Analyzer analyzer = new SimpleAnalyzer(Version.LUCENE_47); - IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_47, - analyzer); - iwc.setOpenMode(OpenMode.CREATE_OR_APPEND); - this.writer = new IndexWriter(dir, iwc); - this.writer.commit(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static void Build(IndexWriter writer, File file, int page, - String contents) { - if (file.canRead()) { - try { - //Log.i(TAG, "Started Indexing file: " + file.getName() + " " - // + page); - Document doc = new Document(); - doc.add(new StringField("id", file.getPath() + ":" + page, - Field.Store.NO)); - doc.add(new StringField("path", file.getPath(), - Field.Store.YES)); - doc.add(new LongField("modified", file.lastModified(), - Field.Store.YES)); - // for(int i = 0; i < contents.size(); i++){ - doc.add(new TextField("text", "" + contents, Field.Store.YES)); - doc.add(new IntField("page", page, Field.Store.YES)); - // } + private static String TAG = "ca.dracode.ais.androidindexer.FileIndexer"; + private IndexWriter writer; + private FileSearcher searcher; + + public FileIndexer() { + super(); + this.searcher = new FileSearcher(); + Directory dir; + try { + dir = FSDirectory.open(new File(FileIndexer.getRootStorageDir())); + Analyzer analyzer = new SimpleAnalyzer(Version.LUCENE_47); + IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_47, + analyzer); + iwc.setOpenMode(OpenMode.CREATE_OR_APPEND); + this.writer = new IndexWriter(dir, iwc); + this.writer.commit(); + } catch(IOException e) { + e.printStackTrace(); + } + } + + /** + * Creates a Document containing contents and metadata for a specific page of a file + * @param writer The writer used to save the metadata + * @param file The file that the page belongs to + * @param page The index of the page in the file + * @param contents The string contents of the file + */ + public static void Build(IndexWriter writer, File file, int page, + String contents) { + if(file.canRead()) { + try { + //Log.i(TAG, "Started Indexing file: " + file.getName() + " " + // + page); + Document doc = new Document(); + doc.add(new StringField("id", file.getPath() + ":" + page, + Field.Store.NO)); + doc.add(new StringField("path", file.getPath(), + Field.Store.YES)); + doc.add(new LongField("modified", file.lastModified(), + Field.Store.YES)); + // for(int i = 0; i < contents.size(); i++){ + doc.add(new TextField("text", "" + contents, Field.Store.YES)); + doc.add(new IntField("page", page, Field.Store.YES)); + // } // TODO - Check what OpenMode.CREATE_OR_APPEND does; I think updateDocument should // always be used with CREATE_OR_APPEND, the if part may need to be removed - if (writer.getConfig().getOpenMode() == OpenMode.CREATE) { - writer.addDocument(doc); - } else { - // TODO - Test UpdateDocument - writer.updateDocument(new Term("id", file.getPath() + ":" - + page), doc); - } - //Log.i(TAG, "Done Indexing file: " + file.getName() + " " + page); - } catch (Exception e) { - Log.e(TAG, "Error ", e); - } - } - } - - public static String getRootStorageDir() { - boolean mExternalStorageAvailable; - boolean mExternalStorageWriteable; - String state = Environment.getExternalStorageState(); - - if (Environment.MEDIA_MOUNTED.equals(state)) { - // We can read and write the media - mExternalStorageAvailable = mExternalStorageWriteable = true; - } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { - // We can only read the media - mExternalStorageAvailable = true; - mExternalStorageWriteable = false; - } else { - // Something else is wrong. It may be one of many other states, but - // all we need - // to know is we can neither read nor write - mExternalStorageAvailable = mExternalStorageWriteable = false; - } - - if (mExternalStorageAvailable && mExternalStorageWriteable) { - return Environment.getExternalStorageDirectory() - + "/Android/data/ca.dracode.ais"; - } else { - return null; - } - } - - - // TODO - make the indexer restart indexing a file if it fails. When - // buildIndex is called from SearchService android.os.DeadObjectException is - // called on the SearchService from building larger indexes - - - public int checkForIndex(String field, String value, long modified) throws IOException { + if(writer.getConfig().getOpenMode() == OpenMode.CREATE) { + writer.addDocument(doc); + } else { + // TODO - Test UpdateDocument + writer.updateDocument(new Term("id", file.getPath() + ":" + + page), doc); + } + //Log.i(TAG, "Done Indexing file: " + file.getName() + " " + page); + } catch(Exception e) { + Log.e(TAG, "Error ", e); + } + } + } + + /** + * Gets the directory that stores the Index + * @return the path of the directory that stores the Index; null if the directory is not + * writeable or is not available + */ + public static String getRootStorageDir() { + boolean mExternalStorageAvailable; + boolean mExternalStorageWriteable; + String state = Environment.getExternalStorageState(); + + if(Environment.MEDIA_MOUNTED.equals(state)) { + // We can read and write the media + mExternalStorageAvailable = mExternalStorageWriteable = true; + } else if(Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + // We can only read the media + mExternalStorageAvailable = true; + mExternalStorageWriteable = false; + } else { + // Something else is wrong. It may be one of many other states, but + // all we need + // to know is we can neither read nor write + mExternalStorageAvailable = mExternalStorageWriteable = false; + } + + if(mExternalStorageAvailable && mExternalStorageWriteable) { + return Environment.getExternalStorageDirectory() + + "/Android/data/ca.dracode.ais"; + } else { + return null; + } + } + + + // TODO - make the indexer restart indexing a file if it fails. When + // buildIndex is called from SearchService android.os.DeadObjectException is + // called on the SearchService from building larger indexes + + + /** + * Checks to see if a file with a matching Document field and value exists in the index + * @param field The field to use to check for the file's existence + * @param value The identifier for the file; type depends of the given field + * @param modified The time that the file in the filesystem was last modified; used to + * determine if the file has been modified since it was indexed + * @return -1 if the index does not exist; 1 if the index exists but is out of date; 0 if the + * index exists and is up to date + * @throws IOException + */ + private int checkForIndex(String field, String value, long modified) throws IOException { Document doc = this.searcher.getDocument(field, value); if(doc != null) { - if( Long.parseLong(doc.get("modified")) < modified){ + if(Long.parseLong(doc.get("modified")) < modified) { return 1; } else { return 0; @@ -148,45 +170,48 @@ public int checkForIndex(String field, String value, long modified) throws IOExc } else { return -1; } - } + } + + /** + * Checks to see if a file with a matching id exists in the index + * @param id The field to use to check for the file's existence + * @param modified The time that the file in the filesystem was last modified; used to + * determine if the file has been modified since it was indexed + * @return -1 if the index does not exist; 1 if the index exists but is out of date; 0 if the + * index exists and is up to date + * @throws IOException + */ + public int checkForIndex(String id, long modified) throws IOException { + return this.checkForIndex("id", id, modified); + } - public boolean removeIndex(String path){ + /** + * Removes all files matching the given path + *

+ * Removes the metadata file and page files for the given file path + *

+ * @param path Path of the file(s) to be removed + * @return true if one or more files were removed; false otherwise + */ + public boolean removeIndex(String path) { try { if(this.checkForIndex("path", path, 0) != -1) { this.writer.deleteDocuments(new Term("path", path)); return true; } - } catch(IOException e){ + } catch(IOException e) { Log.e(TAG, "Error ", e); } return false; } - public int buildIndex(String filename){ - try { - //Log.i(TAG, "Writing Metadata"); - Document doc = new Document(); - File file = new File(filename); - doc.add(new StringField("id", file.getPath() + ":meta", Field.Store.NO)); - doc.add(new LongField("modified", file.lastModified(), Field.Store.YES)); - doc.add(new StringField("path", file.getAbsolutePath(), Field.Store.YES)); - if (writer.getConfig().getOpenMode() == OpenMode.CREATE) { - writer.addDocument(doc); - } else { - writer.updateDocument(new Term("id", file.getPath() + ":meta"), - doc); - } - Log.i(TAG, "Done creating metadata for file " + filename); - // Must only call ForceMerge and Commit once per document as they are very resource heavy operations - writer.commit(); - } catch (Exception e) { - Log.e(TAG, "Error", e); - return -1; - } - return 0; - } - - public int buildIndex(String filename, int pages){ + /** + * Creates the metadata Document for a given file + * @param filename The path of the file that the metadata will describe + * @param pages The number of pages in the file; -1 if it's contents are not indexed + * @return 0 upon successful index creation; -1 on error + */ + public int buildIndex(String filename, int pages) { try { //Log.i(TAG, "Writing Metadata"); Document doc = new Document(); @@ -194,8 +219,10 @@ public int buildIndex(String filename, int pages){ doc.add(new StringField("id", file.getPath() + ":meta", Field.Store.NO)); doc.add(new LongField("modified", file.lastModified(), Field.Store.YES)); doc.add(new StringField("path", file.getAbsolutePath(), Field.Store.YES)); - doc.add(new IntField("pages", pages, Field.Store.YES)); - if (writer.getConfig().getOpenMode() == OpenMode.CREATE) { + if(pages != -1) { + doc.add(new IntField("pages", pages, Field.Store.YES)); + } + if(writer.getConfig().getOpenMode() == OpenMode.CREATE) { writer.addDocument(doc); } else { writer.updateDocument(new Term("id", file.getPath() + ":meta"), @@ -204,40 +231,52 @@ public int buildIndex(String filename, int pages){ Log.i(TAG, "Done creating metadata for file " + filename); // Must only call ForceMerge and Commit once per document as they are very resource heavy operations writer.commit(); - } catch (Exception e) { + } catch(Exception e) { + Log.e(TAG, "Error", e); + return -1; + } + return 0; + } + + /** + * Creates a metadata Document and page Documents for a given file + * @param contents The text contents of the file; separated into pages + * @param file The file that will be added to the index + * @return 0 upon successful index creation; -1 otherwise + */ + public int buildIndex(List contents, File file) { + try { + for(int i = 0; i < contents.size(); i++) { + if(this.checkForIndex("id", file.getAbsolutePath() + ":" + i, file.lastModified()) != 0) { + FileIndexer.Build(writer, file, i, contents + .get(i)); + } else { + //Log.i(TAG, "Skipping " + file.getAbsolutePath() + ":" + i + // + " Already in index"); + } + } + buildIndex(file.getPath(), contents.size()); + } catch(Exception e) { Log.e(TAG, "Error", e); return -1; } return 0; } - public int buildIndex(List contents, File file) { - try { - for (int i = 0; i < contents.size(); i++) { - if (this.checkForIndex("id", file.getAbsolutePath() + ":" + i, file.lastModified()) != 0) { - FileIndexer.Build(writer, file, i, contents - .get(i)); - } else { - //Log.i(TAG, "Skipping " + file.getAbsolutePath() + ":" + i - // + " Already in index"); - } - } - buildIndex(file.getPath(), contents.size()); - } catch (Exception e) { - Log.e(TAG, "Error", e); - return -1; - } - return 0; - } - - public void close() { - try { - writer.commit(); - // TODO - Determine how much of a speed increase is gained while searching after ForceMerge - writer.forceMerge(1); - writer.close(); - } catch (IOException e) { - Log.e(TAG, "Error while closing indexwriter", e); - } - } + /** + * Closes the index + *

+ * Must be called before the indexer is disposed of + *

+ */ + public void close() { + try { + writer.commit(); + // TODO - Determine how much of a speed increase is gained while searching after ForceMerge + writer.forceMerge(1); + writer.close(); + } catch(IOException e) { + Log.e(TAG, "Error while closing indexwriter", e); + } + } } diff --git a/src/ca/dracode/ais/indexer/FileSearcher.java b/src/ca/dracode/ais/indexer/FileSearcher.java index 6749f2e..161fbad 100644 --- a/src/ca/dracode/ais/indexer/FileSearcher.java +++ b/src/ca/dracode/ais/indexer/FileSearcher.java @@ -60,23 +60,17 @@ import ca.dracode.ais.indexdata.PageResult; -/* +/** * FileSearcher.java - * + * * Contains functions for searching the lucene index - * - * v0.3 - * Add capacity to check if the file needs to be updated by - * comparing the index metadata's modified date with the file's modified - * date - * Highlighter code needs to be tested + * @author Benjamin Winger */ - public class FileSearcher { - // NOTE - Standard searches take significantly longer than boolean ones due to their use of the highlighter - // which can make them take a long time to finish for common terms. They will be faster when the - // number of results is actually restricted but boolean searches should be recommended where speed is + // NOTE - Standard searches take significantly longer than boolean ones due to their use of the highlighter + // which can make them take a long time to finish for common terms. They will be faster when the + // number of results is actually restricted but boolean searches should be recommended where speed is // important public static final int QUERY_BOOLEAN = 0; public static final int QUERY_STANDARD = 1; @@ -84,7 +78,7 @@ public class FileSearcher { private final String TAG = "ca.dracode.ais.androidindexer.FileSearcher"; private IndexSearcher indexSearcher; - private boolean interrupt = false; + private boolean interrupt = false; public FileSearcher() { IndexReader indexReader; @@ -94,251 +88,436 @@ public FileSearcher() { Directory tmpDir = FSDirectory.open(indexDirFile); indexReader = DirectoryReader.open(tmpDir); indexSearcher = new IndexSearcher(indexReader); - } catch (IOException ioe) { + } catch(IOException ioe) { Log.e(TAG, "Error", ioe); } this.indexSearcher = indexSearcher; } - public boolean checkForIndex(String field, String value) throws IOException { - //Log.i(TAG, "Checking for existance of " + value); - BooleanQuery qry = new BooleanQuery(); - qry.add(new TermQuery(new Term(field, value)), BooleanClause.Occur.MUST); - if (this.indexSearcher != null) { - ScoreDoc[] hits; - hits = indexSearcher.search(qry, 1).scoreDocs; - return hits.length > 0; - } else { - Log.i(TAG, "Unable to check for index " + field + ":" + value); - IndexReader indexReader; - IndexSearcher indexSearcher = null; - File indexDirFile = new File(FileIndexer.getRootStorageDir()); - Directory tmpDir = FSDirectory.open(indexDirFile); - indexReader = DirectoryReader.open(tmpDir); - indexSearcher = new IndexSearcher(indexReader); - this.indexSearcher = indexSearcher; - return false; - } - } - - public Document getDocument(String field, String value) throws IOException{ + /** + * Returns the first document that matches the given field/value combination + * @param field The field to check + * @param value The value to check with + * @return the Document found by the IndexSearcher + * @throws IOException + */ + public Document getDocument(String field, String value) throws IOException { //Log.i(TAG, "Checking for existance of " + value); BooleanQuery qry = new BooleanQuery(); qry.add(new TermQuery(new Term(field, value)), BooleanClause.Occur.MUST); - if (this.indexSearcher != null) { + if(this.indexSearcher != null) { ScoreDoc[] hits; hits = indexSearcher.search(qry, 1).scoreDocs; - if(hits!= null && hits.length > 0) { + if(hits != null && hits.length > 0) { return indexSearcher.doc(hits[0].doc); } } return null; } - public Document getMetaFile(String value) { + /** + * Searches the index for a metadata Document with "id" matching the passed id + * @param id The "id" of the metadata Document + * @return The metadata Document matching id or null if it does not exist + */ + public Document getMetaFile(String id) { BooleanQuery qry = new BooleanQuery(); - qry.add(new TermQuery(new Term("id", value + ":meta")), + qry.add(new TermQuery(new Term("id", id + ":meta")), BooleanClause.Occur.MUST); ScoreDoc[] hits = null; try { hits = indexSearcher.search(qry, 1).scoreDocs; - } catch (IOException e) { + } catch(IOException e) { Log.e(TAG, "Error ", e); } - assert hits != null; - if (hits.length == 0) { + if(hits == null || hits.length == 0) { return null; } Document doc = null; try { doc = indexSearcher.doc(hits[0].doc); - } catch (IOException e) { + } catch(IOException e) { Log.e(TAG, "Error ", e); } return doc; } - public List findName(String term, String field, List constrainValues, - String constrainField, int maxResults, int set, int type){ - Query qry = null; - if(this.interrupt){ - this.interrupt = true; - return null; - } - if (type == FileSearcher.QUERY_BOOLEAN) { - String[] terms = term.split(" "); - qry = new BooleanQuery(); - ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, "*" + term +"*")), - BooleanClause.Occur.MUST); - } else if (type == FileSearcher.QUERY_STANDARD) { - try { - qry = new QueryParser(Version.LUCENE_47, field, - new SimpleAnalyzer(Version.LUCENE_47)).parse(term); - } catch (ParseException e) { - e.printStackTrace(); - } - // qry.add(new Query(field, value)); - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - if (qry != null) { - BooleanQuery cqry = new BooleanQuery(); - for (String s : constrainValues) { - cqry.add(new TermQuery(new Term(constrainField, s)), - BooleanClause.Occur.MUST); - } - Filter filter = new QueryWrapperFilter(cqry); - ScoreDoc[] hits = null; - try { - hits = indexSearcher.search(qry, filter, maxResults + maxResults*set).scoreDocs; - } catch (IOException e) { - Log.e(TAG, "Error ", e); - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - ArrayList docs = new ArrayList(); - for (int i = maxResults*set; i < hits.length && i < maxResults *set + maxResults; i++) { - try { - Document tmp =indexSearcher.doc(hits[i].doc); - docs.add(tmp.get("path")); - } catch (IOException e) { - Log.e(TAG, "Error ", e); - } - } - Log.i(TAG, "Found instance of term in " + docs.size() + " documents"); - if(this.interrupt){ - this.interrupt = true; - return null; - } - return docs; - } - else { - Log.e(TAG, "Query Type: " + type + " not recognised"); - return new ArrayList(); - } - } - - public PageResult[] findIn(String term, String field, List constrainValues, - String constrainField, int maxResults, int set, int type){ - Query qry = null; - if(this.interrupt){ - this.interrupt = true; - return null; - } - if (type == FileSearcher.QUERY_BOOLEAN) { - String[] terms = term.split(" "); - qry = new BooleanQuery(); - ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, "*" + term +"*")), - BooleanClause.Occur.MUST); - } else if (type == FileSearcher.QUERY_STANDARD) { - try { - qry = new QueryParser(Version.LUCENE_47, field, - new SimpleAnalyzer(Version.LUCENE_47)).parse(term); - } catch (ParseException e) { - e.printStackTrace(); - } - // qry.add(new Query(field, value)); - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - if (qry != null) { - BooleanQuery cqry = new BooleanQuery(); - for (String s : constrainValues) { - cqry.add(new TermQuery(new Term(constrainField, s)), - BooleanClause.Occur.MUST); - } - Filter filter = new QueryWrapperFilter(cqry); - ScoreDoc[] hits = null; - try { - hits = indexSearcher.search(qry, filter, maxResults*set + maxResults).scoreDocs; - } catch (IOException e) { - Log.e(TAG, "Error ", e); - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - ArrayList docs = new ArrayList(); - for (int i = maxResults*set; i < hits.length && i < maxResults*set + maxResults ; i++) { - try { - docs.add(indexSearcher.doc(hits[i].doc)); - } catch (IOException e) { - Log.e(TAG, "Error ", e); - } - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - Log.i(TAG, "Found instance of term in " + docs.size() + " documents"); - /** - * TODO - Add Highlighter Code to retrieve the generated phrase here (In progress) - **/ - try { - int numResults = 0; - PageResult[] results = new PageResult[docs.size()]; - for (int i = 0; i < docs.size() && numResults < maxResults; i++) { - if(this.interrupt){ - this.interrupt = true; - return null; - } - Document d = docs.get(i); - int docPage = Integer.parseInt(d.get("page")); - String name = d.get("path"); - if(type != FileSearcher.QUERY_BOOLEAN) { - String contents = d.get("text"); - Highlighter highlighter = new Highlighter(new QueryScorer(qry)); - - String[] frag = null; - try { - frag = highlighter.getBestFragments(new SimpleAnalyzer(Version.LUCENE_47), "text", contents, maxResults - numResults); - numResults += frag.length; - } catch (IOException e) { - Log.e(TAG, "Error while reading index", e); - } catch (InvalidTokenOffsetsException e) { - Log.e(TAG, "Error while highlighting", e); - } - Log.i(TAG, "Frags: " + frag.length + " " + frag + " " + frag[0]); - ArrayList tmpList = new ArrayList(Arrays - .asList(frag != null ? frag : new String[0])); - Log.i(TAG, "list " + tmpList.getClass().getName()); - results[i] = new PageResult(tmpList, docPage, name); - Log.i(TAG, "" + results[i]); - } - else { - ArrayList tmp = new ArrayList(); - tmp.add(term); - results[i] = new PageResult(tmp, docPage, name); - } - - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - Log.i(TAG, "" +results.length); - return results; - }catch(Exception e){ - Log.e("TAG", "Error while Highlighting", e); - return null; - } - } else { - Log.e(TAG, "Query Type: " + type + " not recognised"); - return new PageResult[0]; - } - } - - /** Adapted from Lucene 4.7 IntComparator - * Identical but intended for sorting pages of a document starting at a certain page. - * Previous pages are added to the end of the list - * */ + /** + * Searches for matches in a file's path and name + *

+ * For example, a search with the term "Foo" and the constrainValues {"/Bar", "/Stool"} + * will return files with a path related to "Foo" only from the directories or + * subdirectories or "/Bar" and "/Stool" + *

+ * @param term The search term for choosing and ranking results + * @param field The field to search, i.e. "path" + * @param constrainValues The paths to which to constrain the search + * @param constrainField The field used to constrain searches + * @param maxResults The maximum number of results that will be returned + * @param set The set number, e.g., searching set 0 returns the first n results, + * searching set 1 returns the 2nd n results + * @param type The type of the search, one of QUERY_BOOLEAN or QUERY_STANDARD + * @return A list containing all of the paths the searcher found, sorted by relevance + */ + public List findName(String term, String field, List constrainValues, + String constrainField, int maxResults, int set, int type) { + Query qry = this.getQuery(term, field, type); + if(qry != null){ + Filter filter = this.getFilter(constrainField, constrainValues); + ScoreDoc[] hits = null; + try { + hits = indexSearcher.search(qry, filter, maxResults + maxResults * set).scoreDocs; + } catch(IOException e) { + Log.e(TAG, "Error ", e); + } + if(this.interrupt) { + this.interrupt = true; + return null; + } + List docs = this.getDocPaths(maxResults, set, hits); + Log.i(TAG, "Found instance of term in " + docs.size() + " documents"); + return docs; + } else { + Log.e(TAG, "Query Type: " + type + " not recognised"); + return new ArrayList(); + } + } + + /** + * Searches for matches in the contents of multiple files + *

+ * For example, a search with the term "Foo" and the constrainValues {"/Bar", "/Stool"} + * will return pages with contents related to "Foo" only from the directories or + * subdirectories or "/Bar" and "/Stool" + *

+ * @param term The search term for choosing and ranking results + * @param field The field to search, i.e. "contents" + * @param constrainValues The paths to which to constrain the search + * @param constrainField The field used to constrain searches + * @param maxResults The maximum number of results that will be returned + * @param set The set number, e.g., searching set 0 returns the first n results, + * searching set 1 returns the 2nd n results + * @param type The type of the search, one of QUERY_BOOLEAN or QUERY_STANDARD + * @return A list of PageResult containing all of the results the searcher found, + * sorted by relevance + */ + public PageResult[] findInFiles(String term, String field, List constrainValues, + String constrainField, int maxResults, int set, int type) { + Query qry = this.getQuery(term, field, type); + if(qry != null){ + Filter filter = this.getFilter(constrainField, constrainValues); + ScoreDoc[] hits = null; + try { + hits = indexSearcher.search(qry, filter, maxResults * set + maxResults).scoreDocs; + } catch(IOException e) { + Log.e(TAG, "Error ", e); + } + if(this.interrupt) { + this.interrupt = true; + return null; + } + List docs = this.getDocs(maxResults, set, hits); + Log.i(TAG, "Found instance of term in " + docs.size() + " documents"); + return this.getHighlightedResults(docs, qry, type, term, maxResults); + } else { + Log.e(TAG, "Query Type: " + type + " not recognised"); + return new PageResult[0]; + } + } + + /** + * Searches for matches in the contents of multiple files + *

+ * For example, a search with the term "Foo" and the constrainValue "/Bar.txt" + * will return pages with contents related to "Foo" only from inside the file "/Bar.txt" + *

+ * @param term The search term for choosing and ranking results + * @param field The field to search, i.e. "contents" + * @param constrainValue The path to which to constrain the search + * @param constrainField The field used to constrain searches + * @param maxResults The maximum number of results that will be returned + * @param set The set number, e.g., searching set 0 returns the first n results, + * searching set 1 returns the 2nd n results + * @param type The type of the search, one of QUERY_BOOLEAN or QUERY_STANDARD + * @return A list of PageResult containing all of the results the searcher found, + * sorted by relevance + */ + public PageResult[] findInFile(String term, String field, String constrainValue, + String constrainField, int maxResults, int set, int type, + final int page) { + Query qry = this.getQuery(term, field, type); + if(qry != null){ + String[] values = {constrainValue}; + Filter filter = this.getFilter(constrainField, Arrays.asList(values)); + ScoreDoc[] hits = null; + try { + Log.i(TAG, "Searching..."); + hits = indexSearcher.search(qry, filter, maxResults * set + maxResults, + this.getPagedSort(page)).scoreDocs; + } catch(IOException e) { + Log.e(TAG, "Error ", e); + } + if(this.interrupt) { + this.interrupt = true; + return null; + } + + Log.i(TAG, "Found instance of term in " + hits.length + " documents"); + return this.getHighlightedResults(this.getDocs(maxResults, set, hits), qry, type, + term, maxResults); + } else { + Log.e(TAG, "Query Type: " + type + " not recognised"); + return new PageResult[0]; + } + } + + /** + * Creates a query based on the given term field and type + * @param term Search Term for the query + * @param field Document Field for the Query which the term is matched against + * @param type The type of query to be created, either QUERY_BOOLEAN, or QUERY_STANDARD, + * @return a query for the given field and term using either a BooleanQuery with a + * WildcardQuery for the term or a Query built from a QueryParser and SimpleAnalyzer + */ + private Query getQuery(String term, String field, int type) { + if(this.interrupt) { + this.interrupt = false; + return null; + } + Query qry = null; + if(type == FileSearcher.QUERY_BOOLEAN) { + String[] terms = term.split(" "); + qry = new BooleanQuery(); + ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, "*" + term + "*")), + BooleanClause.Occur.MUST); + } else if(type == FileSearcher.QUERY_STANDARD) { + try { + qry = new QueryParser(Version.LUCENE_47, field, + new SimpleAnalyzer(Version.LUCENE_47)).parse(term); + } catch(ParseException e) { + e.printStackTrace(); + } + } + if(this.interrupt) { + this.interrupt = false; + return null; + } + return qry; + } + + /** + * Creates a directory filter + * @param constrainField The field that contains the directory info + * @param constrainValues The directories to which the filters shold limit + * @return The created filter + */ + private Filter getFilter(String constrainField, List constrainValues){ + BooleanQuery cqry = new BooleanQuery(); + for(String s : constrainValues) { + cqry.add(new TermQuery(new Term(constrainField, s)), + BooleanClause.Occur.SHOULD); + } + return new QueryWrapperFilter(cqry); + } + + /** + * Takes a list of Documents and highlights information relevant to a given Query + * @param docs The documents to highlight + * @param qry The query used to highlight the documents + * @param type The type of the search, one of QUERY_BOOLEAN, + * which just notes the page on which the term exists or QUERY_STANDARD, + * which gives highlighted fragments and the page on which they exist. + * @param term The term that created the query + * @param maxResults The maximum number of results that will be returned + * @return An array containing a PageResult entry for each result + */ + private PageResult[] getHighlightedResults(List docs, Query qry, int type, + String term, int maxResults){ + try { + int numResults = 0; + PageResult[] results = new PageResult[docs.size()]; + for(int i = 0; i < docs.size() && numResults < maxResults; i++) { + if(this.interrupt) { + this.interrupt = false; + return null; + } + Document d = docs.get(i); + int docPage = Integer.parseInt(d.get("page")); + String name = d.get("path"); + if(type != FileSearcher.QUERY_BOOLEAN) { + String contents = d.get("text"); + Highlighter highlighter = new Highlighter(new QueryScorer(qry)); + + String[] frag = null; + try { + frag = highlighter.getBestFragments(new SimpleAnalyzer(Version.LUCENE_47), "text", contents, maxResults - numResults); + numResults += frag.length; + } catch(IOException e) { + Log.e(TAG, "Error while reading index", e); + } catch(InvalidTokenOffsetsException e) { + Log.e(TAG, "Error while highlighting", e); + } + Log.i(TAG, "Frags: " + frag.length + " " + frag + " " + frag[0]); + ArrayList tmpList = new ArrayList(Arrays + .asList(frag != null ? frag : new String[0])); + Log.i(TAG, "list " + tmpList.getClass().getName()); + results[i] = (new PageResult(tmpList, docPage, name)); + Log.i(TAG, "" + results[i]); + } else { + ArrayList tmp = new ArrayList(); + tmp.add(term); + results[i] = (new PageResult(tmp, docPage, name)); + } + + } + Log.i(TAG, "" + results.length); + if(this.interrupt) { + this.interrupt = false; + return null; + } + return results; + } catch(Exception e) { + Log.e("TAG", "Error while Highlighting", e); + return null; + } + } + + /** + * Takes an array of ScoreDoc and turns it into the relevant number of Documents + * @param maxResults The maximum number of documents that will be parsed + * @param set The set number, e.g., searching set 0 returns the first n results, + * searching set 1 returns the 2nd n results + * @param hits The ScoreDoc to be parsed + * @return The list of documents that is maxResults long and skips set*maxResults results + */ + private List getDocs(int maxResults, int set, ScoreDoc[] hits){ + ArrayList docs = new ArrayList(); + for(int i = maxResults * set; i < hits.length && i < (maxResults * set + maxResults); + i++) { + try { + Document tmp = indexSearcher.doc(hits[i].doc); + docs.add(tmp); + } catch(IOException e) { + Log.e(TAG, "Error ", e); + } + } + return docs; + } + + /** + * Takes an array of ScoreDoc and turns it into the relevant number of Document paths + * @param maxResults The maximum number of paths that will be parsed + * @param set The set number, e.g., searching set 0 returns the first n results, + * searching set 1 returns the 2nd n results + * @param hits The ScoreDoc to be parsed + * @return The list of paths that is maxResults long and skips set*maxResults results + */ + private List getDocPaths(int maxResults, int set, ScoreDoc[] hits){ + ArrayList docs = new ArrayList(); + for(int i = maxResults * set; i < hits.length && i < maxResults * set + maxResults; i++) { + try{ + Document tmp = indexSearcher.doc(hits[i].doc); + docs.add(tmp.get("path")); + } catch(IOException e) { + Log.e(TAG, "Error ", e); + } + } + return docs; + } + + /** + * Calls for the any current searches to be inturrupted + * @return false if the interrupt flag was already set; otherwise true + */ + public boolean interrupt() { + // TODO - Must become path dependent so that interrupt calls do not interrupt searches + // from other applications + boolean tmp = this.interrupt; + this.interrupt = true; + return !tmp; + } + + /** + * Creates a sort that splits results around a given page + * @param page The page that will show first in the results, + * @return A Sort object that splits results around the given page + */ + private Sort getPagedSort(final int page){ + return new Sort(new SortField("page", new FieldComparatorSource() { + @Override + public FieldComparator newComparator(String fieldname, int numhits, + int sortpos, + boolean reversed) throws + IOException { + return new PagedIntComparator(numhits, fieldname, null, + null) { + public int compare(int slot1, int slot2) { + final int v1 = this.values[slot1]; + final int v2 = this.values[slot2]; + if(v1 < page && v2 >= page) { + return 1; + } else if(v1 >= page && v2 < page) { + return -1; + } else { + if(v1 > v2) { + return 1; + } else if(v1 < v2) { + return -1; + } else { + return 0; + } + } + } + + @Override + public int compareBottom(int doc) { + int v2 = this.currentReaderValues.get(doc); + if(docsWithField != null && v2 == 0 && !docsWithField.get(doc)) { + v2 = missingValue; + } + if(v2 < page) { + return -1; + } + if(this.bottom > v2) { + return 1; + } else if(this.bottom < v2) { + return -1; + } else { + return 0; + } + } + + @Override + public int compareTop(int doc) { + int docValue = this.currentReaderValues.get(doc); + if(docsWithField != null && docValue == 0 && !docsWithField.get(doc)) { + docValue = missingValue; + } + if(this.topValue < page && docValue > page) { + return 1; + } + if(this.topValue < docValue) { + return -1; + } else if(this.topValue > docValue) { + return 1; + } else { + return 0; + } + } + }; + } + })); + } + + /** + * Adapted from Lucene 4.7 IntComparator + *

+ * Identical but intended for sorting pages of a document starting at a certain page. + * Previous pages are added to the end of the list + *

+ * @since v0.4 + */ public abstract class PagedIntComparator extends NumericComparator { protected final int[] values; private final IntParser parser; @@ -347,7 +526,7 @@ public abstract class PagedIntComparator extends NumericComparator { protected int topValue; PagedIntComparator(int numHits, String field, FieldCache.Parser parser, - Integer missingValue) { + Integer missingValue) { super(field, missingValue); values = new int[numHits]; this.parser = (IntParser) parser; @@ -358,7 +537,7 @@ public void copy(int slot, int doc) { int v2 = currentReaderValues.get(doc); // Test for v2 == 0 to save Bits.get method call for // the common case (doc has value and value is non-zero): - if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) { + if(docsWithField != null && v2 == 0 && !docsWithField.get(doc)) { v2 = missingValue; } @@ -388,204 +567,4 @@ public Integer value(int slot) { return Integer.valueOf(values[slot]); } } - - // TODO - Boolean Search returns a huge number of results for simple queries, maxResults must be implemented - public PageResult[] find(String term, String field, String constrainValue, - String constrainField, int maxResults, int set, int type, - final int page) { - if(this.interrupt) { - this.interrupt = false; - return null; - } - Query qry = null; - if (type == FileSearcher.QUERY_BOOLEAN) { - String[] terms = term.split(" "); - qry = new BooleanQuery(); - ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, "*" + term +"*")), - BooleanClause.Occur.MUST); - } else if (type == FileSearcher.QUERY_STANDARD) { - try { - qry = new QueryParser(Version.LUCENE_47, field, - new SimpleAnalyzer(Version.LUCENE_47)).parse(term); - } catch (ParseException e) { - e.printStackTrace(); - } - // qry.add(new Query(field, value)); - } - if(this.interrupt){ - this.interrupt = false; - return null; - } - if (qry != null) { - BooleanQuery cqry = new BooleanQuery(); - cqry.add(new TermQuery(new Term(constrainField, constrainValue)), - BooleanClause.Occur.MUST); - Filter filter = new QueryWrapperFilter(cqry); - ScoreDoc[] hits = null; - try { - Log.i(TAG, "Searching..."); - hits = indexSearcher.search(qry, filter, maxResults * set + maxResults, - new Sort(new SortField("page",new FieldComparatorSource() { - @Override - public FieldComparator newComparator(String fieldname, int numhits, - int sortpos, - boolean reversed) throws - IOException { - return new PagedIntComparator(numhits, fieldname, null, - null){ - public int compare(int slot1, int slot2){ - // TODO: there are sneaky non-branch ways to compute - // -1/+1/0 sign - // Cannot return values[slot1] - values[slot2] because that - // may overflow - final int v1 = this.values[slot1]; - final int v2 = this.values[slot2]; - if(v1 < page && v2 >= page) { - return 1; - } else if (v1 >= page && v2 < page){ - return -1; - } else{ - if(v1 > v2) { - return 1; - } else if(v1 < v2) { - return -1; - } else { - return 0; - } - } - } - - @Override - public int compareBottom(int doc) { - // TODO: there are sneaky non-branch ways to compute - // -1/+1/0 sign - // Cannot return bottom - values[slot2] because that - // may overflow - int v2 = this.currentReaderValues.get(doc); - // Test for v2 == 0 to save Bits.get method call for - // the common case (doc has value and value is non-zero): - if(docsWithField != null && v2 == 0 && !docsWithField.get(doc)) { - v2 = missingValue; - } - if(v2 < page){ - return -1; - } - if(this.bottom > v2) { - return 1; - } else if(this.bottom < v2) { - return -1; - } else { - return 0; - } - } - - @Override - public int compareTop(int doc){ - int docValue = this.currentReaderValues.get(doc); - // Test for docValue == 0 to save Bits.get method call for - // the common case (doc has value and value is non-zero): - if(docsWithField != null && docValue == 0 && !docsWithField.get(doc)) { - docValue = missingValue; - } - // Cannot use Integer.compare (it's java 7) - if(this.topValue < page && docValue > page){ - return 1; - } - if(this.topValue < docValue) { - return -1; - } else if(this.topValue > docValue) { - return 1; - } else { - return 0; - } - } - }; - } - }))).scoreDocs; - } catch (IOException e) { - Log.e(TAG, "Error ", e); - } - if(this.interrupt){ - this.interrupt = true; - return null; - } - - Log.i(TAG, "Sorted " + hits.length); - ArrayList docs = new ArrayList(); - for (int i = maxResults*set; i < hits.length || i < (maxResults * set + maxResults); - i++) { - try { - Document tmp = indexSearcher.doc(hits[i].doc); - int docPage = Integer.parseInt(tmp.get("page")); - docs.add(tmp); - } catch (IOException e) { - Log.e(TAG, "Error ", e); - } - } - if(this.interrupt){ - this.interrupt = false; - return null; - } - Log.i(TAG, "Found instance of term in " + hits.length + " documents"); - /** - * TODO - Add Highlighter Code to retrieve the generated phrase here (In progress) - **/ - try { - int numResults = 0; - ArrayList results = new ArrayList(); - for (int i = 0; i < docs.size(); i++) { - if(this.interrupt){ - this.interrupt = false; - return null; - } - Document d = docs.get(i); - int docPage = Integer.parseInt(d.get("page")); - if(type != FileSearcher.QUERY_BOOLEAN) { - String contents = d.get("text"); - Highlighter highlighter = new Highlighter(new QueryScorer(qry)); - - String[] frag = null; - try { - frag = highlighter.getBestFragments(new SimpleAnalyzer(Version.LUCENE_47), "text", contents, maxResults - numResults); - numResults += frag.length; - } catch (IOException e) { - Log.e(TAG, "Error while reading index", e); - } catch (InvalidTokenOffsetsException e) { - Log.e(TAG, "Error while highlighting", e); - } - Log.i(TAG, "Frags: " + frag.length + " " + frag + " " + frag[0]); - ArrayList tmpList = new ArrayList(Arrays - .asList(frag != null ? frag : new String[0])); - Log.i(TAG, "list " + tmpList.getClass().getName()); - results.add(new PageResult(tmpList, docPage, constrainValue)); - Log.i(TAG, "" + results.get(i)); - } - else { - ArrayList tmp = new ArrayList(); - tmp.add(term); - results.add(new PageResult(tmp, docPage, constrainValue)); - } - - } - Log.i(TAG, "" +results.size()); - if(this.interrupt){ - this.interrupt = false; - return null; - } - return results.toArray(new PageResult[0]); - }catch(Exception e){ - Log.e("TAG", "Error while Highlighting", e); - return null; - } - } else { - Log.e(TAG, "Query Type: " + type + " not recognised"); - return new PageResult[0]; - } - } - - public boolean interrupt(){ - boolean tmp = this.interrupt; - this.interrupt = true; - return tmp; - } } diff --git a/src/ca/dracode/ais/indexinfo/FileChangeListener.java b/src/ca/dracode/ais/indexinfo/FileChangeListener.java deleted file mode 100644 index 3d6b111..0000000 --- a/src/ca/dracode/ais/indexinfo/FileChangeListener.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright 2014 Benjamin Winger. - * - * This file is part of Android Indexing Service. - * - * Android Indexing Service 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. - * - * Android Indexing Service 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 Android Indexing Service. If not, see . - ******************************************************************************/ - -package ca.dracode.ais.indexinfo; - -import java.io.File; - -public interface FileChangeListener { - public void onFileChanged(File newFile); -} diff --git a/src/ca/dracode/ais/indexinfo/IndexComm.aidl b/src/ca/dracode/ais/indexinfo/IndexComm.aidl index 3e43f8e..b2616c7 100644 --- a/src/ca/dracode/ais/indexinfo/IndexComm.aidl +++ b/src/ca/dracode/ais/indexinfo/IndexComm.aidl @@ -21,20 +21,18 @@ package ca.dracode.ais.indexinfo; interface IndexComm { void stopIndexer(); - /* + /** Get Current State of the Indexer @return true if the indexer is running, false otherwise */ boolean isIndexerRunning(); - /* - Sets the file change listener - @param a listener that will be notified whenever the indexer starts indexing a - new file + /** + Gets the path of the directory currently being indexed */ - void setFileChangeListener(out FileChangeListener listener); + String getCurrentIndexPath(); - /* + /** Gets the number of documents in the index @return the number of documents in the index */ diff --git a/src/ca/dracode/ais/indexinfo/IndexInfo.java b/src/ca/dracode/ais/indexinfo/IndexInfo.java index e0f0853..bfd80bd 100644 --- a/src/ca/dracode/ais/indexinfo/IndexInfo.java +++ b/src/ca/dracode/ais/indexinfo/IndexInfo.java @@ -24,9 +24,8 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; -import android.util.Log; - import android.os.RemoteException; +import android.util.Log; import ca.dracode.ais.alarm.Alarm; import ca.dracode.ais.service.FileListener; @@ -34,58 +33,7 @@ public class IndexInfo { private static final String TAG = "ca.dracode.ais.indexinfo"; private boolean enabled = true; - /* - Get Current State of the Indexer - @return true if the indexer is running, false otherwise - */ - public boolean isIndexerRunning() { - return false; - } - - /* - Sets the file change listener - @param a listener that will be notified whenever the indexer starts indexing a - new file - */ - public void setFileChangeListener(FileChangeListener listener) { - - } - - /* - Gets the number of documents in the index - @return the number of documents in the index - */ - public int getNumDocumentsInIndex() { - return 0; - } - - /* - Manually stops the indexer - @precondition the indexer is running - @postcondition the indexer will no longer be running - */ - public void stopIndexer(Context context) { - Alarm.CancelAlarm(context); - try { - mService.stopIndexer(); - } catch(RemoteException e){ - Log.e(TAG, "Error", e); - } - } - - /* - Manually starts the indexer - @precondition the indexer is not running - @postcondition the indexer will be running - */ - public void startIndexer(Context context) { - Alarm.SetAlarm(context); - Intent serviceIntent = new Intent(context, FileListener.class); - context.startService(serviceIntent); - } - private IndexComm mService; - private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { @@ -98,4 +46,56 @@ public void onServiceDisconnected(ComponentName className) { mService = null; } }; + + /** + * Get Current State of the Indexer + * @return true if the indexer is running, false otherwise + */ + public boolean isIndexerRunning() { + return false; + } + + /** + * Sets the file change listener + * @return The path that the indexer is currently indexing + */ + public String getCurrentIndexPath(){ + try { + return mService.getCurrentIndexPath(); + } catch(RemoteException e){ + Log.e(TAG, "Error", e); + } + return "Error connecting to the service!"; + } + + /** + * Gets the number of documents in the index + * @return the number of documents in the index + */ + public int getNumDocumentsInIndex() { + return 0; + } + + /** + * Manually stops the indexer + * @param context + */ + public void stopIndexer(Context context) { + Alarm.CancelAlarm(context); + try { + mService.stopIndexer(); + } catch(RemoteException e) { + Log.e(TAG, "Error", e); + } + } + + /** + * Manually starts the indexer + * @param context + */ + public void startIndexer(Context context) { + Alarm.SetAlarm(context); + Intent serviceIntent = new Intent(context, FileListener.class); + context.startService(serviceIntent); + } } diff --git a/src/ca/dracode/ais/service/BSearchService1_0.aidl b/src/ca/dracode/ais/service/BSearchService1_0.aidl index 195e801..0ce60d0 100644 --- a/src/ca/dracode/ais/service/BSearchService1_0.aidl +++ b/src/ca/dracode/ais/service/BSearchService1_0.aidl @@ -56,7 +56,7 @@ interface BSearchService1_0 { /** * Used to search for file names - * @param dir - the root directory for the search. + * @param docs - the root directory for the search. * type - allows the client to specify how to filter the files * text - the search term * numHits - the maximum number of results to return, a value of -1 means no limit diff --git a/src/ca/dracode/ais/service/FileListener.java b/src/ca/dracode/ais/service/FileListener.java index cede3df..d1e8b01 100644 --- a/src/ca/dracode/ais/service/FileListener.java +++ b/src/ca/dracode/ais/service/FileListener.java @@ -36,20 +36,12 @@ public class FileListener extends Service { private static final String TAG = "ca.dracode.ais.service.FileListener"; + private static final int DELAY = 2000; + Handler timerHandler = new Handler(); private AISObserver listener; private IBinder mLocalBinder = new LocalBinder(); private Timer timeSinceLastChange; - private static final int DELAY = 2000; - - @Override - public void onCreate(){ - this.timeSinceLastChange = new Timer(); - this.listener = new AISObserver(Environment.getExternalStorageDirectory().getPath()); - Log.i(TAG, "Starting listener on " + Environment.getExternalStorageDirectory().getPath()); - this.listener.startWatching(); - } - - Handler timerHandler = new Handler(); + private IndexService mBoundService; Runnable timerRunnable = new Runnable() { @Override public void run() { @@ -61,30 +53,77 @@ public void run() { } } }; + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + mBoundService = ((IndexService.LocalBinder) service).getService(); + } + + public void onServiceDisconnected(ComponentName className) { + mBoundService = null; + } + }; + private boolean mIsBound = false; - public void stopListener(){ + @Override + public void onCreate() { + this.timeSinceLastChange = new Timer(); + this.listener = new AISObserver(Environment.getExternalStorageDirectory().getPath()); + Log.i(TAG, "Starting listener on " + Environment.getExternalStorageDirectory().getPath()); + this.listener.startWatching(); + } + + /** + * Tells the listener to stop watching the filesystem and close + */ + public void stopListener() { this.listener.stopWatching(); + this.doUnbindService(); this.stopSelf(); } - private class AISObserver extends FileObserver{ - public AISObserver(String path){ + public IBinder onBind(Intent intent) { + return mLocalBinder; + } + + private void doBindService() { + bindService(new Intent(FileListener.this, + IndexService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + } + + private void doUnbindService() { + if(mIsBound) { + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + doUnbindService(); + } + + private class AISObserver extends FileObserver { + public AISObserver(String path) { super(path); } - public void onEvent(int event, String path){ + @Override + public void onEvent(int event, String path) { if(path != null) { if(path.contains("Android/data/ca.dracode.ais")) { return; } if(event == FileObserver.MODIFY || event == FileObserver.MOVED_TO || event == FileObserver.CREATE) { - if(mBoundService == null){ + if(mBoundService == null) { doBindService(); - while(mBoundService == null){ - try{ + while(mBoundService == null) { + try { Thread.sleep(10); - } catch(InterruptedException e){ + } catch(InterruptedException e) { Log.e(TAG, "Error ", e); } } @@ -95,12 +134,12 @@ public void onEvent(int event, String path){ timerHandler.postDelayed(timerRunnable, DELAY); Log.i(TAG, "File changed: " + path); } else if(event == FileObserver.DELETE || event == FileObserver.MOVED_FROM) { - if(mBoundService == null){ + if(mBoundService == null) { doBindService(); - while(mBoundService == null){ - try{ + while(mBoundService == null) { + try { Thread.sleep(10); - } catch(InterruptedException e){ + } catch(InterruptedException e) { Log.e(TAG, "Error ", e); } } @@ -120,55 +159,4 @@ public FileListener getService() { return FileListener.this; } } - - public IBinder onBind(Intent intent){ - return mLocalBinder; - } - - - private IndexService mBoundService; - private boolean mIsBound = false; - - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. Because we have bound to a explicit - // service that we know is running in our own process, we can - // cast its IBinder to a concrete class and directly access it. - mBoundService = ((IndexService.LocalBinder)service).getService(); - } - - public void onServiceDisconnected(ComponentName className) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - // Because it is running in our same process, we should never - // see this happen. - mBoundService = null; - } - }; - - void doBindService() { - // Establish a connection with the service. We use an explicit - // class name because we want a specific service implementation that - // we know will be running in our own process (and thus won't be - // supporting component replacement by other applications). - bindService(new Intent(FileListener.this, - IndexService.class), mConnection, Context.BIND_AUTO_CREATE); - mIsBound = true; - } - - void doUnbindService() { - if (mIsBound) { - // Detach our existing connection. - unbindService(mConnection); - mIsBound = false; - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - doUnbindService(); - } } \ No newline at end of file diff --git a/src/ca/dracode/ais/service/IndexService.java b/src/ca/dracode/ais/service/IndexService.java index 0cd909d..e73e677 100644 --- a/src/ca/dracode/ais/service/IndexService.java +++ b/src/ca/dracode/ais/service/IndexService.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; import java.util.Queue; import ca.dracode.ais.R; @@ -60,23 +61,70 @@ */ public class IndexService extends Service { - private static String TAG = "ca.dracode.ais.service.IndexService"; - private final IBinder mBinder = new LocalBinder(); - protected boolean interrupt; - private NotificationManager nm; - private int mIsBound = 0; - private boolean doneCrawling; - private boolean canStop = false; - private ArrayList services; - private Queue pIndexes; - private FileIndexer indexer; + private static String TAG = "ca.dracode.ais.service.IndexService"; + private final IBinder mBinder = new LocalBinder(); private final int maxTasks = 30; + protected boolean interrupt; + private NotificationManager nm; + private int mIsBound = 0; + private boolean doneCrawling; + private boolean canStop = false; + private ArrayList services; + private Queue pIndexes; + private FileIndexer indexer; private int tasks = 0; private boolean crawl = false; - public int onStartCommand (Intent intent, int flags, int startId){ + /** + * Retrieves service information from files found in the directory passed and all of its + * subdirectories + * @param directory directory to be searched + * @param services The list to store the given services + */ + private static void getServices(File directory, List services) { + File[] contents = directory.listFiles(); + for(File content : contents) { + if(content.canRead()) { + if(content.isFile()) { + if(content.getName().toLowerCase().endsWith(".is")) { + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader( + content.getAbsolutePath())); + } catch(FileNotFoundException e) { + e.printStackTrace(); + } + if(br != null) { + try { + String name = br.readLine(); + Log.i(TAG, "Found service of name: " + name); + ArrayList tmpExt = new ArrayList(); + if(name != null) { + String tmp; + while((tmp = br.readLine()) != null) { + tmpExt.add(tmp); + Log.i(TAG, "Found Extension: " + tmp); + } + } + br.close(); + services.add(new ParserService(name, + tmpExt)); + } catch(IOException e) { + e.printStackTrace(); + } + } + } + } else { + IndexService.getServices(content, services); + } + } + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { this.crawl = intent.getBooleanExtra("crawl", false); - if(this.crawl){ + if(this.crawl) { canStop = true; doneCrawling = false; this.crawl(); @@ -88,364 +136,347 @@ public int onStartCommand (Intent intent, int flags, int startId){ return START_STICKY; } - @Override - public void onCreate() { - this.services = new ArrayList(); - nm = (NotificationManager) this - .getSystemService(NOTIFICATION_SERVICE); - IndexService.this.notifyPersistent( - getText(R.string.notification_indexer_started), 1); - boolean mExternalStorageAvailable; - boolean mExternalStorageWriteable; - String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state)) { - // We can read and write the media - mExternalStorageAvailable = mExternalStorageWriteable = true; - } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { - // We can only read the media - mExternalStorageAvailable = true; - mExternalStorageWriteable = false; - } else { - mExternalStorageAvailable = mExternalStorageWriteable = false; - } + @Override + public void onCreate() { + this.services = new ArrayList(); + nm = (NotificationManager) this + .getSystemService(NOTIFICATION_SERVICE); + IndexService.this.notifyPersistent( + getText(R.string.notification_indexer_started), 1); + boolean mExternalStorageAvailable; + boolean mExternalStorageWriteable; + String state = Environment.getExternalStorageState(); + if(Environment.MEDIA_MOUNTED.equals(state)) { + // We can read and write the media + mExternalStorageAvailable = mExternalStorageWriteable = true; + } else if(Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + // We can only read the media + mExternalStorageAvailable = true; + mExternalStorageWriteable = false; + } else { + mExternalStorageAvailable = mExternalStorageWriteable = false; + } - if (mExternalStorageAvailable && mExternalStorageWriteable) { - this.getServices(new File(Environment - .getExternalStorageDirectory() + "/Android/data"), this.services); - } else { - notify("Error: External Storage not mounted", 2); - return; - } - Log.i(TAG, "Creating Indexer"); - this.indexer = new FileIndexer(); + if(mExternalStorageAvailable && mExternalStorageWriteable) { + IndexService.getServices(new File(Environment + .getExternalStorageDirectory() + "/Android/data"), this.services); + } else { + notify("Error: External Storage not mounted", 2); + return; + } + Log.i(TAG, "Creating Indexer"); + this.indexer = new FileIndexer(); Log.i(TAG, "Created Indexer"); - this.pIndexes = new LinkedList(); - this.doneCrawling = true; - new Thread(new Runnable() { - public void run() { - //Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - while (true) { - try { - Thread.sleep(1); - } catch (InterruptedException e1) { - e1.printStackTrace(); - } - Indexable tmp = pIndexes.poll(); - if (tmp != null) { - Log.i(TAG, "Indexing: " + tmp.file.getAbsolutePath()); - if (tmp.tmpData == null || tmp.tmpData.size() == 0) { - indexer.buildIndex(tmp.file.getAbsolutePath()); + this.pIndexes = new LinkedList(); + this.doneCrawling = true; + new Thread(new Runnable() { + public void run() { + //Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + while(true) { + try { + Thread.sleep(1); + } catch(InterruptedException e1) { + e1.printStackTrace(); + } + Indexable tmp = pIndexes.poll(); + if(tmp != null) { + Log.i(TAG, "Indexing: " + tmp.file.getAbsolutePath()); + if(tmp.tmpData == null || tmp.tmpData.size() == 0) { + indexer.buildIndex(tmp.file.getAbsolutePath(), -1); + tasks--; + } else { + try { + indexer.buildIndex(tmp.tmpData, + tmp.file); tasks--; - } else { - try { - indexer.buildIndex(tmp.tmpData, - tmp.file); - tasks--; - } catch (Exception e) { - Log.e(TAG, "Error ", e); - } - } - } - if (doneCrawling && mIsBound == 0 - && pIndexes.size() == 0 && canStop) { - Log.i(TAG, "Done Indexing, Closing... "); - indexer.close(); - doneCrawling = false; - NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(1); - IndexService.this.stopSelf(); - break; - } - } - } - }).start(); - } + } catch(Exception e) { + Log.e(TAG, "Error ", e); + } + } + } + if(doneCrawling && mIsBound == 0 + && pIndexes.size() == 0 && canStop) { + Log.i(TAG, "Done Indexing, Closing... "); + indexer.close(); + doneCrawling = false; + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(1); + IndexService.this.stopSelf(); + break; + } + } + } + }).start(); + } - public void stopWhenReady(){ + /** + * Tells the service to stop itself once it is finished its queued tasks + */ + public void stopWhenReady() { canStop = true; } - public static void getServices(File directory, ArrayList services) { - File[] contents = directory.listFiles(); - for (File content : contents) { - if (content.canRead()) { - if (content.isFile()) { - if (content.getName().toLowerCase().endsWith(".is")) { - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader( - content.getAbsolutePath())); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - if (br != null) { - try { - String name = br.readLine(); - Log.i(TAG, "Found service of name: " + name); - ArrayList tmpExt = new ArrayList(); - if (name != null) { - String tmp; - while ((tmp = br.readLine()) != null) { - tmpExt.add(tmp); - Log.i(TAG, "Found Extension: " + tmp); - } - } - br.close(); - services.add(new ParserService(name, - tmpExt)); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } else { - getServices(content, services); - } - } - } - } - - public void onDestroy() { - nm.cancel(1); - } + @Override + public void onDestroy() { + nm.cancel(1); + } - public void crawl(){ - new Thread(new Runnable(){ - public void run(){ + /** + * Starts indexing the external storage directory + */ + public void crawl() { + new Thread(new Runnable() { + public void run() { //Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); try { crawl(Environment.getExternalStorageDirectory()); doneCrawling = true; - } catch(IOException e){ + } catch(IOException e) { Log.e(TAG, "Error", e); } } }).start(); } - public void crawl(File directory) throws IOException { - // Log.i(TAG, "Indexing directory " + directory.getAbsolutePath()); - File[] contents = directory.listFiles(); + /** + * Indexes the directory passed and calls itself on all subdirectories + * @param directory The directory that will be indexed + * @throws IOException + */ + public void crawl(File directory) throws IOException { + // TODO Implement breadth search, indexing important directories such as ~/Documents first + File[] contents = directory.listFiles(); Log.i(TAG, "Indexer Entered Directory " + directory.getAbsolutePath()); - if (contents != null) { - for (File content : contents) { - if(content.getAbsolutePath().contains("Android/data/ca.dracode.ais")){ + if(contents != null) { + for(File content : contents) { + if(content.getAbsolutePath().contains("Android/data/ca.dracode.ais")) { return; } - while(tasks > maxTasks){ + while(tasks > maxTasks) { try { Thread.sleep(10); - } catch(InterruptedException e){ + } catch(InterruptedException e) { Log.e(TAG, "Error", e); } } - if (content.canRead()) { - createIndex(content); - } - } - for (File content : contents) { - if (content.canRead()) { - if (content.isDirectory() - && content.getAbsolutePath().equals( - content.getCanonicalPath())) { - this.crawl(content); - } - } - } - } - } - - + if(content.canRead()) { + createIndex(content); + } + } + for(File content : contents) { + if(content.canRead()) { + if(content.isDirectory() + && content.getAbsolutePath().equals( + content.getCanonicalPath())) { + this.crawl(content); + } + } + } + } + } - public void createIndex(File content){ + /** + * calls for an index to be created for the given file + * @param content The file to be stored in the index + */ + public void createIndex(File content) { String serviceName = null; - if (content.isFile()) { + if(content.isFile()) { int size = services.size(); - for (int j = 0; j < size; j++) { + for(int j = 0; j < size; j++) { int mLoc = content.getName().lastIndexOf(".") + 1; - if (mLoc != 0) { + if(mLoc != 0) { boolean found = services.get(j).checkExtension( content.getName().substring(mLoc) .toLowerCase() ); - if (found) { + if(found) { serviceName = services.get(j).getName(); } } } } - try { - int state =indexer.checkForIndex("id", - content.getAbsolutePath() + ":meta", content.lastModified()); - if (state == 0) { - Log.i(TAG, "Found index for " + content.getName() + "; skipping."); - } else if(state == 1) { + try { + int state = indexer.checkForIndex(content.getAbsolutePath() + ":meta", content.lastModified()); + if(state == 0) { + Log.i(TAG, "Found index for " + content.getName() + "; skipping."); + } else if(state == 1) { Log.i(TAG, "Index for " + content.getName() + " out of date, building index"); try { new RemoteBuilder( content, serviceName); tasks++; - } catch (Exception e) { + } catch(Exception e) { Log.e(TAG, "" + e.getMessage()); } - } - else if(state == -1){ - Log.i(TAG, "Index for " + content.getName() + " not found, building index"); - try { - new RemoteBuilder( - content, - serviceName); + } else if(state == -1) { + Log.i(TAG, "Index for " + content.getName() + " not found, building index"); + try { + new RemoteBuilder( + content, + serviceName); tasks++; - } catch (Exception e) { - Log.e(TAG, "" + e.getMessage()); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void removeIndex(String path){ - + } catch(Exception e) { + Log.e(TAG, "" + e.getMessage()); + } + } + } catch(Exception e) { + e.printStackTrace(); + } } - public void notify(CharSequence c, int id) { - Notification notification = new Notification(R.drawable.file_icon, c, - System.currentTimeMillis()); - Intent intent = new Intent(getApplicationContext(), IndexService.class); - PendingIntent pendingIntent = PendingIntent.getActivity( - getApplicationContext(), 0, intent, 0); - notification.setLatestEventInfo(this, c, c, pendingIntent); - nm.notify(id, notification); - } - - public void notifyPersistent(CharSequence c, int id) { - Notification not = new Notification(R.drawable.file_icon, c, - System.currentTimeMillis()); - PendingIntent contentIntent = PendingIntent.getActivity(this, 0, - new Intent(this, IndexService.class), - Notification.FLAG_ONGOING_EVENT); - not.flags = Notification.FLAG_ONGOING_EVENT; - not.setLatestEventInfo(this, "AIS", "Indexing...", contentIntent); - nm.notify(1, not); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } + /** + * calls for the index matching path to be removed from the index + * @param path the path of the index to be removed + */ + public void removeIndex(String path) { + this.indexer.removeIndex(path); + } - private class Indexable { - private ArrayList tmpData; - private File file; - private IBinder mService; - private String serviceName = null; - private RemoteBuilder builder; + /** + * Creates a notification with the given text + * @param text The text that will appear in the notification + * @param id The id that can be used to stop the notification + */ + private void notify(CharSequence text, int id) { + Notification notification = new Notification(R.drawable.file_icon, text, + System.currentTimeMillis()); + Intent intent = new Intent(getApplicationContext(), IndexService.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + getApplicationContext(), 0, intent, 0); + notification.setLatestEventInfo(this, text, text, pendingIntent); + nm.notify(id, notification); + } - public Indexable(ArrayList tmpData, File file, - IBinder mService, String serviceName, RemoteBuilder builder) { - super(); - this.tmpData = tmpData; - this.file = file; - this.mService = mService; - this.serviceName = serviceName; - this.builder = builder; - } + /** + * Creates the persistent notification showing that the indexer is running + * @param text The text that will appear in the notification + * @param id The id that can be used to stop the notification + */ + private void notifyPersistent(CharSequence text, int id) { + Notification not = new Notification(R.drawable.file_icon, text, + System.currentTimeMillis()); + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, IndexService.class), + Notification.FLAG_ONGOING_EVENT); + not.flags = Notification.FLAG_ONGOING_EVENT; + not.setLatestEventInfo(this, "AIS", "Indexing...", contentIntent); + nm.notify(1, not); + } - } + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } - private class RemoteBuilder { - private ArrayList tmpData; - private File file; - private IBinder mService; - private String serviceName = null; + private class Indexable { + private ArrayList tmpData; + private File file; + private IBinder mService; + private String serviceName = null; + private RemoteBuilder builder; + + public Indexable(ArrayList tmpData, File file, + IBinder mService, String serviceName, RemoteBuilder builder) { + super(); + this.tmpData = tmpData; + this.file = file; + this.mService = mService; + this.serviceName = serviceName; + this.builder = builder; + } - public RemoteBuilder(File file, String serviceName) { - this.file = file; - this.serviceName = serviceName; - if(serviceName != null) { - this.doBindService(getApplicationContext()); - } else{ - pIndexes.add(new Indexable(null, file, null, - null, RemoteBuilder.this)); - } - } + } - void doBindService(Context c) { - // Establish a connection with the service. We use an explicit - // class name because we want a specific service implementation that - // we know will be running in our own process (and thus won't be - // supporting component replacement by other applications). - Log.i(TAG, "Binding to service..."); + private class RemoteBuilder { + private ArrayList tmpData; + private File file; + private IBinder mService; + private String serviceName = null; + + public RemoteBuilder(File file, String serviceName) { + this.file = file; + this.serviceName = serviceName; + if(serviceName != null) { + this.doBindService(getApplicationContext()); + } else { + pIndexes.add(new Indexable(null, file, null, + null, RemoteBuilder.this)); + } + } - if (serviceName == null) { - return; - } - /* - * while(mIsBound > 0){ try { Thread.sleep(1); } catch - * (InterruptedException e) { e.printStackTrace(); } } - */ - if (c.bindService(new Intent(serviceName), mConnection, - Context.BIND_AUTO_CREATE)) { - mIsBound++; - } - Log.i(TAG, "Service is bound = " + mIsBound); - } + /** + * Binds the builders service to the given context + * @param context + */ + void doBindService(Context context) { + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + Log.i(TAG, "Binding to service..."); + + if(serviceName == null) { + return; + } + if(context.bindService(new Intent(serviceName), mConnection, + Context.BIND_AUTO_CREATE)) { + mIsBound++; + } + Log.i(TAG, "Service is bound = " + mIsBound); + } - public void doUnbindService(Context c) { - if (mIsBound > 0) { - // Detach our existing connection. - c.unbindService(mConnection); - mIsBound--; - /* - * if (mIsBound == 0) { nm.cancel(1); stopSelf(); } - */ - } - } + /** + * Unbinds the service from the context + * @param context + */ + public void doUnbindService(Context context) { + if(mIsBound > 0) { + // Detach our existing connection. + context.unbindService(mConnection); + mIsBound--; + } + } - private ServiceConnection mConnection = new ServiceConnection() { - // Called when the connection with the service is established - public void onServiceConnected(ComponentName className, - IBinder service) { - // Following the example above for an AIDL interface, - // this gets an instance of the IRemoteInterface, which we can - // use - // to call on the service - mService = service; - Log.i(TAG, "Service: " + mService); - try { - MClientService tmp = MClientService.Stub - .asInterface(mService); - tmp.loadFile(file.getAbsolutePath()); - tmpData = new ArrayList(); - int pages = tmp.getPageCount(); - for (int i = 0; i < pages; i++) { - tmpData.add(tmp.getWordsForPage(i)); - } - } catch (RemoteException e) { - e.printStackTrace(); - } - // build(); - pIndexes.add(new Indexable(tmpData, file, mService, - serviceName, RemoteBuilder.this)); - doUnbindService(getApplicationContext()); - } + private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, + IBinder service) { + mService = service; + Log.i(TAG, "Service: " + mService); + try { + MClientService tmp = MClientService.Stub + .asInterface(mService); + tmp.loadFile(file.getAbsolutePath()); + tmpData = new ArrayList(); + int pages = tmp.getPageCount(); + for(int i = 0; i < pages; i++) { + tmpData.add(tmp.getWordsForPage(i)); + } + } catch(RemoteException e) { + e.printStackTrace(); + } + // build(); + pIndexes.add(new Indexable(tmpData, file, mService, + serviceName, RemoteBuilder.this)); + doUnbindService(getApplicationContext()); + } - // Called when the connection with the service disconnects - // unexpectedly - public void onServiceDisconnected(ComponentName className) { - Log.e(TAG, "Service has unexpectedly disconnected"); - mService = null; - mIsBound--; - } - }; - } + // Called when the connection with the service disconnects + // unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "Service has unexpectedly disconnected"); + mService = null; + mIsBound--; + } + }; + } - public class LocalBinder extends Binder { - IndexService getService() { - return IndexService.this; - } - } + public class LocalBinder extends Binder { + IndexService getService() { + return IndexService.this; + } + } } diff --git a/src/ca/dracode/ais/service/InfoProxy.java b/src/ca/dracode/ais/service/InfoProxy.java index 4a256aa..a3c183d 100644 --- a/src/ca/dracode/ais/service/InfoProxy.java +++ b/src/ca/dracode/ais/service/InfoProxy.java @@ -26,7 +26,6 @@ import android.content.ServiceConnection; import android.os.IBinder; -import ca.dracode.ais.indexinfo.FileChangeListener; import ca.dracode.ais.indexinfo.IndexComm; public class InfoProxy extends Service { @@ -43,9 +42,7 @@ public boolean isIndexerRunning(){ return false; } - public void setFileChangeListener(FileChangeListener listener){ - - } + public String getCurrentIndexPath(){ return "/sdcard";} }; @Override @@ -58,28 +55,15 @@ public IBinder onBind(Intent intent) { private ServiceConnection mListenerConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. Because we have bound to a explicit - // service that we know is running in our own process, we can - // cast its IBinder to a concrete class and directly access it. mListenerService = ((FileListener.LocalBinder)service).getService(); } public void onServiceDisconnected(ComponentName className) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - // Because it is running in our same process, we should never - // see this happen. mListenerService = null; } }; void doBindService() { - // Establish a connection with the service. We use an explicit - // class name because we want a specific service implementation that - // we know will be running in our own process (and thus won't be - // supporting component replacement by other applications). bindService(new Intent(InfoProxy.this, FileListener.class), mListenerConnection, Context.BIND_AUTO_CREATE); mIsListenerBound = true; @@ -87,7 +71,6 @@ void doBindService() { void doUnbindService() { if (mIsListenerBound) { - // Detach our existing connection. unbindService(mListenerConnection); mIsListenerBound = false; } diff --git a/src/ca/dracode/ais/service/ParserService.java b/src/ca/dracode/ais/service/ParserService.java index 6cc4896..294ccc9 100644 --- a/src/ca/dracode/ais/service/ParserService.java +++ b/src/ca/dracode/ais/service/ParserService.java @@ -45,6 +45,11 @@ public String getName() { return name; } + /** + * Checks to see if this ParserService can handle the given extension + * @param ext The extension that needs to be parsed + * @return true if the ParserService can handle the given extension; false otherwise + */ public boolean checkExtension(String ext) { return this.extensions.contains(ext); } diff --git a/src/ca/dracode/ais/service/SearchData.java b/src/ca/dracode/ais/service/SearchData.java index da759c5..e700945 100644 --- a/src/ca/dracode/ais/service/SearchData.java +++ b/src/ca/dracode/ais/service/SearchData.java @@ -1,34 +1,30 @@ -/******************************************************************************* +/* * Copyright 2014 Benjamin Winger. * - * This file is part of Android Indexing Service. + * This file is part of AIS. * - * Android Indexing Service is free software: you can redistribute it and/or modify + * AIS 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. * - * Android Indexing Service is distributed in the hope that it will be useful, + * AIS 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 Android Indexing Service. If not, see . - ******************************************************************************/ - + * along with AIS. If not, see . + */ package ca.dracode.ais.service; - import java.util.ArrayList; - /* - * SearchData.java - * - * Some container - must verify - * I think it is deprecated code that was replaced with PageResut - */ - +* SearchData.java +* +* Some container - must verify +* I think it is deprecated code that was replaced with PageResut +*/ public class SearchData { - int pages; - ArrayList text = new ArrayList(); -} + int pages; + ArrayList text = new ArrayList(); +} \ No newline at end of file diff --git a/src/ca/dracode/ais/service/SearchService.java b/src/ca/dracode/ais/service/SearchService.java index 7e71306..26b6d25 100644 --- a/src/ca/dracode/ais/service/SearchService.java +++ b/src/ca/dracode/ais/service/SearchService.java @@ -35,98 +35,47 @@ import ca.dracode.ais.indexer.FileIndexer; import ca.dracode.ais.indexer.FileSearcher; -/* +/** * SearchService.java * * Service accessed by the client library with functions to search through the index - * - * Make the indexer search from disk for multi-file searches and search from memory - * for single file searches (load file into memory in load function). */ +// TODO - Make the indexer search from disk for multi-file searches and search from memory +// for single file searches (create new index in RAMDirectory and add appropriate documents in +// the load function). + public class SearchService extends Service { private static final String TAG = "ca.dracode.ais.service.SearchService"; private final BSearchService1_0.Stub mBinder = new BSearchService1_0.Stub() { - public PageResult[] find(String doc, int type, String text, int numHits, int set, int page){ - return sm.find(text, doc, numHits, type, set, page); - } - - /** - * Used to search the contents of multiple files - * @param docs - A list containing the names of the documents that should be searched. This allows metadata - * for multiple files to be in the search service's memory at once. An empty list will - * cause the search service to search all files on the device - * Directories can also be included for search of their contents - * type - allows the client to specify what type of results it wants to receive - * text - the search term - * numHits - the maximum number of results to return per file, a value of -1 means no limit - * @return a list containing the terms found that matched the query and what page of the document they appear on. - */ - public PageResult[] findIn(List docs, int type, String text, int numHits, int set){ - return sm.findIn(text, docs, numHits, set, type); - } - - /** - * Used to search for file names - * @param docs - the root directory for the search. - * type - allows the client to specify how to filter the files - * text - the search term - * numHits - the maximum number of results to return - */ - public List findName(List docs, int type, String text, int numHits, - int set){ - return sm.findName(text, docs, numHits, set, type); - } - - /** - * used to send file contents to the indexing service. Because of the limitations of - * the service communicsation system the information may have to be sent in chunks as - * there can only be a maximum of about 1MB in the buffer at a time (which is shared - * among all applications). The client class sends data in chunks that do not exceed 256KB, - * currently pages cannot exceed 256KB as the data transfer will fail - * @param filePath - the location of the file to be built; used by the indexer to identify the file - * text - the text to be added to the index - * page - the page upon which the chunk of the file that is being transferred starts. - * It is a double to allow the transfer of parts of a single page if the page is too large - * maxPage - the total number of pages in the entire file - * @return 0 if index was built successfully; - * 1 if the file lock was in place due to another build operation being in progress; - * 2 if the Service is still waiting for the rest of the pages - * -1 on error - */ - public int buildIndex(String filePath, List text, double page, int maxPage){ - return SearchService.this.buildIndex(filePath, text, page, maxPage); - } - - /** - * Tells the indexer to load a file's metadata into memory for use in searches. - * The function can be called multiple times to load several files. Files remain loaded until the unload - * function is called. Please make sure to call unload when you are finished with the document. - * @param filePath - the location of the file to prepare; is also the identifier for the file's data in the index - * @return 0 if the file exists in the index and was not already loaded; - * 1 if the file was already loaded; - * 2 if the file was not loaded and does not exist in the index; - * -1 if there was an error - */ - public int load(String filePath){ - return SearchService.this.load(filePath); - } - - /** - * Tells the indexer to unload a file's metadata from memory as it will not be used in future searches. - * @param filePath - the location of the file; used to identify which file should be unloaded - * @return true if the file exists in the index; false otherwise - */ - public boolean unload(String filePath){ - return SearchService.this.unload(filePath); - } - - /** - * Tells the search service to cancel any searches that are currently running - */ - public boolean interrupt(){ - return sm.interrupt(); - } + public PageResult[] find(String doc, int type, String text, int numHits, int set, int page) { + return sm.find(text, doc, numHits, type, set, page); + } + + public PageResult[] findIn(List docs, int type, String text, int numHits, int set) { + return sm.findIn(text, docs, numHits, set, type); + } + + public List findName(List docs, int type, String text, int numHits, + int set) { + return sm.findName(text, docs, numHits, set, type); + } + + public int buildIndex(String filePath, List text, double page, int maxPage) { + return SearchService.this.buildIndex(filePath, text, page, maxPage); + } + + public int load(String filePath) { + return SearchService.this.load(filePath); + } + + public boolean unload(String filePath) { + return SearchService.this.unload(filePath); + } + + public boolean interrupt() { + return sm.interrupt(); + } }; private SearchManager sm; @@ -145,29 +94,46 @@ public void onCreate() { this.data = new HashMap(); } + /** + * used to send file contents to the indexing service. Because of the limitations of + * the service communicsation system the information may have to be sent in chunks as + * there can only be a maximum of about 1MB in the buffer at a time (which is shared + * among all applications). The client class sends data in chunks that do not exceed 256KB, + * currently pages cannot exceed 256KB as the data transfer will fail + * @param filePath - the location of the file to be built; used by the indexer to identify the file + * @param text - the text to be added to the index + * @param page - the page upon which the chunk of the file that is being transferred + * starts. + * It is a double to allow the transfer of parts of a single page if the page is too large + * maxPage - the total number of pages in the entire file + * @return 0 if index was built successfully; + * 1 if the file lock was in place due to another build operation being in progress; + * 2 if the Service is still waiting for the rest of the pages + * -1 on error + */ public int buildIndex(String filePath, List text, double page, int maxPage) { File indexDirFile = new File(FileIndexer.getRootStorageDir()); File[] dirContents = indexDirFile.listFiles(); - if (dirContents != null) { - for (File dirContent : dirContents) { - if (dirContent.getName().equals("write.lock")) { + if(dirContents != null) { + for(File dirContent : dirContents) { + if(dirContent.getName().equals("write.lock")) { return 1; } } } FileIndexer indexer = new FileIndexer(); SearchData tmpData = this.data.get(filePath); - if (page == 0) { + if(page == 0) { tmpData.text.clear(); } - if (page + text.size() != maxPage) { + if(page + text.size() != maxPage) { tmpData.text.addAll(text); } else { tmpData.text.addAll(text); try { indexer.buildIndex(tmpData.text, new File(filePath)); - } catch (Exception ex) { + } catch(Exception ex) { Log.v("PDFIndex", "" + ex.getMessage()); ex.printStackTrace(); } finally { @@ -178,27 +144,37 @@ public int buildIndex(String filePath, List text, double page, return 2; } + /** + * Tells the indexer to load a file's metadata into memory for use in searches. + * The function can be called multiple times to load several files. Files remain loaded until the unload + * function is called. Please make sure to call unload when you are finished with the document. + * @param filePath - the location of the file to prepare; is also the identifier for the file's data in the index + * @return 0 if the file exists in the index and was not already loaded; + * 1 if the file was already loaded; + * 2 if the file was not loaded and does not exist in the index; + * -1 if there was an error + */ public int load(final String filePath) { - if (this.data.containsKey(filePath)) { + if(this.data.containsKey(filePath)) { return 1; } SearchData tmpData = new SearchData(); Document tmp; - if (SearchService.this.sm.searcher == null) { + if(SearchService.this.sm.searcher == null) { Log.e(TAG, "Searcher is null"); return -1; } - if ((tmp = this.sm.searcher.getMetaFile(filePath)) != null) { + if((tmp = this.sm.searcher.getMetaFile(filePath)) != null) { try { IndexableField f = tmp.getField("pages"); - if (f == null) { + if(f == null) { Log.e(TAG, "Cannot find pages in metafile: " + tmp.toString()); return -1; } else { tmpData.pages = f.numericValue().intValue(); } - } catch (Exception e) { + } catch(Exception e) { Log.e(TAG, "Error", e); return -1; } @@ -210,6 +186,11 @@ public int load(final String filePath) { } + /** + * Tells the indexer to unload a file's metadata from memory as it will not be used in future searches. + * @param path - the location of the file; used to identify which file should be unloaded + * @return true if the file exists in the index; false otherwise + */ public boolean unload(String path) { return this.data.remove(path) != null; } @@ -217,33 +198,55 @@ public boolean unload(String path) { private class SearchManager { boolean found; boolean finishedLoading = false; - private FileSearcher searcher; + private FileSearcher searcher; public SearchManager() { - this.searcher = new FileSearcher(); + this.searcher = new FileSearcher(); } - private boolean interrupt(){ - return searcher.interrupt(); - } + /** + * Tells the search service to cancel any searches that are currently running + */ + private boolean interrupt() { + return searcher.interrupt(); + } - private List findName(String term, List directory, int numHits, int set, - int type){ - return this.searcher.findName(term, "path", directory, "path", numHits, set, type); - } + /** + * Used to search for file names + * @param directory - A list containing directories to search + * type - allows the client to specify how to filter the files + * text - the search term + * numHits - the maximum number of results to return + */ + private List findName(String term, List directory, int numHits, int set, + int type) { + return this.searcher.findName(term, "path", directory, "path", numHits, set, type); + } - private PageResult[] findIn(String term, List documents, int numHits, int set, - int type){ - return this.searcher.findIn(term, "text", documents, "path", numHits, set, type); - } + /** + * Used to search the contents of multiple files + * @param documents - A list containing the names of the documents that should be + * searched. This allows metadata + * for multiple files to be in the search service's memory at once. An empty list will + * cause the search service to search all files on the device + * Directories can also be included for search of their contents + * type - allows the client to specify what type of results it wants to receive + * text - the search term + * numHits - the maximum number of results to return per file, a value of -1 means no limit + * @return a list containing the terms found that matched the query and what page of the document they appear on. + */ + private PageResult[] findIn(String term, List documents, int numHits, int set, + int type) { + return this.searcher.findInFiles(term, "text", documents, "path", numHits, set, type); + } private PageResult[] find(String term, String constrainValue, int maxResults, int set, int type, int page) { /** * TODO - Preload information about the index in the load function for use here * **/ - Log.i(TAG, "Received request to search for: " + term); - return this.searcher.find(term, "text", + Log.i(TAG, "Received request to search for: " + term); + return this.searcher.findInFile(term, "text", constrainValue, "path", maxResults, set, type, page); } } diff --git a/src/ca/dracode/ais/ui/AISApplication.java b/src/ca/dracode/ais/ui/AISApplication.java index adb706d..e2d28f9 100644 --- a/src/ca/dracode/ais/ui/AISApplication.java +++ b/src/ca/dracode/ais/ui/AISApplication.java @@ -22,8 +22,6 @@ import android.app.Application; import android.util.Log; -import org.apache.lucene.search.IndexSearcher; - /* * AISApplication.java * @@ -36,7 +34,6 @@ public class AISApplication extends Application { private final static String TAG = "ca.dracode.ais.ais"; - protected IndexSearcher indexSearcher; // TODO - Use this as a global searcher for whenever the aplication is running (is this necessary?) public void onCreate() { super.onCreate(); diff --git a/src/ca/dracode/ais/ui/AISPreferences.java b/src/ca/dracode/ais/ui/AISPreferences.java index 4f53dff..dcb358b 100644 --- a/src/ca/dracode/ais/ui/AISPreferences.java +++ b/src/ca/dracode/ais/ui/AISPreferences.java @@ -28,27 +28,22 @@ import ca.dracode.ais.indexinfo.IndexInfo; public class AISPreferences extends PreferenceActivity { - private boolean enabled; private final IndexInfo indexInfo = new IndexInfo(); + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.prefs); SwitchPreference mEnableWifi = (SwitchPreference) findPreference("enabled"); mEnableWifi.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference.isEnabled() != newValue) { - if(!((Boolean)newValue)){ + if(!((Boolean) newValue)) { indexInfo.stopIndexer(getApplicationContext()); } else { indexInfo.startIndexer(getApplicationContext()); } return true; - } else { - return false; - } } }); } diff --git a/src/ca/dracode/ais/ui/MainActivity.java b/src/ca/dracode/ais/ui/MainActivity.java index 772b12c..7472a64 100644 --- a/src/ca/dracode/ais/ui/MainActivity.java +++ b/src/ca/dracode/ais/ui/MainActivity.java @@ -33,29 +33,31 @@ public class MainActivity extends Activity { - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.setContentView(R.layout.activity_main); - TextView t = (TextView)findViewById(R.id.textView1); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.setContentView(R.layout.activity_main); + TextView t = (TextView) findViewById(R.id.textView1); + + // TODO Remove testing code Intent serviceIntent = new Intent(this, IndexService.class); serviceIntent.putExtra("crawl", true); this.startService(serviceIntent); Intent serviceIntent2 = new Intent(this, FileListener.class); this.startService(serviceIntent2); Alarm.SetAlarm(this); - } + } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.main, menu); - return true; - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } @Override - public boolean onOptionsItemSelected(MenuItem item){ + public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); - if (id == R.id.action_settings) { + if(id == R.id.action_settings) { Intent i = new Intent(this, AISPreferences.class); startActivity(i); return true;