Merge tag 'oc-android-1.9' into sdcard-save
[pub/Android/ownCloud.git] / src / com / owncloud / android / providers / UsersAndGroupsSearchProvider.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author David A. Velasco
5 * Copyright (C) 2015 ownCloud Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2,
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21
22 package com.owncloud.android.providers;
23
24 import android.accounts.Account;
25 import android.app.SearchManager;
26 import android.content.ContentProvider;
27 import android.content.ContentValues;
28 import android.content.UriMatcher;
29 import android.database.Cursor;
30 import android.database.MatrixCursor;
31 import android.net.Uri;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.provider.BaseColumns;
35 import android.support.annotation.Nullable;
36 import android.widget.Toast;
37
38 import com.owncloud.android.R;
39 import com.owncloud.android.authentication.AccountUtils;
40 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
41 import com.owncloud.android.lib.common.utils.Log_OC;
42 import com.owncloud.android.lib.resources.shares.GetRemoteShareesOperation;
43 import com.owncloud.android.utils.ErrorMessageAdapter;
44
45 import org.json.JSONException;
46 import org.json.JSONObject;
47
48 import java.util.ArrayList;
49 import java.util.Iterator;
50 import java.util.List;
51
52
53 /**
54 * Content provider for search suggestions, to search for users and groups existing in an ownCloud server.
55 */
56 public class UsersAndGroupsSearchProvider extends ContentProvider {
57
58 private static final String TAG = UsersAndGroupsSearchProvider.class.getSimpleName();
59
60 private static final String[] COLUMNS = {
61 BaseColumns._ID,
62 SearchManager.SUGGEST_COLUMN_TEXT_1,
63 SearchManager.SUGGEST_COLUMN_INTENT_DATA
64 };
65
66 private static final int SEARCH = 1;
67
68 private static final int RESULTS_PER_PAGE = 50;
69 private static final int REQUESTED_PAGE = 1;
70
71 public static final String AUTHORITY = UsersAndGroupsSearchProvider.class.getCanonicalName();
72 public static final String ACTION_SHARE_WITH = AUTHORITY + ".action.SHARE_WITH";
73 public static final String DATA_USER = AUTHORITY + ".data.user";
74 public static final String DATA_GROUP = AUTHORITY + ".data.group";
75
76 private UriMatcher mUriMatcher;
77
78 @Nullable
79 @Override
80 public String getType(Uri uri) {
81 // TODO implement
82 return null;
83 }
84
85 @Override
86 public boolean onCreate() {
87 mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
88 mUriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH);
89 return true;
90 }
91
92 /**
93 * TODO description
94 *
95 * Reference: http://developer.android.com/guide/topics/search/adding-custom-suggestions.html#CustomContentProvider
96 *
97 * @param uri Content {@link Uri}, formattted as
98 * "content://com.owncloud.android.providers.UsersAndGroupsSearchProvider/" +
99 * {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY} + "/" + 'userQuery'
100 * @param projection Expected to be NULL.
101 * @param selection Expected to be NULL.
102 * @param selectionArgs Expected to be NULL.
103 * @param sortOrder Expected to be NULL.
104 * @return Cursor with users and groups in the ownCloud server that match 'userQuery'.
105 */
106 @Nullable
107 @Override
108 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
109 Log_OC.d(TAG, "query received in thread " + Thread.currentThread().getName());
110
111 int match = mUriMatcher.match(uri);
112 switch (match) {
113 case SEARCH:
114 return searchForUsersOrGroups(uri);
115
116 default:
117 return null;
118 }
119 }
120
121 private Cursor searchForUsersOrGroups(Uri uri) {
122 MatrixCursor response = null;
123
124
125 String userQuery = uri.getLastPathSegment().toLowerCase();
126
127
128 /// need to trust on the AccountUtils to get the current account since the query in the client side is not
129 /// directly started by our code, but from SearchView implementation
130 Account account = AccountUtils.getCurrentOwnCloudAccount(getContext());
131
132 /// request to the OC server about users and groups matching userQuery
133 GetRemoteShareesOperation searchRequest = new GetRemoteShareesOperation(
134 userQuery, REQUESTED_PAGE, RESULTS_PER_PAGE
135 );
136 RemoteOperationResult result = searchRequest.execute(account, getContext());
137 List<JSONObject> names = new ArrayList<JSONObject>();
138 if (result.isSuccess()) {
139 for (Object o : result.getData()) {
140 // Get JSonObjects from response
141 names.add((JSONObject) o);
142 }
143 } else {
144 showErrorMessage(result);
145 }
146
147 /// convert the responses from the OC server to the expected format
148 if (names.size() > 0) {
149 response = new MatrixCursor(COLUMNS);
150 Iterator<JSONObject> namesIt = names.iterator();
151 int count = 0;
152 JSONObject item;
153 String displayName;
154 Uri dataUri;
155 Uri userBaseUri = new Uri.Builder().scheme("content").authority(DATA_USER).build();
156 Uri groupBaseUri = new Uri.Builder().scheme("content").authority(DATA_GROUP).build();
157 try {
158 while (namesIt.hasNext()) {
159 item = namesIt.next();
160 String userName = item.getString(GetRemoteShareesOperation.PROPERTY_LABEL);
161 JSONObject value = item.getJSONObject(GetRemoteShareesOperation.NODE_VALUE);
162 byte type = (byte) value.getInt(GetRemoteShareesOperation.PROPERTY_SHARE_TYPE);
163 String shareWith = value.getString(GetRemoteShareesOperation.PROPERTY_SHARE_WITH);
164 if (GetRemoteShareesOperation.GROUP_TYPE.equals(type)) {
165 displayName = getContext().getString(R.string.share_group_clarification, userName);
166 dataUri = Uri.withAppendedPath(groupBaseUri, shareWith);
167 } else {
168 displayName = userName;
169 dataUri = Uri.withAppendedPath(userBaseUri, shareWith);
170 }
171 response.newRow()
172 .add(count++) // BaseColumns._ID
173 .add(displayName) // SearchManager.SUGGEST_COLUMN_TEXT_1
174 .add(dataUri);
175 }
176 } catch (JSONException e) {
177 Log_OC.e(TAG, "Exception while parsing data of users/groups", e);
178 }
179 }
180
181 return response;
182 }
183
184 @Nullable
185 @Override
186 public Uri insert(Uri uri, ContentValues values) {
187 // TODO implementation
188 return null;
189 }
190
191 @Override
192 public int delete(Uri uri, String selection, String[] selectionArgs) {
193 // TODO implementation
194 return 0;
195 }
196
197 @Override
198 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
199 // TODO implementation
200 return 0;
201 }
202
203 /**
204 * Show error message
205 *
206 * @param result Result with the failure information.
207 */
208 public void showErrorMessage(final RemoteOperationResult result) {
209 Handler handler = new Handler(Looper.getMainLooper());
210 handler.post(new Runnable() {
211 @Override
212 public void run() {
213 // The Toast must be shown in the main thread to grant that will be hidden correctly; otherwise
214 // the thread may die before, an exception will occur, and the message will be left on the screen
215 // until the app dies
216 Toast.makeText(
217 getContext().getApplicationContext(),
218 ErrorMessageAdapter.getErrorCauseMessage(
219 result,
220 null,
221 getContext().getResources()
222 ),
223 Toast.LENGTH_SHORT
224 ).show();
225 }
226 });
227 }
228
229 }