瀏覽代碼

Users and groups retrieved from server to fill search suggestions

David A. Velasco 9 年之前
父節點
當前提交
305536f8c2

+ 8 - 1
AndroidManifest.xml

@@ -118,6 +118,13 @@
             android:label="@string/sync_string_files"
             android:syncable="true" />
 
+        <provider
+            android:name=".providers.UsersAndGroupsSearchProvider"
+            android:authorities="com.owncloud.android.providers.UsersAndGroupsSearchProvider"
+            android:enabled="true"
+            android:exported="false"
+            android:label="@string/search_users_and_groups_hint" />
+
         <activity
             android:name=".authentication.AuthenticatorActivity"
             android:exported="true"
@@ -195,7 +202,7 @@
                 <action android:name="android.intent.action.SEARCH" />
             </intent-filter>
             <meta-data android:name="android.app.searchable"
-                       android:resource="@xml/searchable"/>
+                       android:resource="@xml/users_and_groups_searchable"/>
         </activity>
     </application>
 

+ 1 - 0
res/values/strings.xml

@@ -375,5 +375,6 @@
     <string name="share_group_indicator">(group)</string>
 
     <string name="search_users_and_groups_hint">Search users and groups</string>
+    <string name="share_group_clarification">%1$s (group)</string>
 
 </resources>

+ 4 - 1
res/xml/searchable.xml → res/xml/users_and_groups_searchable.xml

@@ -21,5 +21,8 @@
 <!-- Searchable configuration to search users & groups in an OC server -->
 <searchable xmlns:android="http://schemas.android.com/apk/res/android"
     android:label="@string/app_name"
-    android:hint="@string/search_users_and_groups_hint" >
+    android:hint="@string/search_users_and_groups_hint"
+    android:searchSuggestAuthority="com.owncloud.android.providers.UsersAndGroupsSearchProvider"
+    android:searchSuggestIntentAction="com.owncloud.android.providers.UsersAndGroupsSearchProvider.action.SHARE_WITH"
+    android:searchSuggestThreshold="1" >
 </searchable>

+ 1 - 1
src/com/owncloud/android/operations/GetUsersOrGroupsOperation.java

@@ -56,7 +56,7 @@ public class GetUsersOrGroupsOperation extends SyncOperation{
     protected RemoteOperationResult run(OwnCloudClient client) {
         GetRemoteShareesOperation operation =
                 new GetRemoteShareesOperation(mSearchString,
-                mLimit, mOffset);
+                mOffset, mLimit);
         RemoteOperationResult result = operation.execute(client);
 
         return result;

+ 189 - 0
src/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java

@@ -0,0 +1,189 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author David A. Velasco
+ *   Copyright (C) 2015 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+package com.owncloud.android.providers;
+
+import android.accounts.Account;
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.support.annotation.Nullable;
+import android.util.Pair;
+
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.shares.GetRemoteShareesOperation;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Content provider for search suggestions, to search for users and groups existing in an ownCloud server.
+ */
+public class UsersAndGroupsSearchProvider extends ContentProvider {
+
+    private static final String TAG = UsersAndGroupsSearchProvider.class.getSimpleName();
+
+    private static final String[] COLUMNS = {
+        BaseColumns._ID,
+        SearchManager.SUGGEST_COLUMN_TEXT_1,
+        SearchManager.SUGGEST_COLUMN_FLAGS,
+        SearchManager.SUGGEST_COLUMN_INTENT_DATA
+    };
+
+    private static final int SEARCH = 1;
+
+    private static final int RESULTS_PER_PAGE = 50;
+    private static final int REQUESTED_PAGE = 1;
+
+    public static final String AUTHORITY = UsersAndGroupsSearchProvider.class.getCanonicalName();
+    public static final String ACTION_SHARE_WITH = AUTHORITY + ".action.SHARE_WITH";
+    public static final String DATA_USER = AUTHORITY + ".data.user";
+    public static final String DATA_GROUP = AUTHORITY + ".data.group";
+
+    private UriMatcher mUriMatcher;
+
+    @Nullable
+    @Override
+    public String getType(Uri uri) {
+        // TODO implement
+        return null;
+    }
+
+    @Override
+    public boolean onCreate() {
+        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+        mUriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH);
+        return true;
+    }
+
+    /**
+     * TODO description
+     *
+     * Reference: http://developer.android.com/guide/topics/search/adding-custom-suggestions.html#CustomContentProvider
+     *
+     * @param uri           Content {@link Uri}, formattted as
+     *                      "content://com.owncloud.android.providers.UsersAndGroupsSearchProvider/" +
+     *                      {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY} + "/" + 'userQuery'
+     * @param projection    Expected to be NULL.
+     * @param selection     Expected to be NULL.
+     * @param selectionArgs Expected to be NULL.
+     * @param sortOrder     Expected to be NULL.
+     * @return              Cursor with users and groups in the ownCloud server that match 'userQuery'.
+     */
+    @Nullable
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+        Log_OC.d(TAG, "query received in thread " + Thread.currentThread().getName());
+
+        int match = mUriMatcher.match(uri);
+        switch (match) {
+            case SEARCH:
+                return searchForUsersOrGroups(uri);
+
+            default:
+                return null;
+        }
+    }
+
+    private Cursor searchForUsersOrGroups(Uri uri) {
+        MatrixCursor response = null;
+
+
+        String userQuery = uri.getLastPathSegment().toLowerCase();
+
+
+        /// need to trust on the AccountUtils to get the current account since the query in the client side is not
+        /// directly started by our code, but from SearchView implementation
+        Account account = AccountUtils.getCurrentOwnCloudAccount(getContext());
+
+        /// request to the OC server about users and groups matching userQuery
+        GetRemoteShareesOperation searchRequest = new GetRemoteShareesOperation(
+                userQuery, REQUESTED_PAGE, RESULTS_PER_PAGE
+        );
+        RemoteOperationResult result = searchRequest.execute(account, getContext());
+        List<Pair<String, Byte>> names = new ArrayList<Pair<String, Byte>>();
+        if (result.isSuccess()) {
+            for (Object o : result.getData()) {
+                names.add((Pair<String, Byte>) o);
+            }
+        }
+
+        /// convert the responses from the OC server to the expected format
+        if (names.size() > 0) {
+            response = new MatrixCursor(COLUMNS);
+            Iterator<Pair<String, Byte>> namesIt = names.iterator();
+            int count = 0;
+            Pair<String, Byte> item;
+            String displayName;
+            Uri dataUri;
+            Uri userBaseUri = new Uri.Builder().scheme("content").authority(DATA_USER).build();
+            Uri groupBaseUri = new Uri.Builder().scheme("content").authority(DATA_GROUP).build();
+
+            while (namesIt.hasNext()) {
+                item = namesIt.next();
+                if (GetRemoteShareesOperation.GROUP_TYPE.equals(item.second)) {
+                    displayName = getContext().getString(R.string.share_group_clarification, item.first);
+                    dataUri = Uri.withAppendedPath(groupBaseUri, item.first);
+                } else {
+                    displayName = item.first;
+                    dataUri = Uri.withAppendedPath(userBaseUri, item.first);
+                }
+                response.newRow()
+                    .add(count++)             // BaseColumns._ID
+                    .add(displayName)        // SearchManager.SUGGEST_COLUMN_TEXT_1
+                    .add(SearchManager.FLAG_QUERY_REFINEMENT)
+                    .add(dataUri);
+            }
+        }
+
+        return response;
+    }
+
+    @Nullable
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        // TODO implementation
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // TODO implementation
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        // TODO implementation
+        return 0;
+    }
+
+}

+ 25 - 1
src/com/owncloud/android/ui/activity/ShareActivity.java

@@ -34,6 +34,8 @@ import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.fragment.SearchFragment;
 import com.owncloud.android.ui.fragment.ShareFileFragment;
@@ -46,6 +48,8 @@ public class ShareActivity extends AppCompatActivity
         implements ShareFileFragment.OnShareFragmentInteractionListener,
         SearchFragment.OnSearchFragmentInteractionListener {
 
+    private static final String TAG = ShareActivity.class.getSimpleName();
+
     private static final String TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT";
     private static final String TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT";
 
@@ -113,14 +117,34 @@ public class ShareActivity extends AppCompatActivity
         if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
             String query = intent.getStringExtra(SearchManager.QUERY);
             doMySearch(query);
+
+        } else if (UsersAndGroupsSearchProvider.ACTION_SHARE_WITH.equals(intent.getAction())) {
+            Uri data = intent.getData();
+            doShareWith(
+                    data.getLastPathSegment(),
+                    UsersAndGroupsSearchProvider.DATA_GROUP.equals(data.getAuthority())
+            );
+
+        } else {
+            Log_OC.wtf(TAG, "Unexpected intent " + intent.toString());
         }
     }
 
     private void doMySearch(String query) {
-        // TODO implement
+        // TODO implement , or prevent that search may be sent without choosing from the suggestions list
         Toast.makeText(this, "You want to search for [" + query + "]", Toast.LENGTH_SHORT).show();
     }
 
+    private void doShareWith(String username, boolean isGroup) {
+        // TODO implement
+        if (isGroup) {
+            Toast.makeText(this, "You want to SHARE with GROUP [" + username + "]", Toast.LENGTH_SHORT).show();
+
+        } else {
+            Toast.makeText(this, "You want to SHARE with USER [" + username + "]", Toast.LENGTH_SHORT).show();
+        }
+    }
+
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);