package weiweiwang.github.search; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.provider.CallLog.Calls; import android.provider.ContactsContract.CommonDataKinds.Phone; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.Term; import weiweiwang.github.search.utils.PinyinConverter; import java.util.HashMap; import java.util.Map; public class SearchService extends AbstractSearchService { public static final String TAG = SearchService.class.getName(); private static final String INDEX_DIR="idx"; private static final int REBUILD_TRIGGER_THRESHOLD=50; private static AbstractSearchService INSTANCE =null; private Context context; private SearchService(Context context) { super(context.getDir(INDEX_DIR, Context.MODE_PRIVATE)); this.context = context; if(indexWriter.numDocs()<REBUILD_TRIGGER_THRESHOLD) { asyncRebuild(true); } } public static synchronized AbstractSearchService getInstance(Context context) { if (null == INSTANCE) { INSTANCE = new SearchService(context); } return INSTANCE; } @Override public void destroy() { super.destroy(); INSTANCE = null; log(TAG, "destroy " + getClass().getSimpleName()); } @Override public long rebuildCalllog(boolean urgent) { long start = System.currentTimeMillis(); ContentResolver cr = this.context.getContentResolver(); Cursor cur = cr.query(Calls.CONTENT_URI, new String[] { Calls._ID, // 0 Calls.NUMBER, // 1 Calls.DATE, // 2 Calls.TYPE, // 3 }, Calls.CACHED_NAME + " is NULL", null, Calls.DEFAULT_SORT_ORDER + " LIMIT 200"); // 只build最近的200条通话记录,加快速度 long hits = 0; try { indexWriter .deleteDocuments(new Term(FIELD_TYPE, INDEX_TYPE_CALLLOG)); // reuseable document and fields Document document = new Document(); Field numberField = createTextField(FIELD_NUMBER, ""); Field typeField = createStringField(FIELD_TYPE,INDEX_TYPE_CALLLOG); document.add(numberField); document.add(typeField); long current = System.currentTimeMillis(); Map<String, Object[]> aggregatedCalllogs = new HashMap<String, Object[]>(); while (cur.moveToNext()) { hits++; try { String number = stripNumber(cur.getString(1)); long date = cur.getLong(2); int type = cur.getInt(3); long diff = (current - date); float boost = (type == Calls.OUTGOING_TYPE ? 2.0f : 1.0f) + 1.0f / (diff / (float) ONE_DAY_IN_MILLISECONDS + 1.0f); if (!aggregatedCalllogs.containsKey(number)) { aggregatedCalllogs.put(number, new Object[] { number, boost }); } // log(TAG,"rebuild calllog:"+number+",boost:"+boost); } catch (Exception e) { log(TAG, e.toString()); } } cur.close(); for (Map.Entry<String, Object[]> entry : aggregatedCalllogs .entrySet()) { Object[] arr = entry.getValue(); String number = (String) arr[0]; float boost = (Float) arr[1]; numberField.setStringValue(number); numberField.setBoost(boost); indexWriter.addDocument(document); if (!urgent) { yieldInterrupt(); } } Map<String, String> userData = new HashMap<String, String>(); userData.put("action", "rebuild"); indexWriter.commit(userData); long end = System.currentTimeMillis(); log(TAG, "commited " + hits + " calllogs, time used:" + (end - start) + ",numDocs:" + indexWriter.numDocs()); } catch (Exception e) { android.util.Log.w(TAG, e.toString(), e); } return hits; } @Override public long rebuildContacts(boolean urgent) { long start = System.currentTimeMillis(); ContentResolver cr = this.context.getContentResolver(); // 取得电话本中开始一项的光标,必须先moveToNext() Cursor cur = cr .query(Phone.CONTENT_URI, new String[] { Phone.CONTACT_ID, // 0 Phone.DISPLAY_NAME, // 1 Phone.NUMBER, // 2 Phone.STARRED, // 3 Phone.TIMES_CONTACTED, // 4 Phone.LAST_TIME_CONTACTED, // 5 }, null, null, String.format( "%s DESC, %s DESC, %s DESC", Phone.STARRED, Phone.LAST_TIME_CONTACTED, Phone.TIMES_CONTACTED)); long hits = 0; PinyinConverter pinyinConverter = PinyinConverter .getInstance(this.context); Document document = new Document(); Field idField = createStringField(FIELD_ID,""); Field typeField = createStringField(FIELD_TYPE,INDEX_TYPE_CONTACT); Field nameField = createStringField(FIELD_NAME,""); Field numberField = createTextField(FIELD_NUMBER, ""); Field pinyinField = createTextField(FIELD_PINYIN,""); // Field t9Field = createTextField(FIELD_T9,""); document.add(pinyinField); // document.add(t9Field); document.add(nameField); document.add(numberField); document.add(idField); document.add(typeField); try { indexWriter .deleteDocuments(new Term(FIELD_TYPE, INDEX_TYPE_CONTACT)); long current = System.currentTimeMillis(); while (cur.moveToNext()) { hits++; try { String id = cur.getString(0); String name = cur.getString(1); String number = stripNumber(cur.getString(2)); boolean starred = cur.getInt(3) != 0; int contacted = cur.getInt(4); long lastContacted = cur.getLong(5); float boost = (starred ? 5.0f : 1.0f) + contacted / (1.0f + (current - lastContacted) / (7.0f * ONE_DAY_IN_MILLISECONDS)); String pinyin = pinyinConverter.convert(name, true); // log(TAG, "index contact:" + id + "\t" + name // + "\t" + number + "\t" + pinyin + "\t" + boost // + "\t" + starred + "\t" + contacted + "\t" // + lastContacted); idField.setStringValue(id); numberField.setStringValue(number); nameField.setStringValue(name); // t9Field.setStringValue(pinyin); pinyinField.setStringValue(pinyin); pinyinField.setBoost(boost); // t9Field.setBoost(boost); numberField.setBoost(boost); indexWriter.addDocument(document); if (!urgent) { yieldInterrupt(); } } catch (Exception e) { log(TAG, e.toString()); } } cur.close(); Map<String, String> userData = new HashMap<String, String>(); userData.put("action", "rebuild-contacts"); userData.put("time", String.valueOf(System.currentTimeMillis())); indexWriter.commit(userData); long end = System.currentTimeMillis(); log(TAG, "commited " + hits + " contacts, time used:" + (end - start) + ",numDocs:" + indexWriter.numDocs()); } catch (Exception e) { android.util.Log.w(TAG, e.toString(), e); } return hits; } @Override protected void log(String tag, String msg) { android.util.Log.d(tag,msg); } }