user search

This commit is contained in:
Benedek László 2024-05-08 19:22:15 +02:00
parent 093883d40e
commit 25d9688c8a
8 changed files with 362 additions and 2 deletions

View File

@ -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;
}
} }

View File

@ -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();

View File

@ -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) {
}
}

View File

@ -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() + "'";
}
}
}

View File

@ -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();
}
}
} }

View 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>

View 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>

View File

@ -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 components 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 wont 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>