mirror of
https://github.com/lubosz/overte.git
synced 2025-04-06 12:02:19 +02:00
Android - Home screen has search. First implementation that retrieves 10 pages of concurrency from the user_stories API and allows filtering
This commit is contained in:
parent
9c44be3557
commit
0ae564b9b6
6 changed files with 200 additions and 27 deletions
|
@ -12,10 +12,12 @@ import android.support.v7.app.AppCompatActivity;
|
|||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabWidget;
|
||||
import android.widget.TextView;
|
||||
|
@ -97,7 +99,7 @@ public class HomeActivity extends AppCompatActivity implements NavigationView.On
|
|||
});
|
||||
domainsView.setAdapter(domainAdapter);
|
||||
|
||||
SearchView searchView = findViewById(R.id.searchView);
|
||||
EditText searchView = findViewById(R.id.searchView);
|
||||
int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null);
|
||||
View searchPlate = searchView.findViewById(searchPlateId);
|
||||
if (searchPlate!=null) {
|
||||
|
@ -106,7 +108,18 @@ public class HomeActivity extends AppCompatActivity implements NavigationView.On
|
|||
TextView searchTextView = searchView.findViewById(searchTextId);
|
||||
searchTextView.setTextAppearance(R.style.SearchText);
|
||||
}
|
||||
searchView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
domainAdapter.loadDomains(editable.toString());
|
||||
}
|
||||
});
|
||||
updateLoginMenu();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 4/18/18.
|
||||
*/
|
||||
|
||||
public interface Callback<T> {
|
||||
public void callback(T t);
|
||||
}
|
|
@ -10,9 +10,9 @@ import io.highfidelity.hifiinterface.view.DomainAdapter;
|
|||
|
||||
public interface DomainProvider {
|
||||
|
||||
void retrieve(Callback callback);
|
||||
void retrieve(String filterText, DomainCallback domainCallback);
|
||||
|
||||
interface Callback {
|
||||
interface DomainCallback {
|
||||
void retrieveOk(List<DomainAdapter.Domain> domain);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.MutableInt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.DomainAdapter;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
@ -21,10 +25,18 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
|
||||
public static final String BASE_URL = "https://metaverse.highfidelity.com/";
|
||||
|
||||
private static final String INCLUDE_ACTIONS_FOR_PLACES = "concurrency";
|
||||
private static final String INCLUDE_ACTIONS_FOR_FULL_SEARCH = "concurrency,announcements,snapshot";
|
||||
private static final int MAX_PAGES_TO_GET = 10;
|
||||
|
||||
private String mProtocol;
|
||||
private Retrofit mRetrofit;
|
||||
private UserStoryDomainProviderService mUserStoryDomainProviderService;
|
||||
|
||||
private boolean startedToGetFromAPI = false;
|
||||
private List<UserStory> allStories; // All retrieved stories from the API
|
||||
private List<DomainAdapter.Domain> suggestions; // Filtered places to show
|
||||
|
||||
public UserStoryDomainProvider(String protocol) {
|
||||
mRetrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
|
@ -32,36 +44,144 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
.build();
|
||||
mUserStoryDomainProviderService = mRetrofit.create(UserStoryDomainProviderService.class);
|
||||
mProtocol = protocol;
|
||||
allStories = new ArrayList<>();
|
||||
suggestions = new ArrayList<>();
|
||||
}
|
||||
|
||||
private void fillDestinations(String filterText, DomainCallback domainCallback) {
|
||||
StoriesFilter filter = new StoriesFilter(filterText);
|
||||
final MutableInt counter = new MutableInt(0);
|
||||
allStories.clear();
|
||||
getUserStoryPage(1,
|
||||
e -> {
|
||||
allStories.subList(counter.value, allStories.size()).forEach(userStory -> {
|
||||
// TODO Report error? e
|
||||
filter.filterOrAdd(userStory);
|
||||
// TODO Visibility stuff according to size of suggestions?
|
||||
});
|
||||
if (domainCallback != null) {
|
||||
domainCallback.retrieveOk(suggestions); //ended
|
||||
}
|
||||
},
|
||||
a -> {
|
||||
allStories.forEach(userStory -> {
|
||||
counter.value++;
|
||||
filter.filterOrAdd(userStory);
|
||||
// TODO Visibility stuff according to size of suggestions?
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void handleError(String url, Throwable t, Callback<Exception> restOfPagesCallback) {
|
||||
restOfPagesCallback.callback(new Exception("Error accessing url [" + url + "]", t));
|
||||
}
|
||||
|
||||
private void getUserStoryPage(int pageNumber, Callback<Exception> restOfPagesCallback, Callback<Void> firstPageCallback) {
|
||||
Call<UserStories> userStories = mUserStoryDomainProviderService.getUserStories(
|
||||
INCLUDE_ACTIONS_FOR_PLACES,
|
||||
"open",
|
||||
true,
|
||||
mProtocol,
|
||||
pageNumber);
|
||||
userStories.enqueue(new retrofit2.Callback<UserStories>() {
|
||||
@Override
|
||||
public void onResponse(Call<UserStories> call, Response<UserStories> response) {
|
||||
UserStories data = response.body();
|
||||
allStories.addAll(data.user_stories);
|
||||
if (data.current_page < data.total_pages && data.current_page <= MAX_PAGES_TO_GET) {
|
||||
if (pageNumber == 1 && firstPageCallback!=null) {
|
||||
firstPageCallback.callback(null);
|
||||
}
|
||||
getUserStoryPage(pageNumber + 1, restOfPagesCallback, null);
|
||||
return;
|
||||
}
|
||||
restOfPagesCallback.callback(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UserStories> call, Throwable t) {
|
||||
handleError(call.request().url().toString(), t, restOfPagesCallback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class StoriesFilter {
|
||||
String[] mWords = new String[]{};
|
||||
public StoriesFilter(String filterText) {
|
||||
mWords = filterText.toUpperCase().split("\\s+");
|
||||
}
|
||||
|
||||
private boolean matches(UserStory story) {
|
||||
if (mWords.length<=0) return true;
|
||||
|
||||
boolean res = true;
|
||||
for (String word: mWords) {
|
||||
res = res && story.searchText().contains(word);
|
||||
if (!res) break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private void addToSuggestions(UserStory story) {
|
||||
suggestions.add(story.toDomain());
|
||||
}
|
||||
|
||||
public void filterOrAdd(UserStory story) {
|
||||
if (matches(story)) {
|
||||
addToSuggestions(story);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void filterChoicesByText(String filterText, DomainCallback domainCallback) {
|
||||
suggestions.clear();
|
||||
StoriesFilter storiesFilter = new StoriesFilter(filterText);
|
||||
allStories.forEach(story -> {
|
||||
storiesFilter.filterOrAdd(story);
|
||||
});
|
||||
domainCallback.retrieveOk(suggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieve(Callback callback) {
|
||||
public synchronized void retrieve(String filterText, DomainCallback domainCallback) {
|
||||
if (!startedToGetFromAPI) {
|
||||
startedToGetFromAPI = true;
|
||||
fillDestinations(filterText, domainCallback);
|
||||
} else {
|
||||
filterChoicesByText(filterText, domainCallback);
|
||||
}
|
||||
}
|
||||
|
||||
public void retrieveNot(DomainCallback domainCallback) {
|
||||
// TODO Call multiple pages
|
||||
Call<UserStories> userStories = mUserStoryDomainProviderService.getUserStories(mProtocol);
|
||||
Call<UserStories> userStories = mUserStoryDomainProviderService.getUserStories(
|
||||
INCLUDE_ACTIONS_FOR_PLACES,
|
||||
"open",
|
||||
true,
|
||||
mProtocol,
|
||||
1);
|
||||
|
||||
Log.d("API-USER-STORY-DOMAINS", "Protocol [" + mProtocol + "] include_actions [" + INCLUDE_ACTIONS_FOR_PLACES + "]");
|
||||
userStories.enqueue(new retrofit2.Callback<UserStories>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<UserStories> call, Response<UserStories> response) {
|
||||
UserStories userStories = response.body();
|
||||
if (userStories == null) {
|
||||
callback.retrieveOk(new ArrayList<>(0));
|
||||
domainCallback.retrieveOk(new ArrayList<>(0));
|
||||
}
|
||||
List<DomainAdapter.Domain> domains = new ArrayList<>(userStories.total_entries);
|
||||
userStories.user_stories.forEach(userStory -> {
|
||||
// TODO Proper url creation (it can or can't have hifi
|
||||
// TODO Or use host value from api?
|
||||
domains.add(new DomainAdapter.Domain(
|
||||
userStory.place_name,
|
||||
"hifi://" + userStory.place_name + "/" + userStory.path,
|
||||
userStory.thumbnail_url
|
||||
));
|
||||
domains.add(userStory.toDomain());
|
||||
});
|
||||
callback.retrieveOk(domains);
|
||||
domainCallback.retrieveOk(domains);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UserStories> call, Throwable t) {
|
||||
callback.retrieveError(new Exception(t), t.getMessage());
|
||||
domainCallback.retrieveError(new Exception(t), t.getMessage());
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -69,7 +189,11 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
|
||||
public interface UserStoryDomainProviderService {
|
||||
@GET("api/v1/user_stories")
|
||||
Call<UserStories> getUserStories(@Query("protocol") String protocol);
|
||||
Call<UserStories> getUserStories(@Query("include_actions") String includeActions,
|
||||
@Query("restriction") String restriction,
|
||||
@Query("require_online") boolean requireOnline,
|
||||
@Query("protocol") String protocol,
|
||||
@Query("page") int pageNumber);
|
||||
}
|
||||
|
||||
class UserStory {
|
||||
|
@ -77,6 +201,28 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
String place_name;
|
||||
String path;
|
||||
String thumbnail_url;
|
||||
String place_id;
|
||||
String domain_id;
|
||||
private String searchText;
|
||||
|
||||
// New fields? tags, description
|
||||
|
||||
String searchText() {
|
||||
if (searchText==null) {
|
||||
searchText = place_name == null? "" : place_name.toUpperCase();
|
||||
}
|
||||
return searchText;
|
||||
}
|
||||
DomainAdapter.Domain toDomain() {
|
||||
// TODO Proper url creation (it can or can't have hifi
|
||||
// TODO Or use host value from api?
|
||||
DomainAdapter.Domain domain = new DomainAdapter.Domain(
|
||||
place_name,
|
||||
"hifi://" + place_name + "/" + path,
|
||||
thumbnail_url
|
||||
);
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
class UserStories {
|
||||
|
@ -86,4 +232,5 @@ public class UserStoryDomainProvider implements DomainProvider {
|
|||
int total_entries;
|
||||
List<UserStory> user_stories;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
|||
private LayoutInflater mInflater;
|
||||
private ItemClickListener mClickListener;
|
||||
private String mProtocol;
|
||||
private UserStoryDomainProvider domainProvider;
|
||||
|
||||
public static class Domain {
|
||||
public String name;
|
||||
|
@ -52,14 +53,17 @@ public class DomainAdapter extends RecyclerView.Adapter<DomainAdapter.ViewHolder
|
|||
mContext = c;
|
||||
this.mInflater = LayoutInflater.from(mContext);
|
||||
mProtocol = protocol;
|
||||
loadDomains();
|
||||
domainProvider = new UserStoryDomainProvider(mProtocol);
|
||||
loadDomains("");
|
||||
}
|
||||
|
||||
private void loadDomains() {
|
||||
DomainProvider domainProvider = new UserStoryDomainProvider(mProtocol);
|
||||
domainProvider.retrieve(new DomainProvider.Callback() {
|
||||
|
||||
|
||||
public void loadDomains(String filterText) {
|
||||
domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<Domain> domain) {
|
||||
mDomains = new Domain[domain.size()];
|
||||
mDomains = domain.toArray(mDomains);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
|
|
@ -28,14 +28,14 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="@color/backgroundDark"/>
|
||||
|
||||
<SearchView
|
||||
<EditText
|
||||
android:id="@+id/searchView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="42dp"
|
||||
android:background="@drawable/search_bg"
|
||||
android:gravity="right"
|
||||
android:layout_gravity="right"
|
||||
|
||||
android:gravity="center_vertical|end"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingEnd="32dp"
|
||||
/>
|
||||
<FrameLayout
|
||||
android:id="@android:id/tabcontent"
|
||||
|
@ -50,7 +50,7 @@
|
|||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvDomains"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
Loading…
Reference in a new issue