Search : Show search results on main page, in place of search history

TODO : Add an option to see search history

https://gitlab.com/AuroraOSS/AuroraStore/issues/103
This commit is contained in:
Mr. Dragon
2019-07-26 02:04:00 +05:30
parent 27ec8c17dc
commit 8cf36e4a32
11 changed files with 150 additions and 116 deletions

View File

@@ -25,6 +25,7 @@ public enum ErrorType {
NO_APPS,
NO_UPDATES,
NO_SEARCH,
NO_SEARCH_RESULT,
NO_DOWNLOADS,
UNKNOWN,
APP_NOT_FOUND,

View File

@@ -42,6 +42,7 @@ import com.aurora.store.R;
import com.aurora.store.adapter.ViewPagerAdapter;
import com.aurora.store.fragment.AppsFragment;
import com.aurora.store.fragment.HomeFragment;
import com.aurora.store.fragment.SearchAppsFragment;
import com.aurora.store.fragment.SearchFragment;
import com.aurora.store.utility.Accountant;
import com.aurora.store.utility.PrefUtil;
@@ -198,7 +199,7 @@ public class AuroraActivity extends AppCompatActivity {
pagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
pagerAdapter.addFragment(0, new HomeFragment());
pagerAdapter.addFragment(1, new AppsFragment());
pagerAdapter.addFragment(2, new SearchFragment());
pagerAdapter.addFragment(2, new SearchAppsFragment());
viewPager.setAdapter(pagerAdapter);
viewPager.setScroll(false);
viewPager.setOffscreenPageLimit(2);

View File

@@ -101,6 +101,11 @@ public class InstalledAppsAdapter extends RecyclerView.Adapter<InstalledAppsAdap
notifyDataSetChanged();
}
public void clearData(){
appList.clear();
notifyDataSetChanged();
}
public boolean isDataEmpty() {
return appList.isEmpty();
}

View File

@@ -251,6 +251,7 @@ public abstract class BaseFragment extends Fragment {
/*Exception handling methods*/
protected void processException(Throwable e) {
e.printStackTrace();
disposable.clear();
if (e instanceof AuthException) {
processAuthException((AuthException) e);

View File

@@ -52,7 +52,6 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
public class DevAppsFragment extends BaseFragment {
@@ -126,7 +125,7 @@ public class DevAppsFragment extends BaseFragment {
if (shouldIterate) {
addApps(appList);
} else if (appList.isEmpty()) {
setErrorView(ErrorType.NO_SEARCH);
setErrorView(ErrorType.NO_SEARCH_RESULT);
switchViews(true);
} else {
switchViews(false);

View File

@@ -20,21 +20,21 @@
package com.aurora.store.fragment;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.ColorStateList;
import android.database.Cursor;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SearchView;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -44,6 +44,7 @@ import com.aurora.store.ErrorType;
import com.aurora.store.Filter;
import com.aurora.store.R;
import com.aurora.store.activity.AuroraActivity;
import com.aurora.store.activity.DetailsActivity;
import com.aurora.store.adapter.EndlessAppsAdapter;
import com.aurora.store.model.App;
import com.aurora.store.sheet.FilterBottomSheet;
@@ -53,14 +54,16 @@ import com.aurora.store.utility.NetworkUtil;
import com.aurora.store.utility.Util;
import com.aurora.store.utility.ViewUtil;
import com.bumptech.glide.Glide;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -72,20 +75,17 @@ public class SearchAppsFragment extends BaseFragment {
@BindView(R.id.search_apps_list)
RecyclerView recyclerView;
@BindView(R.id.search_apps)
SearchView searchView;
@BindView(R.id.filter_fab)
FloatingActionButton filterFab;
@BindView(R.id.searchQuery)
EditText searchQuery;
ExtendedFloatingActionButton filterFab;
@BindView(R.id.related_chip_group)
ChipGroup relatedChipGroup;
@BindView(R.id.view_progress)
RelativeLayout layoutProgress;
private Context context;
private View view;
private String query;
private List<String> relatedTags = new ArrayList<>();
private BottomNavigationView bottomNavigationView;
private EndlessAppsAdapter endlessAppsAdapter;
private SearchTask searchTask;
@@ -94,7 +94,21 @@ public class SearchAppsFragment extends BaseFragment {
}
private void setQuery(String query) {
this.query = query;
if (looksLikeAPackageId(query)) {
context.startActivity(DetailsActivity.getDetailsIntent(getContext(), query));
} else {
this.query = query;
fetchSearchAppsList(false);
}
}
private boolean looksLikeAPackageId(String query) {
if (TextUtils.isEmpty(query)) {
return false;
}
String pattern = "([\\p{L}_$][\\p{L}\\p{N}_$]*\\.)+[\\p{L}_$][\\p{L}\\p{N}_$]*";
Pattern r = Pattern.compile(pattern);
return r.matcher(query).matches();
}
@Override
@@ -108,67 +122,89 @@ public class SearchAppsFragment extends BaseFragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_search_applist, container, false);
ButterKnife.bind(this, view);
Bundle arguments = getArguments();
if (arguments != null) {
setQuery(arguments.getString("SearchQuery"));
searchQuery.setText(getQuery());
} else
Log.e("No category id provided");
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupQueryEdit();
setErrorView(ErrorType.UNKNOWN);
setErrorView(ErrorType.NO_SEARCH);
switchViews(true);
setupSearch();
setupRecycler();
if (getActivity() instanceof AuroraActivity) {
bottomNavigationView = ((AuroraActivity) getActivity()).getBottomNavigation();
setBaseBottomNavigationView(bottomNavigationView);
}
if (bottomNavigationView != null)
ViewUtil.hideBottomNav(bottomNavigationView, true);
filterFab.show();
filterFab.setOnClickListener(v -> getFilterDialog());
}
@Override
public void onResume() {
super.onResume();
if (endlessAppsAdapter == null || endlessAppsAdapter.isDataEmpty())
fetchSearchAppsList(false);
}
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (searchView != null && Util.isIMEEnabled(context))
searchView.requestFocus();
}
}
@Override
public void onDestroy() {
Glide.with(this).pauseAllRequests();
disposable.dispose();
if (bottomNavigationView != null)
ViewUtil.showBottomNav(bottomNavigationView, true);
if (Util.filterSearchNonPersistent(context))
new Filter(context).resetFilterPreferences();
super.onDestroy();
}
private void setupQueryEdit() {
searchQuery.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus)
searchQuery.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_search, 0);
else
searchQuery.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_edit, 0);
});
searchQuery.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
query = searchQuery.getText().toString();
fetchSearchAppsList(false);
searchQuery.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_edit, 0);
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
searchQuery.clearFocus();
private void setupSearch() {
SearchManager searchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
ComponentName componentName = getActivity().getComponentName();
if (null != searchManager && componentName != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName));
}
if (!StringUtils.isEmpty(AuroraActivity.externalQuery))
setQuery(AuroraActivity.externalQuery);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextChange(String query) {
if (StringUtils.isEmpty(query)) {
endlessAppsAdapter.clearData();
switchViews(true);
filterFab.hide();
}
return true;
} else
return false;
}
@Override
public boolean onQueryTextSubmit(String query) {
searchView.clearFocus();
setQuery(query);
return true;
}
});
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
@Override
public boolean onSuggestionSelect(int position) {
return true;
}
@Override
public boolean onSuggestionClick(int position) {
Cursor cursor = searchView.getSuggestionsAdapter().getCursor();
cursor.moveToPosition(position);
if (position == 0) {
searchView.setQuery(cursor.getString(2), true);
searchView.setQuery(cursor.getString(1), false);
} else
searchView.setQuery(cursor.getString(1), true);
setQuery(cursor.getString(0));
return true;
}
});
}
@@ -201,17 +237,17 @@ public class SearchAppsFragment extends BaseFragment {
disposable.add(Observable.fromCallable(() -> searchTask.getSearchResults(iterator))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(d -> ViewUtil.showWithAnimation(layoutProgress))
.doOnTerminate(() -> ViewUtil.hideWithAnimation(layoutProgress))
.subscribe(appList -> {
if (view != null) {
if (shouldIterate) {
addApps(appList);
} else if (appList.isEmpty() && endlessAppsAdapter.isDataEmpty()) {
setErrorView(ErrorType.NO_SEARCH);
filterFab.hide(true);
setErrorView(ErrorType.NO_SEARCH_RESULT);
switchViews(true);
} else {
switchViews(false);
filterFab.show(true);
if (endlessAppsAdapter != null)
endlessAppsAdapter.addData(appList);
if (!relatedTags.isEmpty())
@@ -246,18 +282,16 @@ public class SearchAppsFragment extends BaseFragment {
}
private void setupRecycler() {
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false);
endlessAppsAdapter = new EndlessAppsAdapter(context);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(this.getActivity(), R.anim.anim_falldown));
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(endlessAppsAdapter);
EndlessScrollListener endlessScrollListener = new EndlessScrollListener(mLayoutManager) {
recyclerView.addOnScrollListener(new EndlessScrollListener(layoutManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
fetchSearchAppsList(true);
}
};
recyclerView.addOnScrollListener(endlessScrollListener);
});
recyclerView.setOnFlingListener(new RecyclerView.OnFlingListener() {
@Override
public boolean onFling(int velocityX, int velocityY) {
@@ -265,8 +299,6 @@ public class SearchAppsFragment extends BaseFragment {
filterFab.show();
} else if (velocityY > 0) {
filterFab.hide();
if (bottomNavigationView != null)
ViewUtil.hideBottomNav(bottomNavigationView, true);
}
return false;
}
@@ -289,11 +321,9 @@ public class SearchAppsFragment extends BaseFragment {
if (chip.isChecked()) {
query = query + " " + tag;
fetchData();
searchQuery.setText(query);
} else {
query = query.replace(tag, "");
fetchData();
searchQuery.setText(query);
}
});
relatedChipGroup.addView(chip);

View File

@@ -25,10 +25,12 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import com.aurora.store.exception.MalformedRequestException;
import com.aurora.store.manager.BlacklistManager;
import com.aurora.store.model.App;
import com.aurora.store.model.AppBuilder;
import com.aurora.store.utility.Accountant;
import com.aurora.store.utility.Log;
import com.aurora.store.utility.PackageUtil;
import com.dragons.aurora.playstoreapiv2.BulkDetailsEntry;
@@ -75,13 +77,21 @@ public class UpdatableAppsTask extends AllAppsTask {
private List<App> getRemoteAppList(List<String> packageNames) throws IOException {
final List<App> appList = new ArrayList<>();
final List<BulkDetailsEntry> bulkDetailsEntries = getApi().bulkDetails(packageNames).getEntryList();
for (BulkDetailsEntry bulkDetailsEntry : bulkDetailsEntries) {
if (!bulkDetailsEntry.hasDoc()) {
continue;
try {
final List<BulkDetailsEntry> bulkDetailsEntries = getApi().bulkDetails(packageNames).getEntryList();
for (BulkDetailsEntry bulkDetailsEntry : bulkDetailsEntries) {
if (!bulkDetailsEntry.hasDoc()) {
continue;
}
appList.add(AppBuilder.build(bulkDetailsEntry.getDoc()));
}
appList.add(AppBuilder.build(bulkDetailsEntry.getDoc()));
} catch (Exception e) {
if (e instanceof MalformedRequestException) {
Log.e("Malformed Request : %s", e.getMessage());
} else
throw e;
}
return appList;
}

View File

@@ -74,6 +74,11 @@ public class ErrorView extends RelativeLayout {
txtError.setText(R.string.list_empty_updates);
break;
case NO_SEARCH:
imgError.setImageDrawable(context.getDrawable(R.drawable.ic_search_alt));
txtError.setText(R.string.title_search);
btnError.setVisibility(GONE);
break;
case NO_SEARCH_RESULT:
imgError.setImageDrawable(context.getDrawable(R.drawable.ic_search_alt));
txtError.setText(R.string.error_search_not_found);
break;

View File

@@ -24,6 +24,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?android:attr/colorControlNormal"
android:fillColor="?android:attr/colorForeground"
android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z" />
</vector>

View File

@@ -23,30 +23,26 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground">
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/searchQuery"
<androidx.appcompat.widget.SearchView
android:id="@+id/search_apps"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_margin="@dimen/margin_small"
android:autofillHints="@string/app_name"
android:background="@drawable/search_bg"
android:drawableEnd="@drawable/ic_edit"
android:hint="@string/title_search"
android:imeOptions="actionSearch|flagNoFullscreen"
android:imeOptions="actionSearch|actionGo|actionDone"
android:inputType="text"
android:layoutDirection="locale"
android:paddingStart="@dimen/margin_large"
android:paddingEnd="@dimen/margin_normal"
android:selectAllOnFocus="true" />
app:iconifiedByDefault="false"
app:queryBackground="@android:color/transparent"
app:queryHint="@string/title_search"
app:searchIcon="@drawable/ic_round_search" />
<ViewSwitcher
android:id="@+id/view_switcher"
@@ -74,32 +70,17 @@
android:layout_height="wrap_content" />
</HorizontalScrollView>
<View
android:id="@+id/gradient_view"
android:layout_width="match_parent"
android:layout_height="32dp"
android:layout_below="@id/chip_view"
android:background="@drawable/gradient_bg"
android:translationZ="1dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_apps_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/gradient_view"
android:layout_marginTop="-32dp"
android:layout_below="@id/chip_view"
android:clipToPadding="false"
android:overScrollMode="never"
android:paddingTop="@dimen/margin_small"
android:paddingBottom="@dimen/margin_bottom"
android:scrollbars="none"
android:splitMotionEvents="false"
tools:listitem="@layout/item_installed" />
<include
layout="@layout/item_loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</RelativeLayout>
<LinearLayout
@@ -108,23 +89,23 @@
android:layout_height="match_parent"
android:orientation="vertical" />
</ViewSwitcher>
</LinearLayout>
<androidx.core.widget.ContentLoadingProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/filter_fab"
style="@style/Widget.MaterialComponents.FloatingActionButton"
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/margin_normal"
app:fabSize="auto"
app:layout_anchorGravity="top|end"
app:srcCompat="@drawable/ic_filter"
app:useMaterialThemeColors="false" />
android:layout_gravity="center_horizontal|bottom"
android:layout_marginEnd="@dimen/margin_normal"
android:layout_marginBottom="@dimen/margin_bottom"
android:text="Filter"
android:textAppearance="@style/TextAppearance.Aurora.Button"
android:textColor="@color/colorWhite"
android:visibility="gone"
app:backgroundTint="@color/colorAccent"
app:icon="@drawable/ic_filter"
app:iconTint="@color/colorWhite"
app:layout_anchorGravity="top|end" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -20,6 +20,7 @@
-->
<resources>
<dimen name="margin_bottom">64dp</dimen>
<dimen name="margin_xlarge">32dp</dimen>
<dimen name="margin_large">24dp</dimen>
<dimen name="margin_normal">16dp</dimen>