user search
This commit is contained in:
parent
093883d40e
commit
25d9688c8a
@ -15,10 +15,13 @@ import com.dowerx.quack.fragment.FeedFragment;
|
|||||||
import com.dowerx.quack.fragment.NewPostFragment;
|
import com.dowerx.quack.fragment.NewPostFragment;
|
||||||
import com.dowerx.quack.fragment.ProfileFragment;
|
import com.dowerx.quack.fragment.ProfileFragment;
|
||||||
import com.dowerx.quack.R;
|
import com.dowerx.quack.R;
|
||||||
|
import com.dowerx.quack.fragment.SearchFragment;
|
||||||
import com.google.android.material.navigation.NavigationBarView;
|
import com.google.android.material.navigation.NavigationBarView;
|
||||||
|
|
||||||
public class SwitcherActivity extends AppCompatActivity implements NavigationBarView.OnItemSelectedListener, NavigationBarView.OnItemReselectedListener {
|
public class SwitcherActivity extends AppCompatActivity implements NavigationBarView.OnItemSelectedListener, NavigationBarView.OnItemReselectedListener {
|
||||||
|
|
||||||
|
private String userId = "";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -38,6 +41,7 @@ public class SwitcherActivity extends AppCompatActivity implements NavigationBar
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNavigationItemReselected(@NonNull MenuItem menuItem) {
|
public void onNavigationItemReselected(@NonNull MenuItem menuItem) {
|
||||||
|
this.userId = "";
|
||||||
this.onNavigationItemSelected(menuItem);
|
this.onNavigationItemSelected(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,10 +49,12 @@ public class SwitcherActivity extends AppCompatActivity implements NavigationBar
|
|||||||
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
|
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
|
||||||
if (menuItem.getItemId() == R.id.nav_feed) {
|
if (menuItem.getItemId() == R.id.nav_feed) {
|
||||||
loadFragment(new FeedFragment());
|
loadFragment(new FeedFragment());
|
||||||
|
} else if (menuItem.getItemId() == R.id.nav_search) {
|
||||||
|
loadFragment(new SearchFragment());
|
||||||
} else if (menuItem.getItemId() == R.id.nav_post) {
|
} else if (menuItem.getItemId() == R.id.nav_post) {
|
||||||
loadFragment(new NewPostFragment());
|
loadFragment(new NewPostFragment());
|
||||||
} else if (menuItem.getItemId() == R.id.nav_profile) {
|
} else if (menuItem.getItemId() == R.id.nav_profile) {
|
||||||
loadFragment(new ProfileFragment());
|
loadFragment(ProfileFragment.newInstance(userId));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -61,4 +67,8 @@ public class SwitcherActivity extends AppCompatActivity implements NavigationBar
|
|||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUser(String id) {
|
||||||
|
this.userId = id;
|
||||||
|
}
|
||||||
}
|
}
|
@ -40,7 +40,7 @@ public class ProfileFragment extends Fragment implements LoaderManager.LoaderCal
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if (getArguments() != null) {
|
if (getArguments() != null && !getArguments().getString(ID).isEmpty()) {
|
||||||
id = getArguments().getString(ID);
|
id = getArguments().getString(ID);
|
||||||
} else {
|
} else {
|
||||||
id = AuthService.getId();
|
id = AuthService.getId();
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.dowerx.quack.fragment;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.loader.app.LoaderManager;
|
||||||
|
import androidx.loader.content.Loader;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import com.dowerx.quack.R;
|
||||||
|
import com.dowerx.quack.activity.SwitcherActivity;
|
||||||
|
import com.dowerx.quack.model.User;
|
||||||
|
import com.dowerx.quack.service.UserService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SearchFragment extends Fragment implements LoaderManager.LoaderCallbacks<List<User>> {
|
||||||
|
|
||||||
|
private RecyclerView recyclerView;
|
||||||
|
private EditText query;
|
||||||
|
|
||||||
|
public SearchFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static SearchFragment newInstance() {
|
||||||
|
SearchFragment fragment = new SearchFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (getArguments() != null) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_search_list, container, false);
|
||||||
|
|
||||||
|
Context context = view.getContext();
|
||||||
|
recyclerView = view.findViewById(R.id.list);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(context));
|
||||||
|
|
||||||
|
query = view.findViewById(R.id.query);
|
||||||
|
LoaderManager.getInstance(this).initLoader(1, null, this);
|
||||||
|
view.findViewById(R.id.search).setOnClickListener(v -> {
|
||||||
|
LoaderManager.getInstance(this).destroyLoader(1);
|
||||||
|
LoaderManager.getInstance(this).initLoader(1, null, this);
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Loader<List<User>> onCreateLoader(int id, @Nullable Bundle args) {
|
||||||
|
return new UserService.SearchUsername(query.getText().toString(), getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(@NonNull Loader<List<User>> loader, List<User> data) {
|
||||||
|
recyclerView.setAdapter(new UserRecyclerViewAdapter(data, (SwitcherActivity) getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(@NonNull Loader<List<User>> loader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.dowerx.quack.fragment;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.dowerx.quack.R;
|
||||||
|
import com.dowerx.quack.activity.SwitcherActivity;
|
||||||
|
import com.dowerx.quack.databinding.FragmentUserBinding;
|
||||||
|
import com.dowerx.quack.model.User;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserRecyclerViewAdapter extends RecyclerView.Adapter<UserRecyclerViewAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<User> mValues;
|
||||||
|
private final SwitcherActivity activity;
|
||||||
|
|
||||||
|
public UserRecyclerViewAdapter(List<User> items, SwitcherActivity activity) {
|
||||||
|
this.mValues = items;
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
return new ViewHolder(FragmentUserBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||||
|
holder.mItem = mValues.get(position);
|
||||||
|
holder.username.setText(holder.mItem.getUsername());
|
||||||
|
holder.profile_button.setOnClickListener(v -> {
|
||||||
|
activity.setUser(holder.mItem.getId());
|
||||||
|
activity.findViewById(R.id.nav_profile).performClick();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mValues.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public final TextView username;
|
||||||
|
public final Button profile_button;
|
||||||
|
public User mItem;
|
||||||
|
|
||||||
|
public ViewHolder(FragmentUserBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
username = binding.username;
|
||||||
|
profile_button = binding.profileButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " '" + username.getText() + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.loader.content.AsyncTaskLoader;
|
import androidx.loader.content.AsyncTaskLoader;
|
||||||
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
import com.dowerx.quack.model.Post;
|
import com.dowerx.quack.model.Post;
|
||||||
import com.dowerx.quack.model.User;
|
import com.dowerx.quack.model.User;
|
||||||
@ -78,4 +79,43 @@ public class UserService {
|
|||||||
forceLoad();
|
forceLoad();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SearchUsername extends AsyncTaskLoader<List<User>> {
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
public SearchUsername(String username, Context context) {
|
||||||
|
super(context);
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<User> loadInBackground() {
|
||||||
|
List<User> users = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
// firestore doesn't have full-string search
|
||||||
|
// ugly, but i have to filter it myself
|
||||||
|
QuerySnapshot result = Tasks.await(db.collection("user").get());
|
||||||
|
|
||||||
|
for (DocumentSnapshot doc : result) {
|
||||||
|
if (doc.getString("username").contains(username)) {
|
||||||
|
users.add(new User(
|
||||||
|
doc.getString("username"),
|
||||||
|
doc.getString("email"),
|
||||||
|
doc.getString("id")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStartLoading() {
|
||||||
|
forceLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
42
app/src/main/res/layout/fragment_search_list.xml
Normal file
42
app/src/main/res/layout/fragment_search_list.xml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/query"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/search"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:hint="@string/username_hint"
|
||||||
|
android:autofillHints="username,search"
|
||||||
|
android:layout_margin="20dp"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/search"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:text="@string/search_btn"
|
||||||
|
android:layout_margin="20dp"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:name="com.dowerx.quack.fragment.SearchFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layoutManager="LinearLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/query"
|
||||||
|
tools:context=".fragment.SearchFragment"
|
||||||
|
tools:listitem="@layout/fragment_user" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
26
app/src/main/res/layout/fragment_user.xml
Normal file
26
app/src/main/res/layout/fragment_user.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/username"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/profile_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/view_profile"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_margin="20dp"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -16,4 +16,95 @@
|
|||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
<string name="post_btn">Post</string>
|
<string name="post_btn">Post</string>
|
||||||
<string name="remove_btn">Remove</string>
|
<string name="remove_btn">Remove</string>
|
||||||
|
<string name="large_text">
|
||||||
|
"Material is the metaphor.\n\n"
|
||||||
|
|
||||||
|
"A material metaphor is the unifying theory of a rationalized space and a system of motion."
|
||||||
|
"The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
|
||||||
|
"technologically advanced and open to imagination and magic.\n"
|
||||||
|
"Surfaces and edges of the material provide visual cues that are grounded in reality. The "
|
||||||
|
"use of familiar tactile attributes helps users quickly understand affordances. Yet the "
|
||||||
|
"flexibility of the material creates new affordances that supercede those in the physical "
|
||||||
|
"world, without breaking the rules of physics.\n"
|
||||||
|
"The fundamentals of light, surface, and movement are key to conveying how objects move, "
|
||||||
|
"interact, and exist in space and in relation to each other. Realistic lighting shows "
|
||||||
|
"seams, divides space, and indicates moving parts.\n\n"
|
||||||
|
|
||||||
|
"Bold, graphic, intentional.\n\n"
|
||||||
|
|
||||||
|
"The foundational elements of print based design typography, grids, space, scale, color, "
|
||||||
|
"and use of imagery guide visual treatments. These elements do far more than please the "
|
||||||
|
"eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
|
||||||
|
"imagery, large scale typography, and intentional white space create a bold and graphic "
|
||||||
|
"interface that immerse the user in the experience.\n"
|
||||||
|
"An emphasis on user actions makes core functionality immediately apparent and provides "
|
||||||
|
"waypoints for the user.\n\n"
|
||||||
|
|
||||||
|
"Motion provides meaning.\n\n"
|
||||||
|
|
||||||
|
"Motion respects and reinforces the user as the prime mover. Primary user actions are "
|
||||||
|
"inflection points that initiate motion, transforming the whole design.\n"
|
||||||
|
"All action takes place in a single environment. Objects are presented to the user without "
|
||||||
|
"breaking the continuity of experience even as they transform and reorganize.\n"
|
||||||
|
"Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
|
||||||
|
"Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
|
||||||
|
|
||||||
|
"3D world.\n\n"
|
||||||
|
|
||||||
|
"The material environment is a 3D space, which means all objects have x, y, and z "
|
||||||
|
"dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
|
||||||
|
"positive z-axis extending towards the viewer. Every sheet of material occupies a single "
|
||||||
|
"position along the z-axis and has a standard 1dp thickness.\n"
|
||||||
|
"On the web, the z-axis is used for layering and not for perspective. The 3D world is "
|
||||||
|
"emulated by manipulating the y-axis.\n\n"
|
||||||
|
|
||||||
|
"Light and shadow.\n\n"
|
||||||
|
|
||||||
|
"Within the material environment, virtual lights illuminate the scene. Key lights create "
|
||||||
|
"directional shadows, while ambient light creates soft shadows from all angles.\n"
|
||||||
|
"Shadows in the material environment are cast by these two light sources. In Android "
|
||||||
|
"development, shadows occur when light sources are blocked by sheets of material at "
|
||||||
|
"various positions along the z-axis. On the web, shadows are depicted by manipulating the "
|
||||||
|
"y-axis only. The following example shows the card with a height of 6dp.\n\n"
|
||||||
|
|
||||||
|
"Resting elevation.\n\n"
|
||||||
|
|
||||||
|
"All material objects, regardless of size, have a resting elevation, or default elevation "
|
||||||
|
"that does not change. If an object changes elevation, it should return to its resting "
|
||||||
|
"elevation as soon as possible.\n\n"
|
||||||
|
|
||||||
|
"Component elevations.\n\n"
|
||||||
|
|
||||||
|
"The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
|
||||||
|
"does not vary from 6dp in one app to 16dp in another app).\n"
|
||||||
|
"Components may have different resting elevations across platforms, depending on the depth "
|
||||||
|
"of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
|
||||||
|
|
||||||
|
"Responsive elevation and dynamic elevation offsets.\n\n"
|
||||||
|
|
||||||
|
"Some component types have responsive elevation, meaning they change elevation in response "
|
||||||
|
"to user input (e.g., normal, focused, and pressed) or system events. These elevation "
|
||||||
|
"changes are consistently implemented using dynamic elevation offsets.\n"
|
||||||
|
"Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
|
||||||
|
"to the component’s resting state. They ensure that elevation changes are consistent "
|
||||||
|
"across actions and component types. For example, all components that lift on press have "
|
||||||
|
"the same elevation change relative to their resting elevation.\n"
|
||||||
|
"Once the input event is completed or cancelled, the component will return to its resting "
|
||||||
|
"elevation.\n\n"
|
||||||
|
|
||||||
|
"Avoiding elevation interference.\n\n"
|
||||||
|
|
||||||
|
"Components with responsive elevations may encounter other components as they move between "
|
||||||
|
"their resting elevations and dynamic elevation offsets. Because material cannot pass "
|
||||||
|
"through other material, components avoid interfering with one another any number of ways, "
|
||||||
|
"whether on a per component basis or using the entire app layout.\n"
|
||||||
|
"On a component level, components can move or be removed before they cause interference. "
|
||||||
|
"For example, a floating action button (FAB) can disappear or move off screen before a "
|
||||||
|
"user picks up a card, or it can move if a snackbar appears.\n"
|
||||||
|
"On the layout level, design your app layout to minimize opportunities for interference. "
|
||||||
|
"For example, position the FAB to one side of stream of a cards so the FAB won’t interfere "
|
||||||
|
"when a user tries to pick up one of cards.\n\n"
|
||||||
|
</string>
|
||||||
|
<string name="search_btn">Search</string>
|
||||||
|
<string name="view_profile">View</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user