diff --git a/app/src/main/java/com/example/yeseul/movieapp/mapper/MovieMapper.java b/app/src/main/java/com/example/yeseul/movieapp/mapper/MovieMapper.java index c8cd8fe..bf3c1d3 100644 --- a/app/src/main/java/com/example/yeseul/movieapp/mapper/MovieMapper.java +++ b/app/src/main/java/com/example/yeseul/movieapp/mapper/MovieMapper.java @@ -8,15 +8,21 @@ public class MovieMapper { public static final String QUERY_STRING_SEARCH_KEY = "query"; public static final String QUERY_STRING_PAGE_UNIT = "display"; public static final String QUERY_STRING_START = "start"; + public static final String QUERY_STRING_YEAR_FROM = "yearfrom"; + public static final String QUERY_STRING_YEAR_TO = "yearto"; - public static Map toRequest(String searchKey, int pageUnit, int start){ + public static Map toRequest(String searchKey, int pageUnit, int start, int yearFrom, int yearTo, boolean isActiveYear) { Map queryMap = new HashMap<>(); queryMap.put(QUERY_STRING_SEARCH_KEY, searchKey); - queryMap.put(QUERY_STRING_PAGE_UNIT, ""+pageUnit); - queryMap.put(QUERY_STRING_START, ""+start); + queryMap.put(QUERY_STRING_PAGE_UNIT, "" + pageUnit); + queryMap.put(QUERY_STRING_START, "" + start); + + if (isActiveYear) { + queryMap.put(QUERY_STRING_YEAR_FROM, "" + yearFrom); + queryMap.put(QUERY_STRING_YEAR_TO, "" + yearTo); + } return queryMap; } - } diff --git a/app/src/main/java/com/example/yeseul/movieapp/view/main/MainActivity.java b/app/src/main/java/com/example/yeseul/movieapp/view/main/MainActivity.java index 574b81f..a84f0e5 100644 --- a/app/src/main/java/com/example/yeseul/movieapp/view/main/MainActivity.java +++ b/app/src/main/java/com/example/yeseul/movieapp/view/main/MainActivity.java @@ -9,6 +9,8 @@ import android.support.v7.widget.RecyclerView; import android.text.Html; import android.text.TextUtils; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import com.example.yeseul.movieapp.R; @@ -56,11 +58,11 @@ private void initView() { binding.recyclerMovie.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); // 최하단 스크롤 감지 - binding.recyclerMovie.setOnScrollListener(new RecyclerView.OnScrollListener(){ + binding.recyclerMovie.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); - if(!binding.recyclerMovie.canScrollVertically(1)){ + if (!binding.recyclerMovie.canScrollVertically(1)) { presenter.loadItems(false); } } @@ -68,7 +70,7 @@ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newStat // 키보드 검색 버튼 리스너 등록 binding.searchBox.etSearch.setOnEditorActionListener((v, actionId, event) -> { - if(actionId == EditorInfo.IME_ACTION_SEARCH){ + if (actionId == EditorInfo.IME_ACTION_SEARCH) { onSearchButtonClicked(); return true; } @@ -77,16 +79,21 @@ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newStat // 검색 버튼 리스너 등록 binding.searchBox.btnSubmit.setOnClickListener(v -> onSearchButtonClicked()); + binding.searchBox.btnYear.setOnClickListener(v -> onYearButtonClicked()); + binding.calenderBox.yearFrom.setOnClickListener(v -> onYearFromClicked()); + binding.calenderBox.yearTo.setOnClickListener(v -> onYearToClicked()); + } /** - * 키보드 검색 혹은 검색 버튼 눌렀을 때 호출 */ - private void onSearchButtonClicked(){ + * 키보드 검색 혹은 검색 버튼 눌렀을 때 호출 + */ + private void onSearchButtonClicked() { // 입력값이 존재 하는지 체크 String searchKey = binding.searchBox.etSearch.getText().toString(); - if(!TextUtils.isEmpty(searchKey)) { + if (!TextUtils.isEmpty(searchKey)) { presenter.onSearchButtonClicked(searchKey); binding.recyclerMovie.scrollToPosition(0); binding.emptyView.setText(""); @@ -97,8 +104,32 @@ private void onSearchButtonClicked(){ } } + private void onYearFromClicked() { + NumberPickerFragment fragment = NumberPickerFragment.newInstance(presenter, 1); + fragment.show(getSupportFragmentManager(), "NumberPicker"); + } + + private void onYearToClicked() { + NumberPickerFragment fragment = NumberPickerFragment.newInstance(presenter, 2); + fragment.show(getSupportFragmentManager(), "NumberPicker"); + } + + private void onYearButtonClicked() { + presenter.isActiveYear.set(!presenter.isActiveYear.get()); + if (presenter.isActiveYear.get()) { + Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_down); + binding.calenderBox.yearLayout.startAnimation(anim); + } else { + Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_up); + binding.calenderBox.yearLayout.startAnimation(anim); + } + + + } + /** - * 검색 결과가 없는 경우 presenter 에 의해 호출됨 */ + * 검색 결과가 없는 경우 presenter 에 의해 호출됨 + */ @Override public void onSearchResultEmpty(String searchKey) { @@ -108,7 +139,8 @@ public void onSearchResultEmpty(String searchKey) { } /** - * 영화 상세 정보 URL 로 연결 */ + * 영화 상세 정보 URL 로 연결 + */ @Override public void startMovieDetailPage(String linkUrl) { @@ -118,4 +150,14 @@ public void startMovieDetailPage(String linkUrl) { customTabsIntent.launchUrl(this, Uri.parse(linkUrl)); } + + @Override + public void updateYearFrom(int yearFrom) { + binding.calenderBox.yearFrom.setText(yearFrom + "년"); + } + + @Override + public void updateYearTo(int yearTo) { + binding.calenderBox.yearTo.setText(yearTo + "년"); + } } diff --git a/app/src/main/java/com/example/yeseul/movieapp/view/main/MainContract.java b/app/src/main/java/com/example/yeseul/movieapp/view/main/MainContract.java index ac42353..80a73ee 100644 --- a/app/src/main/java/com/example/yeseul/movieapp/view/main/MainContract.java +++ b/app/src/main/java/com/example/yeseul/movieapp/view/main/MainContract.java @@ -12,6 +12,10 @@ interface View extends BaseView { void onSearchResultEmpty(String searchKey); void startMovieDetailPage(String linkUrl); + + void updateYearFrom(int yearFrom); + + void updateYearTo(int yearTo); } interface Presenter extends BasePresenter { @@ -21,5 +25,9 @@ interface Presenter extends BasePresenter { void setAdapterView(AdapterContract.View adapterView); void setAdapterModel(AdapterContract.Model adapterModel); + + void onYearFromSelected(int yearFrom); + + void onYearToSelected(int yearTo); } } diff --git a/app/src/main/java/com/example/yeseul/movieapp/view/main/MainPresenter.java b/app/src/main/java/com/example/yeseul/movieapp/view/main/MainPresenter.java index b57c015..d2290c6 100644 --- a/app/src/main/java/com/example/yeseul/movieapp/view/main/MainPresenter.java +++ b/app/src/main/java/com/example/yeseul/movieapp/view/main/MainPresenter.java @@ -1,21 +1,26 @@ package com.example.yeseul.movieapp.view.main; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.databinding.ObservableBoolean; -import android.databinding.ObservableField; +import android.os.Build; import com.example.yeseul.movieapp.data.source.movie.MovieRepository; import com.example.yeseul.movieapp.mapper.MovieMapper; import com.example.yeseul.movieapp.pojo.Movie; import com.example.yeseul.movieapp.view.adapter.AdapterContract; +import java.time.LocalDate; import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; + +@TargetApi(Build.VERSION_CODES.O) public class MainPresenter implements MainContract.Presenter { public final ObservableBoolean isLoading = new ObservableBoolean(false); // 로딩 중 flag 바인딩 + public final ObservableBoolean isActiveYear = new ObservableBoolean(false); private MainContract.View view; private MovieRepository repository; @@ -26,6 +31,8 @@ public class MainPresenter implements MainContract.Presenter { private final int PAGE_UNIT = 20; // 한번에 가져올 데이터 개수 private int currentPage = 0; // 현재 페이지 index private boolean isEndOfPage = false; // 페이지 끝 flag + private int yearFrom = 1896; + private int yearTo = LocalDate.now().getYear(); public MainPresenter(MainContract.View view, MovieRepository repository) { this.view = view; @@ -41,14 +48,14 @@ public void onViewCreated() { public void loadItems(boolean isRefresh) { // refresh true 의 경우 초기화 - if (isRefresh){ + if (isRefresh) { currentPage = 0; isEndOfPage = false; adapterModel.clearItems(); } // 마지막 페이지가 아니고 로딩중 아닌 경우 getMovieList 호출 - if(!isLoading.get() && !isEndOfPage) { + if (!isLoading.get() && !isEndOfPage) { getMovieList(); } } @@ -59,6 +66,18 @@ public void onSearchButtonClicked(String searchKey) { loadItems(true); } + @Override + public void onYearFromSelected(int yearFrom) { + this.yearFrom = yearFrom; + view.updateYearFrom(yearFrom); + } + + @Override + public void onYearToSelected(int yearTo) { + this.yearTo = yearTo; + view.updateYearTo(yearTo); + } + @Override public void setAdapterView(AdapterContract.View adapterView) { this.adapterView = adapterView; @@ -71,12 +90,20 @@ public void setAdapterModel(AdapterContract.Model adapterModel) { this.adapterModel = adapterModel; } + public int getYearFrom() { + return yearFrom; + } + + public int getYearTo() { + return yearTo; + } + @SuppressLint("CheckResult") - private void getMovieList(){ + private void getMovieList() { isLoading.set(true); - repository.searchMovies(MovieMapper.toRequest(searchKey, PAGE_UNIT, (PAGE_UNIT * currentPage++) + 1)) + repository.searchMovies(MovieMapper.toRequest(searchKey, PAGE_UNIT, (PAGE_UNIT * currentPage++) + 1, yearFrom, yearTo, isActiveYear.get())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(response -> { @@ -85,7 +112,7 @@ private void getMovieList(){ List movieList = response.getMovieList(); - if(movieList.size() == 0){ // 검색 결과가 없는 경우 + if (movieList.size() == 0) { // 검색 결과가 없는 경우 // 페이지 끝 flag ON isEndOfPage = true; // 뷰에 알리기 diff --git a/app/src/main/java/com/example/yeseul/movieapp/view/main/NumberPickerFragment.java b/app/src/main/java/com/example/yeseul/movieapp/view/main/NumberPickerFragment.java new file mode 100644 index 0000000..71806c1 --- /dev/null +++ b/app/src/main/java/com/example/yeseul/movieapp/view/main/NumberPickerFragment.java @@ -0,0 +1,78 @@ +package com.example.yeseul.movieapp.view.main; + +import android.annotation.TargetApi; +import android.app.Dialog; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.widget.NumberPicker; +import android.widget.Toast; + +import java.time.LocalDate; + +public class NumberPickerFragment extends DialogFragment { + + private MainPresenter mPresenter; + private int type; + + public static NumberPickerFragment newInstance(MainPresenter presenter,int type) { + + Bundle args = new Bundle(); + + NumberPickerFragment fragment = new NumberPickerFragment(); + args.putInt("type",type); + fragment.setPresenter(presenter); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + type = getArguments() != null ? getArguments().getInt("type") : 0; + } + + + @TargetApi(Build.VERSION_CODES.O) + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final NumberPicker numberPicker = new NumberPicker(getActivity()); + + if(type==1) { + numberPicker.setMinValue(1896); + }else if(type ==2){ + numberPicker.setMinValue(mPresenter.getYearFrom()); + } + numberPicker.setMaxValue(LocalDate.now().getYear()); + + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle("년도를 선택하세요"); + + builder.setPositiveButton("확인", (dialog, which) -> valueChangeListener.onValueChange(numberPicker, + numberPicker.getValue(), numberPicker.getValue())); + + builder.setNegativeButton("취소",null); + + builder.setView(numberPicker); + return builder.create(); + } + + private NumberPicker.OnValueChangeListener valueChangeListener = (numberPicker, oldValue, newValue) -> { + if(type==1) { + mPresenter.onYearFromSelected(numberPicker.getValue()); + } else if(type==2){ + mPresenter.onYearToSelected(numberPicker.getValue()); + } + Toast.makeText(getContext(), "value: " + numberPicker.getValue(), Toast.LENGTH_SHORT).show(); + + }; + + public void setPresenter(MainPresenter presenter) { + this.mPresenter = presenter; + } +} diff --git a/app/src/main/res/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml new file mode 100644 index 0000000..820e5ff --- /dev/null +++ b/app/src/main/res/anim/slide_down.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml new file mode 100644 index 0000000..2af9e4c --- /dev/null +++ b/app/src/main/res/anim/slide_up.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_movie.xml b/app/src/main/res/layout/activity_movie.xml index 5f7f4bb..921f80c 100644 --- a/app/src/main/res/layout/activity_movie.xml +++ b/app/src/main/res/layout/activity_movie.xml @@ -3,9 +3,10 @@ xmlns:bind="http://schemas.android.com/tools"> + + type="com.example.yeseul.movieapp.view.main.MainPresenter" /> + + + android:overScrollMode="never" /> + android:textSize="@dimen/text_size_main" /> + android:layout_gravity="center" + android:visibility="@{presenter.isLoading}" /> diff --git a/app/src/main/res/layout/layout_search_box.xml b/app/src/main/res/layout/layout_search_box.xml index 439c61f..7e11483 100644 --- a/app/src/main/res/layout/layout_search_box.xml +++ b/app/src/main/res/layout/layout_search_box.xml @@ -1,5 +1,6 @@ - + +