1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tv.search;
18
19import android.app.SearchManager;
20import android.content.ContentProvider;
21import android.content.ContentValues;
22import android.database.Cursor;
23import android.database.MatrixCursor;
24import android.net.Uri;
25import android.text.TextUtils;
26import android.util.Log;
27
28import com.android.tv.util.PermissionUtils;
29
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.List;
33
34public class LocalSearchProvider extends ContentProvider {
35    private static final boolean DEBUG = false;
36    private static final String TAG = "LocalSearchProvider";
37
38    public static final int PROGRESS_PERCENTAGE_HIDE = -1;
39
40    // TODO: Remove this once added to the SearchManager.
41    private static final String SUGGEST_COLUMN_PROGRESS_BAR_PERCENTAGE = "progress_bar_percentage";
42
43    private static final String[] SEARCHABLE_COLUMNS = new String[] {
44        SearchManager.SUGGEST_COLUMN_TEXT_1,
45        SearchManager.SUGGEST_COLUMN_TEXT_2,
46        SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE,
47        SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
48        SearchManager.SUGGEST_COLUMN_INTENT_DATA,
49        SearchManager.SUGGEST_COLUMN_CONTENT_TYPE,
50        SearchManager.SUGGEST_COLUMN_IS_LIVE,
51        SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH,
52        SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT,
53        SearchManager.SUGGEST_COLUMN_DURATION,
54        SUGGEST_COLUMN_PROGRESS_BAR_PERCENTAGE
55    };
56
57    private static final String EXPECTED_PATH_PREFIX = "/" + SearchManager.SUGGEST_URI_PATH_QUERY;
58    // The launcher passes 10 as a 'limit' parameter by default.
59    private static final int DEFAULT_SEARCH_LIMIT = 10;
60
61    private static final String NO_LIVE_CONTENTS = "0";
62    private static final String LIVE_CONTENTS = "1";
63
64    static final String SUGGEST_PARAMETER_ACTION = "action";
65    static final int DEFAULT_SEARCH_ACTION = SearchInterface.ACTION_TYPE_AMBIGUOUS;
66
67    @Override
68    public boolean onCreate() {
69        return true;
70    }
71
72    @Override
73    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
74            String sortOrder) {
75        if (DEBUG) {
76            Log.d(TAG, "query(" + uri + ", " + Arrays.toString(projection) + ", " + selection + ", "
77                    + Arrays.toString(selectionArgs) + ", " + sortOrder + ")");
78        }
79        SearchInterface search;
80        if (PermissionUtils.hasAccessAllEpg(getContext())) {
81            search = new TvProviderSearch(getContext());
82        } else {
83            search = new DataManagerSearch(getContext());
84        }
85        String query = uri.getLastPathSegment();
86        int limit = DEFAULT_SEARCH_LIMIT;
87        int action = DEFAULT_SEARCH_ACTION;
88        try {
89            limit = Integer.parseInt(uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT));
90            action = Integer.parseInt(uri.getQueryParameter(SUGGEST_PARAMETER_ACTION));
91        } catch (NumberFormatException | UnsupportedOperationException e) {
92            // Ignore the exceptions
93        }
94        List<SearchResult> results = new ArrayList<>();
95        if (!TextUtils.isEmpty(query)) {
96            results.addAll(search.search(query, limit, action));
97        }
98        return createSuggestionsCursor(results);
99    }
100
101    private Cursor createSuggestionsCursor(List<SearchResult> results) {
102        MatrixCursor cursor = new MatrixCursor(SEARCHABLE_COLUMNS, results.size());
103        List<String> row = new ArrayList<>(SEARCHABLE_COLUMNS.length);
104
105        for (SearchResult result : results) {
106            row.clear();
107            row.add(result.title);
108            row.add(result.description);
109            row.add(result.imageUri);
110            row.add(result.intentAction);
111            row.add(result.intentData);
112            row.add(result.contentType);
113            row.add(result.isLive ? LIVE_CONTENTS : NO_LIVE_CONTENTS);
114            row.add(result.videoWidth == 0 ? null : String.valueOf(result.videoWidth));
115            row.add(result.videoHeight == 0 ? null : String.valueOf(result.videoHeight));
116            row.add(result.duration == 0 ? null : String.valueOf(result.duration));
117            row.add(String.valueOf(result.progressPercentage));
118            cursor.addRow(row);
119        }
120        return cursor;
121    }
122
123    @Override
124    public String getType(Uri uri) {
125        if (!checkUriCorrect(uri)) return null;
126        return SearchManager.SUGGEST_MIME_TYPE;
127    }
128
129    private static boolean checkUriCorrect(Uri uri) {
130        return uri != null && uri.getPath().startsWith(EXPECTED_PATH_PREFIX);
131    }
132
133    @Override
134    public Uri insert(Uri uri, ContentValues values) {
135        throw new UnsupportedOperationException();
136    }
137
138    @Override
139    public int delete(Uri uri, String selection, String[] selectionArgs) {
140        throw new UnsupportedOperationException();
141    }
142
143    @Override
144    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
145        throw new UnsupportedOperationException();
146    }
147
148    /**
149     * A placeholder to a search result.
150     */
151    public static class SearchResult {
152        public long channelId;
153        public String channelNumber;
154        public String title;
155        public String description;
156        public String imageUri;
157        public String intentAction;
158        public String intentData;
159        public String contentType;
160        public boolean isLive;
161        public int videoWidth;
162        public int videoHeight;
163        public long duration;
164        public int progressPercentage;
165
166        @Override
167        public String toString() {
168            return "channelId: " + channelId +
169                    ", channelNumber: " + channelNumber +
170                    ", title: " + title;
171        }
172    }
173}