From 8fa4c5bcc4c49ac4f29c7dfda1b89aea8f33a105 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Thu, 27 Dec 2018 09:36:14 +0100 Subject: [PATCH] Can search groups --- app/src/main/AndroidManifest.xml | 11 +- .../client/data/enums/KindSearchResult.java | 12 ++ .../client/data/helpers/SearchHelper.java | 182 ++++++++++++++++++ .../client/data/models/APIRequest.java | 22 ++- .../client/data/models/SearchResult.java | 46 +++++ .../data/models/SearchResultWithInfo.java | 28 +++ .../android/comunic/client/ui/Constants.java | 26 ++- .../client/ui/activities/BaseActivity.java | 34 ++++ .../client/ui/activities/MainActivity.java | 49 ++++- .../client/ui/activities/SearchActivity.java | 178 +++++++++++++++++ .../ui/activities/SearchUserActivity.java | 3 +- .../client/ui/adapters/GroupsListAdapter.java | 51 +---- .../ui/adapters/SearchResultsAdapter.java | 112 +++++++++++ .../client/ui/adapters/UsersBasicAdapter.java | 1 + .../ui/asynctasks/GlobalSearchTask.java | 32 +++ .../ui/listeners/OnOpenPageListener.java | 9 + .../ui/viewholders/GroupViewHolder.java | 73 +++++++ .../client/ui/viewholders/UserViewHolder.java | 56 ++++++ app/src/main/res/layout/activity_search.xml | 37 ++++ app/src/main/res/layout/viewholder_user.xml | 26 +++ app/src/main/res/menu/main_menu.xml | 4 +- app/src/main/res/values-fr/strings.xml | 7 + app/src/main/res/values/strings.xml | 4 + 23 files changed, 935 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/data/enums/KindSearchResult.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/data/helpers/SearchHelper.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResult.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResultWithInfo.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchActivity.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/SearchResultsAdapter.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/GlobalSearchTask.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnOpenPageListener.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/GroupViewHolder.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/UserViewHolder.java create mode 100644 app/src/main/res/layout/activity_search.xml create mode 100644 app/src/main/res/layout/viewholder_user.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ed1e13d..5120c6c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,6 +39,11 @@ android:name=".ui.activities.SearchUserActivity" android:label="@string/activity_searchuser_title" /> + + + - + - \ No newline at end of file diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/enums/KindSearchResult.java b/app/src/main/java/org/communiquons/android/comunic/client/data/enums/KindSearchResult.java new file mode 100644 index 0000000..c048889 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/enums/KindSearchResult.java @@ -0,0 +1,12 @@ +package org.communiquons.android.comunic.client.data.enums; + +/** + * Different kinds of search result + * + * @author Pierre HUBERT + */ +public enum KindSearchResult { + USER, + GROUP, + UNKNOWN +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/SearchHelper.java b/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/SearchHelper.java new file mode 100644 index 0000000..a76efd1 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/SearchHelper.java @@ -0,0 +1,182 @@ +package org.communiquons.android.comunic.client.data.helpers; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.ArrayMap; + +import org.communiquons.android.comunic.client.data.enums.KindSearchResult; +import org.communiquons.android.comunic.client.data.models.APIRequest; +import org.communiquons.android.comunic.client.data.models.APIResponse; +import org.communiquons.android.comunic.client.data.models.GroupInfo; +import org.communiquons.android.comunic.client.data.models.SearchResult; +import org.communiquons.android.comunic.client.data.models.SearchResultWithInfo; +import org.communiquons.android.comunic.client.data.models.UserInfo; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + +/** + * Search helper + * + * This helper is used to search users and groups + * + * @author Pierre HUBERT + */ +public class SearchHelper extends BaseHelper { + + public SearchHelper(Context context) { + super(context); + } + + /** + * Perform a global search in the database + * + * @param query The query to search for + * @return The list of results / null in case of failure + */ + @Nullable + public ArrayList global(String query) { + + //Prepare request + APIRequest request = new APIRequest(getContext(), "search/global"); + request.addString("query", query); + + //Execute request + try { + APIResponse response = request.exec(); + + if(response.getResponse_code() != 200) + return null; + + //Get JSON data + JSONArray data = response.getJSONArray(); + if(data == null) + return null; + + //Parse and return results + ArrayList results = new ArrayList<>(); + for(int i = 0; i < data.length(); i++) + results.add(JSONObjectToSearchResult(data.getJSONObject(i))); + return results; + + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + } + + + /** + * Fill search result with information for users, groups... + * + * @param list The list of results to process + * @return Information for groups, users / null in case of failure + */ + @Nullable + public ArrayList fillResults(@NonNull ArrayList list){ + + ArrayList listWithInfo = new ArrayList<>(); + + //Extract the ids of the users and the groups to get + ArrayList usersID = new ArrayList<>(); + ArrayList groupsID = new ArrayList<>(); + + for(SearchResult result : list){ + if(result.getKind() == KindSearchResult.USER) + usersID.add(result.getId()); + + else if(result.getKind() == KindSearchResult.GROUP) + groupsID.add(result.getId()); + + SearchResultWithInfo resultWithInfo = new SearchResultWithInfo(); + result.copyTo(resultWithInfo); + listWithInfo.add(resultWithInfo); + } + + //Get information about users (if any) + if(usersID.size() > 0){ + + ArrayMap usersInfo = new GetUsersHelper(getContext()) + .getMultiple(usersID); + + if(usersInfo == null) + return null; + + //Process the list of result to apply information + for(SearchResultWithInfo result : listWithInfo){ + + //Skip non related entries + if(result.getKind() != KindSearchResult.USER) + continue; + + result.setUserInfo(usersInfo.get(result.getId())); + } + + } + + //Get information about groups (if any) + if(groupsID.size() > 0){ + + ArrayMap groupsInfo = new GroupsHelper(getContext()) + .getInfoMultiple(groupsID); + + if(groupsInfo == null) + return null; + + //Process the list of result to apply information + for(SearchResultWithInfo result : listWithInfo){ + + //Skip non related entries + if(result.getKind() != KindSearchResult.GROUP) + continue; + + result.setGroupInfo(groupsInfo.get(result.getId())); + } + + } + + + return listWithInfo; + } + + /** + * Turn a string into a search kind result + * + * @param string The string to convert + * @return Matching search result + */ + private static KindSearchResult StringToKindSearchResult(String string){ + switch (string){ + + case "user": + return KindSearchResult.USER; + + case "group": + return KindSearchResult.GROUP; + + default: + return KindSearchResult.UNKNOWN; + } + } + + + /** + * Turn a JSONObject into a SearchResult object + * + * @param object The object to convert + * @return Generated SearchResult object + * @throws JSONException This exception is thrown in case of failure + */ + private static SearchResult JSONObjectToSearchResult(JSONObject object) throws JSONException { + SearchResult result = new SearchResult(); + + result.setId(object.getInt("id")); + result.setKind(StringToKindSearchResult(object.getString("kind"))); + + return result; + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/models/APIRequest.java b/app/src/main/java/org/communiquons/android/comunic/client/data/models/APIRequest.java index 672e599..08104be 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/data/models/APIRequest.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/models/APIRequest.java @@ -2,6 +2,8 @@ package org.communiquons.android.comunic.client.data.models; import android.content.Context; +import org.communiquons.android.comunic.client.data.helpers.APIRequestHelper; + import java.util.ArrayList; /** @@ -99,17 +101,17 @@ public class APIRequest { public String get_parameters_encoded(){ //Return string - String result = ""; + StringBuilder result = new StringBuilder(); //Process loop for(int i = 0; i < parameters.size(); i++){ //Make sure to separate parameters - result += (i > 0 ? "&" : ""); - result += parameters.get(i).get_encoded(); + result.append(i > 0 ? "&" : ""); + result.append(parameters.get(i).get_encoded()); } - return result; + return result.toString(); } /** @@ -147,4 +149,16 @@ public class APIRequest { public boolean isTryContinueOnError() { return tryContinueOnError; } + + /** + * Execute the request + * + * This is a convenience method + * + * @return The result of the operation + * @throws Exception In case of failure + */ + public APIResponse exec() throws Exception { + return new APIRequestHelper().exec(this); + } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResult.java b/app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResult.java new file mode 100644 index 0000000..9e7fbac --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResult.java @@ -0,0 +1,46 @@ +package org.communiquons.android.comunic.client.data.models; + +import org.communiquons.android.comunic.client.data.enums.KindSearchResult; + +/** + * Single search result + * + * @author Pierre HUBERT + */ +public class SearchResult { + + //Private fields + private int id; + private KindSearchResult kind; + + /** + * Base constructor + */ + public SearchResult(){} + + /** + * Copy the values of the current object to a new object + * + * @param dst Destination object + */ + public void copyTo(SearchResult dst){ + dst.setId(getId()); + dst.setKind(getKind()); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public KindSearchResult getKind() { + return kind; + } + + public void setKind(KindSearchResult kind) { + this.kind = kind; + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResultWithInfo.java b/app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResultWithInfo.java new file mode 100644 index 0000000..6918226 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/models/SearchResultWithInfo.java @@ -0,0 +1,28 @@ +package org.communiquons.android.comunic.client.data.models; + +/** + * This object handles a search result and all the information associated with it + * + * @author Pierre HUBERT + */ +public class SearchResultWithInfo extends SearchResult { + + private GroupInfo groupInfo; + private UserInfo userInfo; + + public GroupInfo getGroupInfo() { + return groupInfo; + } + + public void setGroupInfo(GroupInfo groupInfo) { + this.groupInfo = groupInfo; + } + + public UserInfo getUserInfo() { + return userInfo; + } + + public void setUserInfo(UserInfo userInfo) { + this.userInfo = userInfo; + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java index 9f09fde..80f9a1e 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java @@ -28,14 +28,36 @@ public final class Constants { public static final int POST_CREATE_FORM_PICK_PHOTO = 2; /** - * Intent code : search a user + * Main Activity : search a user */ public static final int MAIN_ACTIVITY_SEARCH_USER_INTENT = 3; + /** + * Main Activity : make a global search + */ + public static final int MAIN_ACTIVITY_GLOBAL_SEARCH_INTENT = 4; + /** * Pick image to update account image */ - public static final int ACCOUNT_IMAGE_SETTINGS_PICK_NEW_INTENT = 4; + public static final int ACCOUNT_IMAGE_SETTINGS_PICK_NEW_INTENT = 5; } + + /** + * Intents results + */ + public final class IntentResults { + + /** + * Search user result + */ + public static final String SEARCH_USER_RESULT = "org.communiquons.android.searchUser.RESULT"; + + /** + * Global search result + */ + public static final String SEARCH_GLOBAL_RESULT = "org.communiquons.android.globalSearch.RESULT"; + + } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java index 51db929..1f38cf9 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java @@ -1,9 +1,13 @@ package org.communiquons.android.comunic.client.ui.activities; +import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import org.communiquons.android.comunic.client.ui.asynctasks.SafeAsyncTasksManager; + /** * Base application activity * @@ -11,10 +15,40 @@ import android.support.v7.app.AppCompatActivity; */ public abstract class BaseActivity extends AppCompatActivity { + /** + * Tasks manager + */ + private SafeAsyncTasksManager mSafeAsyncTasksManager = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //Initialize task manager + mSafeAsyncTasksManager = new SafeAsyncTasksManager(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + //Unset all task + mSafeAsyncTasksManager.unsetAllTasks(); + } + @NonNull @Override public ActionBar getSupportActionBar() { assert super.getSupportActionBar() != null; return super.getSupportActionBar(); } + + /** + * Get tasks manager associated with this activity + * + * @return Task manager associated to this activity + */ + public SafeAsyncTasksManager getTasksManager() { + return mSafeAsyncTasksManager; + } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/MainActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/MainActivity.java index 0ab4e6b..ac3d045 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/MainActivity.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/MainActivity.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; @@ -49,14 +50,16 @@ import org.communiquons.android.comunic.client.ui.fragments.groups.GroupPageMain import org.communiquons.android.comunic.client.ui.fragments.groups.UserGroupsFragment; import org.communiquons.android.comunic.client.ui.fragments.userpage.UserAccessDeniedFragment; import org.communiquons.android.comunic.client.ui.fragments.userpage.UserPageFragment; -import org.communiquons.android.comunic.client.ui.listeners.OnOpenGroupListener; -import org.communiquons.android.comunic.client.ui.listeners.onOpenUsersPageListener; +import org.communiquons.android.comunic.client.ui.listeners.OnOpenPageListener; import org.communiquons.android.comunic.client.ui.listeners.onPostOpenListener; import org.communiquons.android.comunic.client.ui.listeners.openConversationListener; import org.communiquons.android.comunic.client.ui.listeners.updateConversationListener; import org.communiquons.android.comunic.client.ui.utils.UiUtils; import org.communiquons.android.comunic.client.ui.views.NavigationBar; +import java.util.Objects; + +import static org.communiquons.android.comunic.client.ui.Constants.IntentRequestCode.MAIN_ACTIVITY_GLOBAL_SEARCH_INTENT; import static org.communiquons.android.comunic.client.ui.Constants.IntentRequestCode.MAIN_ACTIVITY_SEARCH_USER_INTENT; @@ -66,8 +69,8 @@ import static org.communiquons.android.comunic.client.ui.Constants.IntentRequest * @author Pierre HUBERT */ public class MainActivity extends BaseActivity implements - openConversationListener, updateConversationListener, onOpenUsersPageListener, - onPostOpenListener, NavigationBar.OnNavigationItemSelectedListener, OnOpenGroupListener { + openConversationListener, updateConversationListener, OnOpenPageListener, + onPostOpenListener, NavigationBar.OnNavigationItemSelectedListener { /** * Debug tag @@ -245,9 +248,9 @@ public class MainActivity extends BaseActivity implements return true; } - //To search a user - if (id == R.id.action_search_user) { - searchUser(); + //To perform a wider search + if(id == R.id.action_search) { + searchGlobal(); return true; } @@ -387,9 +390,28 @@ public class MainActivity extends BaseActivity implements switch (requestCode) { + //User search case MAIN_ACTIVITY_SEARCH_USER_INTENT: assert data.getData() != null; - openUserPage(Integer.decode(data.getData().getQueryParameter("userID"))); + openUserPage(Integer.decode( + Objects.requireNonNull( + data.getData().getQueryParameter("userID")))); + break; + + + //Global search result + case MAIN_ACTIVITY_GLOBAL_SEARCH_INTENT: + assert data.getData() != null; + + //Open user page or group accordingly to the choice of the user + Uri result = data.getData(); + String type = result.getQueryParameter(SearchActivity.INTENT_ARG_RESULT_TYPE); + int id = Integer.decode(Objects.requireNonNull(result.getQueryParameter(SearchActivity.INTENT_ARG_RESULT_ID))); + if(Objects.equals(type, SearchActivity.INTENT_RESULT_USER)) + openUserPage(id); + else + onOpenGroup(id); + break; } @@ -759,6 +781,17 @@ public class MainActivity extends BaseActivity implements } + /** + * Perform a global search + */ + private void searchGlobal(){ + + //Make intent + Intent intent = new Intent(this, SearchActivity.class); + startActivityForResult(intent, MAIN_ACTIVITY_GLOBAL_SEARCH_INTENT); + + } + /** * Open group page * diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchActivity.java new file mode 100644 index 0000000..d8dc470 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchActivity.java @@ -0,0 +1,178 @@ +package org.communiquons.android.comunic.client.ui.activities; + +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.MenuItem; +import android.widget.EditText; +import android.widget.Toast; + +import org.communiquons.android.comunic.client.R; +import org.communiquons.android.comunic.client.data.models.SearchResultWithInfo; +import org.communiquons.android.comunic.client.ui.Constants; +import org.communiquons.android.comunic.client.ui.adapters.SearchResultsAdapter; +import org.communiquons.android.comunic.client.ui.asynctasks.GlobalSearchTask; +import org.communiquons.android.comunic.client.ui.asynctasks.SafeAsyncTask; +import org.communiquons.android.comunic.client.ui.listeners.OnOpenPageListener; + +import java.util.ArrayList; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Search activity + * + * This activity can be used to search both users and groups + * + * @author Pierre HUBERT + */ +public class SearchActivity extends BaseActivity implements TextWatcher, OnOpenPageListener { + + /** + * Debug tag + */ + private static final String TAG = SearchActivity.class.getSimpleName(); + + + /** + * Results type + */ + public static final String INTENT_ARG_RESULT_ID = "id"; + public static final String INTENT_ARG_RESULT_TYPE = "type"; + public static final String INTENT_RESULT_USER = "user"; + public static final String INTENT_RESULT_GROUP = "group"; + + + /** + * Adapters + */ + private SearchResultsAdapter mAdapter; + + + /** + * Views + */ + private RecyclerView mRecyclerView; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_search); + + //Add go back button + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + ((EditText)findViewById(R.id.searchInput)).addTextChangedListener(this); + + //Initialize recycler view + mRecyclerView = findViewById(R.id.resultsRecyclerView); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mAdapter = new SearchResultsAdapter(this ,this); + mRecyclerView.setAdapter(mAdapter); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + + switch (item.getItemId()){ + + //Check if we have to finish activity + case android.R.id.home: + finish(); + return true; + + } + + + return super.onOptionsItemSelected(item); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + //Check if user did not type at least three characters + if(s.length() < 3) + return; //We ignore small researches + + //Perform the search + getTasksManager().unsetSpecificTasks(GlobalSearchTask.class); + GlobalSearchTask task = new GlobalSearchTask(this); + + task.setOnPostExecuteListener(new SafeAsyncTask.OnPostExecuteListener>() { + @Override + public void OnPostExecute(@Nullable ArrayList searchResultWithInfoList) { + onGotNewList(searchResultWithInfoList); + } + }); + + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""+s); + getTasksManager().addTask(task); + } + + @Override + public void afterTextChanged(Editable s) { + + } + + private void onGotNewList(@Nullable ArrayList list){ + + //First, check for errors + if(list == null){ + Toast.makeText(this, R.string.err_get_search_results, Toast.LENGTH_SHORT).show(); + return; + } + + //Apply search results + mAdapter.setList(list); + } + + @Override + public void onOpenGroup(int groupID) { + sendResult(groupID, INTENT_RESULT_GROUP); + } + + @Override + public void onOpenGroupAccessDenied(int groupID) { + onOpenGroup(groupID); + } + + @Override + public void openUserPage(int userID) { + sendResult(userID, INTENT_RESULT_USER); + } + + @Override + public void openUserAccessDeniedPage(int userID) { + openUserPage(userID); + } + + /** + * This method is called once the user has made a selection + * + * @param id The id of the element chosen by the user + * @param type The type of the element chosen by the user + */ + private void sendResult(int id, String type){ + + //Send result and terminates activity + Intent intent = new Intent(Constants.IntentResults.SEARCH_GLOBAL_RESULT); + intent.setData(Uri.parse("?" + INTENT_ARG_RESULT_TYPE+"="+type+"&"+INTENT_ARG_RESULT_ID+"="+id)); + setResult(RESULT_OK, intent); + finish(); + + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchUserActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchUserActivity.java index 37ce966..7f5474c 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchUserActivity.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/SearchUserActivity.java @@ -18,6 +18,7 @@ import android.widget.Toast; import org.communiquons.android.comunic.client.R; import org.communiquons.android.comunic.client.data.helpers.DatabaseHelper; import org.communiquons.android.comunic.client.data.helpers.GetUsersHelper; +import org.communiquons.android.comunic.client.ui.Constants; import org.communiquons.android.comunic.client.ui.adapters.UsersBasicAdapter; import org.communiquons.android.comunic.client.data.models.UserInfo; @@ -81,7 +82,7 @@ public class SearchUserActivity extends AppCompatActivity */ private void onGotUserID(int userID){ - Intent data = new Intent("org.communiquons.android.RESULT"); + Intent data = new Intent(Constants.IntentResults.SEARCH_USER_RESULT); data.setData(Uri.parse("?userID=" + userID)); setResult(RESULT_OK, data); finish(); diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java index de2dfa2..dfefbab 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java @@ -6,13 +6,11 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import org.communiquons.android.comunic.client.R; import org.communiquons.android.comunic.client.data.models.GroupInfo; import org.communiquons.android.comunic.client.ui.listeners.OnGroupActionListener; -import org.communiquons.android.comunic.client.ui.views.GroupImageView; -import org.communiquons.android.comunic.client.ui.views.GroupMembershipStatusView; +import org.communiquons.android.comunic.client.ui.viewholders.GroupViewHolder; import java.util.ArrayList; @@ -62,55 +60,12 @@ public class GroupsListAdapter extends BaseRecyclerViewAdapter { public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View view = LayoutInflater.from(getContext()).inflate( R.layout.viewholder_group, viewGroup, false); - return new GroupHolder(view); + return new GroupViewHolder(view, mOnGroupActionListener); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { - ((GroupHolder)viewHolder).bind(i); + ((GroupViewHolder)viewHolder).bind(mList.get(i)); } - - /** - * Single group holder class - */ - private class GroupHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - - - private GroupInfo mGroupInfo; - - - private GroupImageView mGroupImageView; - private TextView mGroupName; - private GroupMembershipStatusView mGroupMembershipStatus; - - GroupHolder(@NonNull View itemView) { - super(itemView); - - itemView.setOnClickListener(this); - - mGroupImageView = itemView.findViewById(R.id.groupImage); - mGroupName = itemView.findViewById(R.id.groupName); - mGroupMembershipStatus = itemView.findViewById(R.id.groupMembershipStatusView); - - mGroupMembershipStatus.setOnGroupMembershipUpdateListener(mOnGroupActionListener); - } - - GroupInfo getGroup(int pos){ - return mList.get(pos); - } - - void bind(int pos){ - mGroupInfo = getGroup(pos); - mGroupImageView.setGroup(mGroupInfo); - mGroupName.setText(mGroupInfo.getDisplayName()); - mGroupMembershipStatus.setGroup(mGroupInfo); - } - - @Override - public void onClick(View v) { - if(v.equals(itemView)) - mOnGroupActionListener.onOpenGroup(mGroupInfo.getId()); - } - } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/SearchResultsAdapter.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/SearchResultsAdapter.java new file mode 100644 index 0000000..22d7496 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/SearchResultsAdapter.java @@ -0,0 +1,112 @@ +package org.communiquons.android.comunic.client.ui.adapters; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import org.communiquons.android.comunic.client.R; +import org.communiquons.android.comunic.client.data.enums.KindSearchResult; +import org.communiquons.android.comunic.client.data.models.SearchResultWithInfo; +import org.communiquons.android.comunic.client.ui.listeners.OnOpenPageListener; +import org.communiquons.android.comunic.client.ui.viewholders.GroupViewHolder; +import org.communiquons.android.comunic.client.ui.viewholders.UserViewHolder; + +import java.util.ArrayList; + +/** + * Search results adapter + * + * @author Pierre HUBERT + */ +public class SearchResultsAdapter extends BaseRecyclerViewAdapter { + + /** + * The list of results to the search + */ + private ArrayList mList; + + /** + * Open listener + */ + private OnOpenPageListener openPageListener; + + /** + * Get view types + */ + private final int VIEW_TYPE_USER = 0; + private final int VIEW_TYPE_GROUP = 1; + + public SearchResultsAdapter(Context context, @Nullable OnOpenPageListener openPageListener) { + super(context); + + this.mList = new ArrayList<>(); + this.openPageListener = openPageListener; + } + + /** + * Set (update) the list of search results + * + * @param list New list to apply + */ + public void setList(ArrayList list){ + this.mList = list; + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return mList.size(); + } + + @Override + public int getItemViewType(int position) { + return mList.get(position).getKind() == KindSearchResult.USER ? + VIEW_TYPE_USER : VIEW_TYPE_GROUP; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) { + + RecyclerView.ViewHolder viewHolder; + + if(type == VIEW_TYPE_USER){ + viewHolder = new UserViewHolder(LayoutInflater + .from(getContext()) + .inflate(R.layout.viewholder_user, viewGroup, false) + , openPageListener); + } + + else if(type == VIEW_TYPE_GROUP){ + viewHolder = new GroupViewHolder(LayoutInflater + .from(getContext()) + .inflate(R.layout.viewholder_group, viewGroup, false) + , openPageListener); + } + + else + throw new RuntimeException("Intend to display a none supported kind of result!"); + + + return viewHolder; + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { + + SearchResultWithInfo searchResultWithInfo = mList.get(i); + + if(searchResultWithInfo.getKind() == KindSearchResult.USER){ + ((UserViewHolder)viewHolder).bind(searchResultWithInfo.getUserInfo()); + } + + if(searchResultWithInfo.getKind() == KindSearchResult.GROUP){ + ((GroupViewHolder)viewHolder).bind(searchResultWithInfo.getGroupInfo()); + } + } + + +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/UsersBasicAdapter.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/UsersBasicAdapter.java index 2050b1d..69c00c6 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/UsersBasicAdapter.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/UsersBasicAdapter.java @@ -59,6 +59,7 @@ public class UsersBasicAdapter extends ArrayAdapter { account_image.setUser(userInfos); } + return convertView; } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/GlobalSearchTask.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/GlobalSearchTask.java new file mode 100644 index 0000000..5254c5f --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/GlobalSearchTask.java @@ -0,0 +1,32 @@ +package org.communiquons.android.comunic.client.ui.asynctasks; + +import android.content.Context; +import android.support.annotation.Nullable; + +import org.communiquons.android.comunic.client.data.helpers.SearchHelper; +import org.communiquons.android.comunic.client.data.models.SearchResult; +import org.communiquons.android.comunic.client.data.models.SearchResultWithInfo; + +import java.util.ArrayList; + +public class GlobalSearchTask extends SafeAsyncTask> { + + public GlobalSearchTask(Context context) { + super(context); + } + + @Override + @Nullable + protected ArrayList doInBackground(String... strings) { + + SearchHelper helper = new SearchHelper(getContext()); + + //First, perform the search itself + ArrayList list = helper.global(strings[0]); + if(list == null) + return null; + + //Then get information about related users and groups + return helper.fillResults(list); + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnOpenPageListener.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnOpenPageListener.java new file mode 100644 index 0000000..6c66d5e --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnOpenPageListener.java @@ -0,0 +1,9 @@ +package org.communiquons.android.comunic.client.ui.listeners; + +/** + * Implement this interface on activities that can open group and user pages + * + * @author Pierre HUBERT + */ +public interface OnOpenPageListener extends OnOpenGroupListener, onOpenUsersPageListener { +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/GroupViewHolder.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/GroupViewHolder.java new file mode 100644 index 0000000..a90ddc6 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/GroupViewHolder.java @@ -0,0 +1,73 @@ +package org.communiquons.android.comunic.client.ui.viewholders; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import org.communiquons.android.comunic.client.R; +import org.communiquons.android.comunic.client.data.models.GroupInfo; +import org.communiquons.android.comunic.client.ui.listeners.OnGroupActionListener; +import org.communiquons.android.comunic.client.ui.listeners.OnOpenGroupListener; +import org.communiquons.android.comunic.client.ui.views.GroupImageView; +import org.communiquons.android.comunic.client.ui.views.GroupMembershipStatusView; + +/** + * Single group holder class + * + * @author Pierre HUBERT + */ +public class GroupViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + + private GroupInfo mGroupInfo; + + private OnOpenGroupListener mOnOpenGroupListener; + + private GroupImageView mGroupImageView; + private TextView mGroupName; + private GroupMembershipStatusView mGroupMembershipStatus; + + + public GroupViewHolder(@NonNull View itemView, @Nullable OnOpenGroupListener openGroupListener){ + this(itemView, null); + mOnOpenGroupListener = openGroupListener; + } + + public GroupViewHolder(@NonNull View itemView, @Nullable OnGroupActionListener actionListener) { + super(itemView); + + itemView.setOnClickListener(this); + + mGroupImageView = itemView.findViewById(R.id.groupImage); + mGroupName = itemView.findViewById(R.id.groupName); + mGroupMembershipStatus = itemView.findViewById(R.id.groupMembershipStatusView); + + if(actionListener != null) { + mGroupMembershipStatus.setOnGroupMembershipUpdateListener(actionListener); + mOnOpenGroupListener = actionListener; + } + else + mGroupMembershipStatus.setVisibility(View.GONE); + } + + + /** + * Bind (apply) a new group to the view + * + * @param group The group to apply to the view + */ + public void bind(GroupInfo group){ + mGroupInfo = group; + mGroupImageView.setGroup(mGroupInfo); + mGroupName.setText(mGroupInfo.getDisplayName()); + mGroupMembershipStatus.setGroup(mGroupInfo); + } + + @Override + public void onClick(View v) { + if(v.equals(itemView) && mOnOpenGroupListener != null) + mOnOpenGroupListener.onOpenGroup(mGroupInfo.getId()); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/UserViewHolder.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/UserViewHolder.java new file mode 100644 index 0000000..eac706f --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/viewholders/UserViewHolder.java @@ -0,0 +1,56 @@ +package org.communiquons.android.comunic.client.ui.viewholders; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import org.communiquons.android.comunic.client.R; +import org.communiquons.android.comunic.client.data.models.UserInfo; +import org.communiquons.android.comunic.client.ui.listeners.onOpenUsersPageListener; +import org.communiquons.android.comunic.client.ui.views.WebUserAccountImage; + +/** + * User view holder + * + * Handles displaying of a single user + * + * @author Pierre HUBERT + */ +public class UserViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + //Private fields + private onOpenUsersPageListener mListener; + private WebUserAccountImage mUserAccountImage; + private TextView mUserName; + private UserInfo mUserInfo; + + public UserViewHolder(@NonNull View itemView, @Nullable onOpenUsersPageListener listener) { + super(itemView); + + mUserAccountImage = itemView.findViewById(R.id.user_account_image); + mUserName = itemView.findViewById(R.id.user_name); + + this.mListener = listener; + itemView.setOnClickListener(this); + } + + + /** + * Apply a new user to this view + * + * @param userInfo Information about the user to apply + */ + public void bind(UserInfo userInfo){ + this.mUserInfo = userInfo; + mUserAccountImage.setUser(userInfo); + mUserName.setText(userInfo.getDisplayFullName()); + } + + @Override + public void onClick(View v) { + if(mListener != null && v.equals(itemView)) + mListener.openUserPage(mUserInfo.getId()); + } +} diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml new file mode 100644 index 0000000..12ad53b --- /dev/null +++ b/app/src/main/res/layout/activity_search.xml @@ -0,0 +1,37 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/viewholder_user.xml b/app/src/main/res/layout/viewholder_user.xml new file mode 100644 index 0000000..9c8c68e --- /dev/null +++ b/app/src/main/res/layout/viewholder_user.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml index 5a0abcb..ef4167b 100644 --- a/app/src/main/res/menu/main_menu.xml +++ b/app/src/main/res/menu/main_menu.xml @@ -9,8 +9,8 @@ + android:id="@+id/action_search" + android:title="@string/main_menu_search" /> Annuler Quitter Une erreur a survenue lors de la mise à jour de votre appartenance au groupe ! + Rechercher + Recherche + Rechercher un utilisateur, un groupe… + Une erreur a survenue lors de la récupération du résultat de votre recherche ! + Accès au groupe refusé. + Impossible de récupérer les informations sur le groupe ! + Accès sur invitation \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 847868f..cab995c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -314,4 +314,8 @@ Could not get group information! Access to the group denied. Invitation only + Search + Search + Search a user, a group… + Could not get results of your search!