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 
17 package com.android.tv.search;
18 
19 import android.app.SearchManager;
20 import android.content.ContentProvider;
21 import android.content.ContentValues;
22 import android.database.Cursor;
23 import android.database.MatrixCursor;
24 import android.net.Uri;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import com.android.tv.util.PermissionUtils;
29 
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 
34 public 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
onCreate()68     public boolean onCreate() {
69         return true;
70     }
71 
72     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)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 
createSuggestionsCursor(List<SearchResult> results)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
getType(Uri uri)124     public String getType(Uri uri) {
125         if (!checkUriCorrect(uri)) return null;
126         return SearchManager.SUGGEST_MIME_TYPE;
127     }
128 
checkUriCorrect(Uri uri)129     private static boolean checkUriCorrect(Uri uri) {
130         return uri != null && uri.getPath().startsWith(EXPECTED_PATH_PREFIX);
131     }
132 
133     @Override
insert(Uri uri, ContentValues values)134     public Uri insert(Uri uri, ContentValues values) {
135         throw new UnsupportedOperationException();
136     }
137 
138     @Override
delete(Uri uri, String selection, String[] selectionArgs)139     public int delete(Uri uri, String selection, String[] selectionArgs) {
140         throw new UnsupportedOperationException();
141     }
142 
143     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)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
toString()167         public String toString() {
168             return "channelId: " + channelId +
169                     ", channelNumber: " + channelNumber +
170                     ", title: " + title;
171         }
172     }
173 }