0

I'm implementing my own SearchRecentSuggestionsProvider and everything's working except one thing: I can't get the device search to display icons for the results. I'd like to display images from my application's data folder (located at /{sdcard}/Android/data/package_name/files/)

According to the documentation, it's achievable by using SearchManager.SUGGEST_COLUMN_ICON_1, and it apparently supports a number of schemes, including ContentResolver.SCHEME_FILE, which is file. Here's a quote from the official docs:

Column name for suggestions cursor. Optional. If your cursor includes this column, then all suggestions will be provided in a format that includes space for two small icons, one at the left and one at the right of each suggestion. The data in the column must be a resource ID of a drawable, or a URI in one of the following formats:

content (SCHEME_CONTENT)

android.resource (SCHEME_ANDROID_RESOURCE)

file (SCHEME_FILE)

I've tried a number of obvious things, including manual creation of the file URI and automated creation using Uri.Builder(). None of this worked.

I also found someone else asking about the same thing on Google Groups, and it's sadly unanswered: https://groups.google.com/forum/#!topic/android-developers/MJj7GIaONjc

Does anyone have any experience in getting the device search to display local images from the device?

UPDATE (December 15): I've just tried using the ContentProvider with a SearchView as the searchable info, and it works exactly as expected - including the cover art images. Still, global search doesn't show it...

Michell Bak
  • 13,182
  • 11
  • 64
  • 121
  • What file URI structure have you tried? – ianhanniballake Nov 05 '13 at 22:39
  • I've tried "file://{file_path}", "file:/{file_path}" as well as {file_path}. – Michell Bak Nov 05 '13 at 22:53
  • I wonder if perhaps you'll have to implement your own `ContentProvider` to expose the images in your app's `data` folder. Have you tried that? Could be worth a shot, especially since it's not too complicated. Alternatively, have you tried loading an image through the `file://` scheme from a more public location? For example, the root of the SD card? – MH. Nov 15 '13 at 02:58
  • I'd love to try, but I've just updated all my devices to Android 4.4 and for some reason that means I can no longer search the content of installed applications. Everything that worked before, including Google Play, no longer appear in the "Tablet search" / "Phone search" part of Google search. Strange... – Michell Bak Nov 15 '13 at 13:03
  • Just created a question about that particular issue as well: http://stackoverflow.com/questions/20001979/global-search-not-working-as-expected-in-android-4-4 – Michell Bak Nov 15 '13 at 14:23
  • I just tried using `file://` and `file:/` for a public file (screenshot folder), and that still didn't work. Might have to create a `ContentProvider` for it to work - it's just weird when the documentation says it should work. – Michell Bak Nov 15 '13 at 15:33
  • The following works for me: `"'" + uri + "' AS " + SearchManager.SUGGEST_COLUMN_ICON_1` where `uri = Uri.fromFile(new File("/path/to/file"))`. Note that `uri` needs to be within single quotes. – Vikram Nov 16 '13 at 02:52
  • Thanks, I'll try it out as soon as I can. Can't get global search to work on Android 4.4, so I'll need to use a friend's phone. – Michell Bak Nov 16 '13 at 03:46
  • @user2558882 Just tried it and I still can't get it to work. Can you perhaps add the solution as an answer with slightly more code? – Michell Bak Nov 16 '13 at 16:12
  • Sure. Do you see an exception (a silent one perhaps)? Or, it just doesn't work? – Vikram Nov 16 '13 at 16:15
  • Didn't notice any exceptions, but admittedly I wasn't looking for any in the Logcat output. It simply displays the app icon where it was supposed to display the images. – Michell Bak Nov 16 '13 at 16:27

2 Answers2

3

I had a similar issue and could not make the other solutions work. I finally substituted the cursor with a new one containing the data I needed in query().

public class RecentQueriesProvider extends SearchRecentSuggestionsProvider {
    public final static String AUTHORITY = "com.package.RecentQueriesProvider";
    public final static int MODE = DATABASE_MODE_QUERIES;

    public RecentQueriesProvider() {
        setupSuggestions(AUTHORITY, MODE);
    }

    // We override query() to replace the history icon in the recent searches suggestions. We create a new cursor
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor superCursor = super.query(uri, projection, selection, selectionArgs, sortOrder);
        Uri iconUri = Uri.parse("android.resource://" + getContext().getPackageName() + "/drawable/ic_action_time");
        MatrixCursor newCursor = new MatrixCursor(superCursor.getColumnNames());
        superCursor.moveToFirst();
        while (superCursor.moveToNext()){
            newCursor.addRow(new Object[]{
                    superCursor.getInt(superCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT)),
                    iconUri,
                    superCursor.getString(superCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1)),
                    superCursor.getString(superCursor.getColumnIndex("suggest_intent_query")),
                    superCursor.getInt(superCursor.getColumnIndex("_id"))
            });
        }
       return newCursor;
    }
}
znat
  • 13,144
  • 17
  • 71
  • 106
2

One way is to copy source code from android.content.SearchRecentSuggestionsProvider, place it in your class that extends ContentProvider, and customize setupSuggestions(String, int). Specifically, you would be changing this:

Uri uriFile = Uri.fromFile(new File("path/to/file"));

mSuggestionProjection = new String [] {
        "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT,

        // Here, you would return a file uri: 'uriFile'
        "'android.resource://system/"
                + com.android.internal.R.drawable.ic_menu_recent_history + "' AS "
                        + SearchManager.SUGGEST_COLUMN_ICON_1,
        "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
        "query AS " + SearchManager.SUGGEST_COLUMN_QUERY,
        "_id"
};

I prefer the following though. Extend SearchRecentSuggestionsProvider and override the query(...) method. Here, intercept SearchManager.SUGGEST_URI_PATH_QUERY and return a cursor.

public class SearchSuggestionProvider extends SearchRecentSuggestionsProvider {

    private UriMatcher matcher;

    private static final int URI_MATCH_SUGGEST = 1;

    public SearchSuggestionProvider() {
        super();

        matcher = new UriMatcher(UriMatcher.NO_MATCH);

        // Add uri to return URI_MATCH_SUGGEST
        matcher.addURI(SearchSuggestionProvider.class.getName(), 
                         SearchManager.SUGGEST_URI_PATH_QUERY, URI_MATCH_SUGGEST);

        setupSuggestions(SearchSuggestionProvider.class.getName(), 
                                                           DATABASE_MODE_QUERIES);
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, 
                                      String[] selectionArgs, String sortOrder) {

        // special case for actual suggestions (from search manager)
        if (matcher.match(uri) == URI_MATCH_SUGGEST) {

            // File to use
            File f = new File("/path/to/file");

            Uri uriFile = Uri.fromFile(f);

            final String[] PROJECTION = new String[] {
                "_id", 
                "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,                
                "query AS " + SearchManager.SUGGEST_COLUMN_QUERY,
                "'" + uriFile + "'" + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
            };

            final Uri URI = Uri.parse("content://" + 
                        SearchSuggestionProvider.class.getName() + "/suggestions");

            // return cursor
            return getContext().getContentResolver().query(
                URI, 
                PROJECTION, 
                "display1 LIKE ?", 
                new String[] {selectionArgs[0] + "%"}, 
                "date DESC");          
        }

        // Let super method handle the query if the check fails
        return super.query(uri, projection, selection, selectionArgs, sortOrder);
    }
}
Vikram
  • 51,313
  • 11
  • 93
  • 122
  • I might have been a bit vague in my original question. If that's the case, I'm sorry. Here's a screenshot of what I've currently achieved: http://mizuu.tv/wp-content/uploads/2013/11/miz_global_search.jpg – Michell Bak Nov 16 '13 at 19:21
  • The results come from a SQLite database that I query in the overridden `query(...)` method. I'd like each result to have a cover image shown alongside it. This solution seems to be useful for showing just one image, if I'm not mistaking. – Michell Bak Nov 16 '13 at 19:22
  • @MichellBak First of all, the app looks awesome, nice work! I (kind of) understand what you are trying to do. And I'll be honest, I don't think I know the answer to this. What _might_ work: add another column to your table to store the image `Uri`. Add another item to the `PROJECTION` array: `"iconuri AS " + SearchManager.SUGGEST_COLUMN_ICON_1`. – Vikram Nov 16 '13 at 21:28
  • Thanks a lot! I'll give it a try when possible. It doesn't seem like there are any other suggestions from people - apart from creating a contentprovider and see if that works - so I'll award you with the bounty, since it's about to expire, and leave the question unanswered for now. – Michell Bak Nov 16 '13 at 22:05
  • @MichellBak No, thank you for the bounty, even though I wasn't much help. I'll give this a go again coming weekend and post back if I do find a way. – Vikram Nov 18 '13 at 14:05
  • Thanks, I appreciate it! :-) It's an annoying issue, especially when you can't test it yourself because all your devices run 4.4 :-( – Michell Bak Nov 18 '13 at 14:43