Coder Social home page Coder Social logo

sunragav / flikrpagination Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 30.9 MB

An Android app that searches images based on text using flickr API and displays them in a recyclerview. The project is designed using android architecture components.

Java 100.00%

flikrpagination's Introduction

FlikrPagination

An Android app that searches images based on text using flickr api and displays them in a recyclerview. The project is designed using android architecture components. The project has been developed using Android Studio 3.0.1. The project is split into two gradle projects, one for UI and the other for data, to clearly separate the concerns.

It uses:

  • Android architecture components Room, LiveData, ViewModel
  • RecyclerView.
  • Glide for image loading and caching.
  • Volley for the web service calls.
  • Java version 1.8 compile options for using lamda expressions in the code

Room, LiveData, ViewModel ( which can withstand configuration changes and activity lifecycles). It uses the MVVM architecture by introducing an abstraction in each three mainlayers of the application namely Presentation, ViewModel(or Business logic) and the repository for the data layer. The Repository layer seemlessly supplies data either from the webservice layer or local persistence layer.

Architecture

Architecture

Code walkthrough

The search query is made using Volley with the search text and the page number as query params.

//RemoteDataSource.java
public void fetch(String searchText, int page) {
        String searchURL = String.format(URL_SEARCH, API_KEY, PAGE_SIZE, page, searchText);
        final JsonObjectRequest jsonObjReq =
                new JsonObjectRequest(Request.Method.GET, searchURL, null,
                        response -> {
                            Log.d(TAG, "Thread->" +
                                    Thread.currentThread().getName() + "\tGot some network response");
                            Log.d(TAG,"Thread->"+Thread.currentThread().getName()+"\n Response:"+response.toString());
                            final ArrayList<FlikrEntity> data = mObjMapper.mapJSONToEntity(response.toString());
                            if (data != null && data.size() > 0)
                                mDataApi.setValue(data);
                        },
                        error -> {
                            Log.d(TAG, "Thread->" +
                                    Thread.currentThread().getName() + "\tGot network error");
                            mError.setValue(error.toString());
                        });

        mQueue.add(jsonObjReq);
 }

The search query response data fetched from webservice (Flickr API) in the RemoteDataSource code snippet shown above, is persisted in the local storage(SQL Lite via Room) and posted to the observers via mDataMerger.post(list) call as shown in the code snippet below. "mDataMerger" is an observable LiveData object. So, as soon as it recieves an update it broadcasts the same to all the observers. In this case, MainActivity is our observer.

//FlikrRepositoryImpl.java
public class FlikrRepositoryImpl implements FlikrRepository {
    private final RemoteDataSource mRemoteDataSource;
    private final LocalDataSource mLocalDataSource;
    private MediatorLiveData<List<FlikrModel>> mDataMerger = new MediatorLiveData<>();
    private MediatorLiveData<String> mErrorMerger = new MediatorLiveData<>();

    private FlikrRepositoryImpl(RemoteDataSource mRemoteDataSource, LocalDataSource mLocalDataSource, FlickrMapper mapper) {
        this.mRemoteDataSource = mRemoteDataSource;
        this.mLocalDataSource = mLocalDataSource;
        mMapper = mapper;
        mDataMerger.addSource(this.mRemoteDataSource.getDataStream(), entities ->
                mExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "mDataMerger\tmRemoteDataSource onChange invoked");
                        mLocalDataSource.writeData(entities);
                    }
                })
        );
        mDataMerger.addSource(this.mLocalDataSource.getDataStream(), entities ->
                mExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "mDataMerger\tmLocalDataSource onChange invoked");
                        List<FlikrModel> models = mMapper.mapEntityToModel(entities);
                        mDataMerger.postValue(models);
                    }
                })

        );

The observable, LiveData, is being actively observed by the view(MainActivity)

//MainActivity.java
 private final Observer<List<FlikrModel>> dataObserver = flikrModels -> updateData(flikrModels);

 private final Observer<String> errorObserver = errorMsg -> setError(errorMsg);
//MainActivity.java
@Override
   protected void onCreate(Bundle savedInstanceState) {
       mViewModel = ViewModelProviders.of(this).get(FlikrViewModel.class);
       mViewModel.getFlikerModels().observe(this, dataObserver);
       mViewModel.getErrorUpdates().observe(this, errorObserver);

and served to the recyclerview adapter of the app as needed.

//MainActivity.java
 @Override
   public void updateData(List<FlikrModel> data) {
       isLoading = false;
       mLastFetchedDataTimeStamp = System.currentTimeMillis();
       mAdapter.setItems(data);

       Log.d(TAG, "Thread->" + Thread.currentThread().getName() + "\nData Size:" + data.size() + "\nAdapter Data Size:" + mAdapter.getItemCount());
       mSwipeRefreshLayout.setRefreshing(false);
   }

   @Override
   public void setError(String msg) {
       showErrorToast(msg);
   }

The data is fetched page by page as long as there is a page that exists for the search. It fetches 15 images per fetch and loads the pages on demand as the user reaches the end of the scroll in the recycler view. This is handled in the recycler view's onScrollListener.

//MainAcitivity.java
recView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                if (dy > 0) //check for scroll down
                {
                    GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
                    int visibleItemCount = layoutManager.getChildCount();
                    int totalItemCount = layoutManager.getItemCount();
                    int pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();

                    if (!isLoading) {
                        if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
                            isLoading = true;
                            Log.d(TAG, "fetchData called for page:" + page);
                            //Do pagination.. i.e. fetch new data
                            mViewModel.fetchData(searchText, page++);
                        }
                    }
                }
            }

Still during configuration changes like phone rotation, the search text the user entered will become empty in the searchView. To retain the search text during configuration changes, I have used the onSaveInstanceState to save it and restored it in the onCreate() of the MainActivity.

//MainActivity.java
@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (searchView != null) {
            searchText = searchView.getQuery().toString();
            outState.putString(SEARCH_KEY, searchText);
        }
    }

Room is used as an ORM tool to interact between the java and the SQL Lite world.

Room

Room

Screenshots

FirstScreen Search Results

Contact

[email protected] +91 8655444565

flikrpagination's People

Contributors

sunragav avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.