diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 552c1d0..2d87202 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -77,10 +77,9 @@ android:name="ca.dracode.ais.service.SearchService" android:exported="true" android:label="@string/app_name" - android:process=":search" + android:process=":index" android:permission="ca.dracode.permission.AIS_SEARCH"> - diff --git a/pom.xml b/pom.xml index 48dc4f3..ebc1784 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,6 @@ package true - my-release-key.keystore @@ -141,7 +140,9 @@ ${project.build.directory}/${project.artifactId}-${project.version}-RELEASE.apk - + + false + diff --git a/res/values/strings.xml b/res/values/strings.xml index 8484f43..8bbb37d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -32,4 +32,5 @@ Allows the app to search content stored in the Lucene index created by the Android Indexing Service. All data stored in the index will be available to the service. + Connect to AIS Indexer diff --git a/src/ca/dracode/ais/indexclient/MClientService.aidl b/src/ca/dracode/ais/indexclient/MClientService.aidl index 2d59a18..64af49e 100644 --- a/src/ca/dracode/ais/indexclient/MClientService.aidl +++ b/src/ca/dracode/ais/indexclient/MClientService.aidl @@ -1,14 +1,24 @@ - +/** + * Copyright 2014 Benjamin Winger + * + * This file is part of The Android Indexing Service Client Library. + * + * The Android Indexing Service Client Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Android Indexing Service Client Library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with The Android Indexing Service Client Library. If not, see . + */ package ca.dracode.ais.indexclient; -/* - * MClientService.aidl - * - * Service interface file implemented by the client application that allows the - * index service to parse files using the client application - * - */ interface MClientService { /** * Load libraries to access the file here so that it only has to be done once. @@ -23,11 +33,11 @@ interface MClientService { * @param page - the page to be returned from the file * @return - A string containing all of the words on the page */ - String getWordsForPage(int page); + String getWordsForPage(int page, String path); /** - * Gets the number of pages in the file + * Gets the number of pages in the file previously specified in loadFile(String path) * @return - the number of pages in the file specified at loadFile(String path) */ - int getPageCount(); + int getPageCount(String path); } diff --git a/src/ca/dracode/ais/indexdata/SearchResult.java b/src/ca/dracode/ais/indexdata/SearchResult.java index d84dd09..d520ee2 100644 --- a/src/ca/dracode/ais/indexdata/SearchResult.java +++ b/src/ca/dracode/ais/indexdata/SearchResult.java @@ -83,7 +83,7 @@ public LinkedHashMap> getFirstResult(){ */ public LinkedHashMap> getResultAtIndex(int index){ if(this.results.size() > 0) - return (LinkedHashMap>)this.results.entrySet().toArray()[index]; + return (LinkedHashMap>)(this.results.values().toArray()[index]); else return null; } diff --git a/src/ca/dracode/ais/indexer/FileIndexer.java b/src/ca/dracode/ais/indexer/FileIndexer.java index 27633a5..2bd7dc1 100644 --- a/src/ca/dracode/ais/indexer/FileIndexer.java +++ b/src/ca/dracode/ais/indexer/FileIndexer.java @@ -104,7 +104,7 @@ public static void Build(IndexWriter writer, File file, int page, writer.updateDocument(new Term("id", file.getPath() + ":" + page), doc); } - //Log.i(TAG, "Done Indexing file: " + file.getName() + " " + page); + Log.i(TAG, "Done Indexing file: " + file.getName() + " " + page); } catch(Exception e) { Log.e(TAG, "Error ", e); } @@ -228,7 +228,7 @@ public int buildIndex(String filename, int pages) { writer.updateDocument(new Term("id", file.getPath() + ":meta"), doc); } - Log.i(TAG, "Done creating metadata for file " + filename); + //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) { @@ -271,10 +271,12 @@ public int buildIndex(List contents, File file) { */ 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(); + if(writer != null) { + 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 d652f17..3716256 100644 --- a/src/ca/dracode/ais/indexer/FileSearcher.java +++ b/src/ca/dracode/ais/indexer/FileSearcher.java @@ -272,11 +272,13 @@ public SearchResult findInFiles(int id, String term, String field, public SearchResult findInFile(int id, String term, String field, String constrainValue, String constrainField, int maxResults, int set, int type, final int page) { + + Query qry = this.getQuery(term, field, type); + Log.i(TAG, "Query: " + term + " " + field + " " + type + " " + constrainValue); if(this.interrupt == id) { this.interrupt = -1; return null; } - Query qry = this.getQuery(term, field, type); if(qry != null){ String[] values = {constrainValue}; Filter filter; @@ -301,8 +303,8 @@ public SearchResult findInFile(int id, String term, String field, String constra filter = this.getFilter(constrainField, Arrays.asList(values), type, 0, page - 1); hits = concat(hits, indexSearcher.search(qry, filter, - maxResults * set + maxResults -hits.length, - new Sort(new SortField("page", SortField.Type.INT))).scoreDocs); + maxResults, + sort).scoreDocs); } } else { sort = new Sort(new SortField("page", SortField.Type.INT, true)); @@ -314,7 +316,7 @@ public SearchResult findInFile(int id, String term, String field, String constra filter = this.getFilter(constrainField, Arrays.asList(values), type, page, Integer.MAX_VALUE); hits = concat(hits, indexSearcher.search(qry, filter, - maxResults * -(set + 1) + maxResults - hits.length, + maxResults - hits.length, sort).scoreDocs); } else { ScoreDoc[] tmp = hits; @@ -364,8 +366,18 @@ private Query getQuery(String term, String field, int type) { Query qry = null; if(type == FileSearcher.QUERY_BOOLEAN) { qry = new BooleanQuery(); - ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, "*" + term + "*")), + String[] words = term.split(" "); + ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, "*" + words[0])), BooleanClause.Occur.MUST); + if(words.length > 1) { + for(int i = 1; i < words.length - 1; i++) { + ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, words[i])), + BooleanClause.Occur.MUST); + } + ((BooleanQuery) qry).add(new WildcardQuery(new Term(field, + words[words.length - 1] + "*")), + BooleanClause.Occur.MUST); + } } else if(type == FileSearcher.QUERY_STANDARD) { try { qry = new QueryParser(Version.LUCENE_47, field, @@ -387,9 +399,14 @@ private Filter getFilter(String constrainField, List constrainValues, int type, int startPage, int endPage){ BooleanQuery cqry = new BooleanQuery(); - for(String s : constrainValues) { - cqry.add(new TermQuery(new Term(constrainField, s)), - BooleanClause.Occur.SHOULD); + if(constrainValues.size() == 1){ + cqry.add(new TermQuery(new Term(constrainField, constrainValues.get(0))), + BooleanClause.Occur.MUST); + } else { + for(String s : constrainValues) { + cqry.add(new TermQuery(new Term(constrainField, s)), + BooleanClause.Occur.SHOULD); + } } if(type == FileSearcher.QUERY_BOOLEAN && startPage != -1 && endPage != -1) { cqry.add(NumericRangeQuery.newIntRange("page", startPage, endPage, true, true), @@ -410,7 +427,7 @@ private Filter getFilter(String constrainField, List constrainValues, * @return A SearchResult containing the results sorted by relevance and page */ private SearchResult getHighlightedResults(List docs, Query qry, int type, - String term, int maxResults){ + String term, int maxResults){ try { int numResults = 0; LinkedHashMap>> results = new LinkedHashMap>>(); @@ -469,9 +486,11 @@ private SearchResult getHighlightedResults(List docs, Query qry, int t */ private ArrayList getDocs(int maxResults, int set, ScoreDoc[] hits){ ArrayList docs = new ArrayList(); - if(set > 0) { - for(int i = maxResults * set; i < hits.length && i < (maxResults * set + - maxResults); + int max = maxResults; + if(max > hits.length)max = hits.length; + Log.i(TAG, "Max: " + maxResults + " Set: " + set); + if(set >= 0) { + for(int i = 0; i < hits.length; i++) { try { Document tmp = indexSearcher.doc(hits[i].doc); @@ -481,8 +500,8 @@ private ArrayList getDocs(int maxResults, int set, ScoreDoc[] hits){ } } } else { - for(int i = 0; i < hits.length && i < (maxResults * -(set + 1) + - maxResults); + for(int i = 0; i < hits.length && i < (max * -(set + 1) + + max); i++) { try { Document tmp = indexSearcher.doc(hits[i].doc); @@ -492,6 +511,7 @@ private ArrayList getDocs(int maxResults, int set, ScoreDoc[] hits){ } } } + Log.i(TAG, "doc size" + docs.size()); return docs; } diff --git a/src/ca/dracode/ais/service/BSearchService1_0.aidl b/src/ca/dracode/ais/service/BSearchService1_0.aidl index 8afc7d8..8c7ea71 100644 --- a/src/ca/dracode/ais/service/BSearchService1_0.aidl +++ b/src/ca/dracode/ais/service/BSearchService1_0.aidl @@ -72,22 +72,14 @@ interface BSearchService1_0 { int resultSet); /** - * 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 + * Tells the indexer to try to build the given file * @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 */ - int buildIndex(int id, String filePath, in List text, double page, int maxPage); + int buildIndex(int id, String filePath); /** * Tells the indexer to load a file's metadata into memory for use in searches. diff --git a/src/ca/dracode/ais/service/FileListener.java b/src/ca/dracode/ais/service/FileListener.java index ac639e6..6272d05 100644 --- a/src/ca/dracode/ais/service/FileListener.java +++ b/src/ca/dracode/ais/service/FileListener.java @@ -130,7 +130,7 @@ public void onEvent(int event, String path) { } // If a file was changed or created, re-index file or index the new file mBoundService.createIndex(new File(Environment.getExternalStorageDirectory() - .getAbsolutePath() + path)); + .getAbsolutePath() + path), null); timerHandler.postDelayed(timerRunnable, DELAY); Log.i(TAG, "File changed: " + path); } else if(event == FileObserver.DELETE || event == FileObserver.MOVED_FROM) { diff --git a/src/ca/dracode/ais/service/IndexService.java b/src/ca/dracode/ais/service/IndexService.java index 3ab4f3b..5972658 100644 --- a/src/ca/dracode/ais/service/IndexService.java +++ b/src/ca/dracode/ais/service/IndexService.java @@ -123,7 +123,9 @@ private static void getServices(File directory, List services) { @Override public int onStartCommand(Intent intent, int flags, int startId) { - this.crawl = intent.getBooleanExtra("crawl", false); + if(intent != null) { + this.crawl = intent.getBooleanExtra("crawl", false); + } if(this.crawl) { canStop = true; doneCrawling = false; @@ -180,14 +182,19 @@ public void run() { } Indexable tmp = pIndexes.poll(); if(tmp != null) { - Log.i(TAG, "Indexing: " + tmp.file.getAbsolutePath()); + //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); + if(tmp.callback != null) { + tmp.callback.indexCreated(tmp.file, indexer.buildIndex(tmp.tmpData, + tmp.file)); + } else { + indexer.buildIndex(tmp.tmpData, + tmp.file); + } tasks--; } catch(Exception e) { Log.e(TAG, "Error ", e); @@ -260,7 +267,7 @@ public void crawl(File directory) throws IOException { } } if(content.canRead()) { - createIndex(content); + createIndex(content, null); } } for(File content : contents) { @@ -275,14 +282,17 @@ public void crawl(File directory) throws IOException { } } + public interface IndexCallback{ + public void indexCreated(File content, int retval); + } + /** * 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) { + public void createIndex(File content, IndexCallback callback) { String serviceName = null; if(content.isFile()) { - int size = services.size(); for(ParserService service : services) { int mLoc = content.getName().lastIndexOf(".") + 1; if(mLoc != 0) { @@ -299,23 +309,23 @@ public void createIndex(File content) { try { int state = indexer.checkForIndex(content.getAbsolutePath() + ":meta", content.lastModified()); if(state == 0) { - Log.i(TAG, "Found index for " + content.getName() + "; skipping."); + //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"); + //Log.i(TAG, "Index for " + content.getName() + " out of date, building index"); try { new RemoteBuilder( content, - serviceName); + serviceName, null); tasks++; } catch(Exception e) { Log.e(TAG, "" + e.getMessage()); } } else if(state == -1) { - Log.i(TAG, "Index for " + content.getName() + " not found, building index"); + //Log.i(TAG, "Index for " + content.getName() + " not found, building index"); try { new RemoteBuilder( content, - serviceName); + serviceName, null); tasks++; } catch(Exception e) { Log.e(TAG, "" + e.getMessage()); @@ -376,15 +386,18 @@ private class Indexable { private IBinder mService; private String serviceName = null; private RemoteBuilder builder; + private IndexCallback callback; public Indexable(ArrayList tmpData, File file, - IBinder mService, String serviceName, RemoteBuilder builder) { + IBinder mService, String serviceName, RemoteBuilder builder, + IndexCallback callback) { super(); this.tmpData = tmpData; this.file = file; this.mService = mService; this.serviceName = serviceName; this.builder = builder; + this.callback = callback; } } @@ -394,15 +407,17 @@ private class RemoteBuilder { private File file; private IBinder mService; private String serviceName = null; + IndexCallback callback; - public RemoteBuilder(File file, String serviceName) { + public RemoteBuilder(File file, String serviceName, IndexCallback callback) { this.file = file; this.serviceName = serviceName; + this.callback = callback; if(serviceName != null) { this.doBindService(getApplicationContext()); } else { pIndexes.add(new Indexable(null, file, null, - null, RemoteBuilder.this)); + null, RemoteBuilder.this, callback)); } } @@ -450,16 +465,16 @@ public void onServiceConnected(ComponentName className, .asInterface(mService); tmp.loadFile(file.getAbsolutePath()); tmpData = new ArrayList(); - int pages = tmp.getPageCount(); + int pages = tmp.getPageCount(file.getAbsolutePath()); for(int i = 0; i < pages; i++) { - tmpData.add(tmp.getWordsForPage(i)); + tmpData.add(tmp.getWordsForPage(i, file.getAbsolutePath())); } } catch(RemoteException e) { - e.printStackTrace(); + Log.e(TAG, "error", e); } // build(); pIndexes.add(new Indexable(tmpData, file, mService, - serviceName, RemoteBuilder.this)); + serviceName, RemoteBuilder.this, callback)); doUnbindService(getApplicationContext()); } @@ -478,5 +493,4 @@ IndexService getService() { return IndexService.this; } } - } diff --git a/src/ca/dracode/ais/service/SearchService.java b/src/ca/dracode/ais/service/SearchService.java index 45f1a3e..bd5fa5d 100644 --- a/src/ca/dracode/ais/service/SearchService.java +++ b/src/ca/dracode/ais/service/SearchService.java @@ -20,7 +20,10 @@ package ca.dracode.ais.service; import android.app.Service; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; @@ -32,7 +35,6 @@ import java.util.List; import ca.dracode.ais.indexdata.SearchResult; -import ca.dracode.ais.indexer.FileIndexer; import ca.dracode.ais.indexer.FileSearcher; /** @@ -45,9 +47,10 @@ // for single file searches (create new index in RAMDirectory and add appropriate documents in // the load function). -public class SearchService extends Service { +public class SearchService extends Service implements IndexService.IndexCallback { private static final String TAG = "ca.dracode.ais.service.SearchService"; int currentId = 0; + HashMap builtIndexes; private final BSearchService1_0.Stub mBinder = new BSearchService1_0.Stub() { public SearchResult find(int id, String doc, int type, String text, int numHits, int set, int page) { @@ -64,9 +67,8 @@ public List findName(int id, List docs, int type, String text, i return sm.findName(id, text, docs, numHits, set, type); } - public int buildIndex(int id, String filePath, List text, double page, - int maxPage) { - return SearchService.this.buildIndex(id, filePath, text, page, maxPage); + public int buildIndex(int id, String filePath) { + return SearchService.this.buildIndex(id, filePath); } public int load(String filePath) { @@ -85,7 +87,18 @@ public int getId(){ return ++currentId; } }; + 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 SearchManager sm; + private IndexService mBoundService; + private boolean mIsBound = false; // private final IBinder mBinder = new LocalBinder(); private HashMap data; @@ -100,56 +113,42 @@ public void onCreate() { super.onCreate(); this.sm = new SearchManager(); this.data = new HashMap(); + this.builtIndexes = new HashMap(); + } + + private void doBindService() { + bindService(new Intent(SearchService.this, + IndexService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + } + + private void doUnbindService() { + if(mIsBound) { + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + } } /** - * 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 + * Tells the indexer to try to build the given file * @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(int id, 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")) { - return 1; - } - } - } - FileIndexer indexer = new FileIndexer(); - SearchData tmpData = this.data.get(filePath); - if(page == 0) { - tmpData.text.clear(); + public int buildIndex(int id, String filePath) { + if(!mIsBound){ + doBindService(); } - 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) { - Log.v("PDFIndex", "" + ex.getMessage()); - ex.printStackTrace(); - } finally { - Log.i(TAG, "Built Index"); - } - return 0; + while(!mIsBound){ + } - return 2; + mBoundService.createIndex(new File(filePath), this); + mBoundService.stopWhenReady(); + unbindService(mConnection); + return waitForIndexer(new File(filePath)); } /** @@ -172,7 +171,8 @@ public int load(final String filePath) { Log.e(TAG, "Searcher is null"); return -1; } - if((tmp = this.sm.searcher.getMetaFile(filePath)) != null) { + Log.i(TAG, "Loading: " + filePath + " " + new File(filePath).getAbsolutePath()); + if((tmp = this.sm.searcher.getMetaFile(new File(filePath).getAbsolutePath())) != null) { try { IndexableField f = tmp.getField("pages"); if(f == null) { @@ -223,7 +223,7 @@ private boolean interrupt(int id) { * Used to search for file names * @param directory - A list containing directories to search * @param type - allows the client to specify how to filter the files - * @param text - the search term + * @param term - the search term * @param numHits - the maximum number of results to return */ private List findName(int id, String term, List directory, int numHits, @@ -253,7 +253,7 @@ private SearchResult findIn(int id, String term, List documents, int num } private SearchResult find(int id, String term, String constrainValue, int maxResults, - int set, int type, int page) { + int type, int set, int page) { /** * TODO - Preload information about the index in the load function for use here * **/ @@ -263,4 +263,18 @@ private SearchResult find(int id, String term, String constrainValue, int maxRes } } + public int waitForIndexer(File content){ + while(!this.builtIndexes.containsKey(content)){ + try{ + wait(5); + } catch(InterruptedException e){ + Log.e(TAG, "Error", e); + } + } + return this.builtIndexes.get(content); + } + + public void indexCreated(File content, int retval){ + this.builtIndexes.put(content, retval); + } }