/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.example.github.ui.search; import com.android.example.github.R; import com.android.example.github.binding.FragmentDataBindingComponent; import com.android.example.github.databinding.SearchFragmentBinding; import com.android.example.github.di.Injectable; import com.android.example.github.ui.common.NavigationController; import com.android.example.github.ui.common.RepoListAdapter; import com.android.example.github.util.AutoClearedValue; import android.arch.lifecycle.LifecycleFragment; import android.arch.lifecycle.ViewModelProvider; import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.databinding.DataBindingComponent; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.FragmentActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import javax.inject.Inject; public class SearchFragment extends LifecycleFragment implements Injectable { @Inject ViewModelProvider.Factory viewModelFactory; @Inject NavigationController navigationController; DataBindingComponent dataBindingComponent = new FragmentDataBindingComponent(this); AutoClearedValue<SearchFragmentBinding> binding; AutoClearedValue<RepoListAdapter> adapter; private SearchViewModel searchViewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { SearchFragmentBinding dataBinding = DataBindingUtil .inflate(inflater, R.layout.search_fragment, container, false, dataBindingComponent); binding = new AutoClearedValue<>(this, dataBinding); return dataBinding.getRoot(); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); searchViewModel = ViewModelProviders.of(this, viewModelFactory).get(SearchViewModel.class); initRecyclerView(); RepoListAdapter rvAdapter = new RepoListAdapter(dataBindingComponent, true, repo -> navigationController.navigateToRepo(repo.owner.login, repo.name)); binding.get().repoList.setAdapter(rvAdapter); adapter = new AutoClearedValue<>(this, rvAdapter); initSearchInputListener(); binding.get().setCallback(() -> searchViewModel.refresh()); } private void initSearchInputListener() { binding.get().input.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_SEARCH) { doSearch(v); return true; } return false; }); binding.get().input.setOnKeyListener((v, keyCode, event) -> { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { doSearch(v); return true; } return false; }); } private void doSearch(View v) { String query = binding.get().input.getText().toString(); // Dismiss keyboard dismissKeyboard(v.getWindowToken()); binding.get().setQuery(query); searchViewModel.setQuery(query); } private void initRecyclerView() { binding.get().repoList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); int lastPosition = layoutManager .findLastVisibleItemPosition(); if (lastPosition == adapter.get().getItemCount() - 1) { searchViewModel.loadNextPage(); } } }); searchViewModel.getResults().observe(this, result -> { binding.get().setSearchResource(result); binding.get().setResultCount((result == null || result.data == null) ? 0 : result.data.size()); adapter.get().replace(result == null ? null : result.data); binding.get().executePendingBindings(); }); searchViewModel.getLoadMoreStatus().observe(this, loadingMore -> { if (loadingMore == null) { binding.get().setLoadingMore(false); } else { binding.get().setLoadingMore(loadingMore.isRunning()); String error = loadingMore.getErrorMessageIfNotHandled(); if (error != null) { Snackbar.make(binding.get().loadMoreBar, error, Snackbar.LENGTH_LONG).show(); } } binding.get().executePendingBindings(); }); } private void dismissKeyboard(IBinder windowToken) { FragmentActivity activity = getActivity(); if (activity != null) { InputMethodManager imm = (InputMethodManager) activity.getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(windowToken, 0); } } }