102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert/*
202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * Copyright (C) 2009 The Android Open Source Project
302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert *
402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * you may not use this file except in compliance with the License.
602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * You may obtain a copy of the License at
702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert *
802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert *
1002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * Unless required by applicable law or agreed to in writing, software
1102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
1202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * See the License for the specific language governing permissions and
1402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert * limitations under the License.
1502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert */
1602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
1702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertpackage com.android.quicksearchbox.benchmarks;
1802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
1902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.app.Activity;
2002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.app.SearchManager;
2102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.app.SearchableInfo;
2202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.content.ComponentName;
2302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.content.ContentResolver;
2402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.database.ContentObserver;
2502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.database.Cursor;
2602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.database.DataSetObserver;
2702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.net.Uri;
2802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.os.Bundle;
2902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.os.Handler;
3002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.os.Looper;
3102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport android.util.Log;
3202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
3302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport java.util.concurrent.ExecutorService;
3402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertimport java.util.concurrent.Executors;
3502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
3602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringertpublic abstract class SourceLatency extends Activity {
3702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
3802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private static final String TAG = "SourceLatency";
3902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
4002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private SearchManager mSearchManager;
4102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
4202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private ExecutorService mExecutorService;
4302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
4402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    @Override
4502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    protected void onCreate(Bundle savedInstanceState) {
4602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        super.onCreate(savedInstanceState);
4702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
4802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        mSearchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
4902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        mExecutorService = Executors.newSingleThreadExecutor();
5002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
5102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
5202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    @Override
5302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    protected void onResume() {
5402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        super.onResume();
5502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
5602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // TODO: call finish() when all tasks are done
5702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
5802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
5902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private SearchableInfo getSearchable(ComponentName componentName) {
6002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        SearchableInfo searchable = mSearchManager.getSearchableInfo(componentName);
6102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        if (searchable == null || searchable.getSuggestAuthority() == null) {
6202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            throw new RuntimeException("Component is not searchable: "
6302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    + componentName.flattenToShortString());
6402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
6502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        return searchable;
6602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
6702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
6802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    /**
6902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert     * Keeps track of timings in nanoseconds.
7002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert     */
7102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private static class ElapsedTime {
7202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private long mTotal = 0;
7302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private int mCount = 0;
7402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        public synchronized void addTime(long time) {
7502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            mTotal += time;
7602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            mCount++;
7702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
7802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        public synchronized long getTotal() {
7902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            return mTotal;
8002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
8102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        public synchronized long getAverage() {
8202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            return mTotal / mCount;
8302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
8402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        public synchronized int getCount() {
8502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            return mCount;
8602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
8702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
8802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
8902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    public void checkSourceConcurrent(final String src, final ComponentName componentName,
9002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            String query, long delay) {
9102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        final ElapsedTime time = new ElapsedTime();
9202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        final SearchableInfo searchable = getSearchable(componentName);
9302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        int length = query.length();
9402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        for (int end = 0; end <= length; end++) {
9502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            final String prefix = query.substring(0, end);
9602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            (new Thread() {
9702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                @Override
9802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                public void run() {
9902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    long t = checkSourceInternal(src, searchable, prefix);
10002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    time.addTime(t);
10102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                }
10202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }).start();
10302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            try {
10402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Thread.sleep(delay);
10502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            } catch (InterruptedException ex) {
10602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Log.e(TAG, "sleep() in checkSourceConcurrent() interrupted.");
10702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
10802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
10902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        int count = length + 1;
11002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // wait for all requests to finish
11102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        while (time.getCount() < count) {
11202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            try {
11302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Thread.sleep(1000);
11402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            } catch (InterruptedException ex) {
11502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Log.e(TAG, "sleep() in checkSourceConcurrent() interrupted.");
11602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
11702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
11802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        Log.d(TAG, src + "[DONE]: " + length + " queries in " + formatTime(time.getAverage())
11902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                + " (average), " + formatTime(time.getTotal()) + " (total)");
12002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
12102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
12202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    public void checkSource(String src, ComponentName componentName, String[] queries) {
12302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        ElapsedTime time = new ElapsedTime();
12402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        int count = queries.length;
12502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        for (int i = 0; i < queries.length; i++) {
12602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            long t = checkSource(src, componentName, queries[i]);
12702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            time.addTime(t);
12802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
12902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        Log.d(TAG, src + "[DONE]: " + count + " queries in " + formatTime(time.getAverage())
13002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                + " (average), " + formatTime(time.getTotal()) + " (total)");
13102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
13202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
13302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    public long checkSource(String src, ComponentName componentName, String query) {
13402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        SearchableInfo searchable = getSearchable(componentName);
13502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        return checkSourceInternal(src, searchable, query);
13602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
13702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
13802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private long checkSourceInternal(String src, SearchableInfo searchable, String query) {
13902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        Cursor cursor = null;
14002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        try {
14102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            final long start = System.nanoTime();
14202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            cursor = getSuggestions(searchable, query);
14302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            long end = System.nanoTime();
14402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            long elapsed = end - start;
14502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            if (cursor == null) {
14602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Log.d(TAG, src + ": null cursor in " + formatTime(elapsed)
14702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                        + " for '" + query + "'");
14802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            } else {
14902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Log.d(TAG, src + ": " + cursor.getCount() + " rows in " + formatTime(elapsed)
15002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                        + " for '" + query + "'");
15102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
15202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            return elapsed;
15302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        } finally {
15402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            if (cursor != null) {
15502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                cursor.close();
15602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
15702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
15802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
15902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
16002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    public Cursor getSuggestions(SearchableInfo searchable, String query) {
16102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        return getSuggestions(searchable, query, -1);
16202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
16302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
16402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    public Cursor getSuggestions(SearchableInfo searchable, String query, int limit) {
16502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        if (searchable == null) {
16602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            return null;
16702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
16802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
16902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        String authority = searchable.getSuggestAuthority();
17002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        if (authority == null) {
17102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            return null;
17202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
17302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
17402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        Uri.Builder uriBuilder = new Uri.Builder()
17502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                .scheme(ContentResolver.SCHEME_CONTENT)
17602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                .authority(authority)
17702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                .query("")  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
17802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                .fragment("");  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
17902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
18002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // if content path provided, insert it now
18102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        final String contentPath = searchable.getSuggestPath();
18202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        if (contentPath != null) {
18302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            uriBuilder.appendEncodedPath(contentPath);
18402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
18502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
18602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // append standard suggestion query path
18702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
18802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
18902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // get the query selection, may be null
19002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        String selection = searchable.getSuggestSelection();
19102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // inject query, either as selection args or inline
19202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        String[] selArgs = null;
19302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        if (selection != null) {    // use selection if provided
19402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            selArgs = new String[] { query };
19502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        } else {                    // no selection, use REST pattern
19602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            uriBuilder.appendPath(query);
19702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
19802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
19902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        if (limit > 0) {
20002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            uriBuilder.appendQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT,
20102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    String.valueOf(limit));
20202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
20302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
20402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        Uri uri = uriBuilder.build();
20502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
20602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        // finally, make the query
20702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        return getContentResolver().query(uri, null, selection, selArgs, null);
20802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
20902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
21002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private static String formatTime(long ns) {
21102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        return (ns / 1000000.0d) + " ms";
21202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
21302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
21402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    public void checkLiveSource(String src, ComponentName componentName, String query) {
21502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        mExecutorService.submit(new LiveSourceCheck(src, componentName, query));
21602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
21702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
21802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    private class LiveSourceCheck implements Runnable {
21902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
22002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private String mSrc;
22102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private SearchableInfo mSearchable;
22202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private String mQuery;
22302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private Handler mHandler = new Handler(Looper.getMainLooper());
22402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
22502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        public LiveSourceCheck(String src, ComponentName componentName, String query) {
22602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            mSrc = src;
22702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            mSearchable = mSearchManager.getSearchableInfo(componentName);
22802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            assert(mSearchable != null);
22902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            assert(mSearchable.getSuggestAuthority() != null);
23002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            mQuery = query;
23102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
23202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
23302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        public void run() {
23402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            Cursor cursor = null;
23502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            try {
23602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                final long start = System.nanoTime();
23702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                cursor = getSuggestions(mSearchable, mQuery);
23802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                long end = System.nanoTime();
23902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                long elapsed = (end - start);
24002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                if (cursor == null) {
24102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    Log.d(TAG, mSrc + ": null cursor in " + formatTime(elapsed)
24202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                            + " for '" + mQuery + "'");
24302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                } else {
24402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    Log.d(TAG, mSrc + ": " + cursor.getCount() + " rows in " + formatTime(elapsed)
24502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                            + " for '" + mQuery + "'");
24602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    cursor.registerContentObserver(new ChangeObserver(cursor));
24702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    cursor.registerDataSetObserver(new MyDataSetObserver(mSrc, start, cursor));
24802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    try {
24902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                        Thread.sleep(2000);
25002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    } catch (InterruptedException ex) {
25102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                        Log.d(TAG, mSrc + ": interrupted");
25202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    }
25302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                }
25402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            } finally {
25502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                if (cursor != null) {
25602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                    cursor.close();
25702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                }
25802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
25902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
26002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
26102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private class ChangeObserver extends ContentObserver {
26202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            private Cursor mCursor;
26302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
26402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            public ChangeObserver(Cursor cursor) {
26502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                super(mHandler);
26602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                mCursor = cursor;
26702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
26802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
26902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            @Override
27002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            public boolean deliverSelfNotifications() {
27102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                return true;
27202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
27302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
27402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            @Override
27502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            public void onChange(boolean selfChange) {
27602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                mCursor.requery();
27702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
27802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
27902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
28002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        private class MyDataSetObserver extends DataSetObserver {
28102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            private long mStart;
28202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            private Cursor mCursor;
28302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            private int mUpdateCount = 0;
28402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
28502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            public MyDataSetObserver(String src, long start, Cursor cursor) {
28602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                mSrc = src;
28702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                mStart = start;
28802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                mCursor = cursor;
28902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
29002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
29102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            @Override
29202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            public void onChanged() {
29302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                long end = System.nanoTime();
29402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                long elapsed = end - mStart;
29502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                mUpdateCount++;
29602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Log.d(TAG, mSrc + ", update " + mUpdateCount + ": " + mCursor.getCount()
29702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                        + " rows in " + formatTime(elapsed));
29802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
29902d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
30002d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            @Override
30102d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            public void onInvalidated() {
30202d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert                Log.d(TAG, mSrc + ": invalidated");
30302d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert            }
30402d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert        }
30502d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert    }
30602d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
30702d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert
30802d897ec26d81fa1f3cb9e1b8f05c6bcb5d7b9dbBjorn Bringert}
309