├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── Lobster-Regular.ttf │ ├── java │ └── com │ │ └── saulmm │ │ └── openlibra │ │ ├── OnItemClickListener.java │ │ ├── ScrollViewListener.java │ │ ├── activities │ │ ├── DetailActivity.java │ │ └── MainActivity.java │ │ ├── fragments │ │ └── BooksFragment.java │ │ ├── models │ │ ├── Book.java │ │ └── BookList.java │ │ ├── network │ │ └── Api.java │ │ ├── other │ │ ├── CustomAnimatorListener.java │ │ └── CustomTransitionListener.java │ │ └── views │ │ ├── Utils.java │ │ ├── adapters │ │ └── BookAdapter.java │ │ └── views │ │ ├── LobstertextView.java │ │ ├── ObservableScrollView.java │ │ └── RecyclerInsetsDecoration.java │ └── res │ ├── anim │ ├── alpha_off.xml │ ├── alpha_on.xml │ ├── fab_animation.xml │ ├── translate_to_bottom.xml │ ├── translate_up_off.xml │ └── translate_up_on.xml │ ├── drawable-hdpi │ ├── ic_done_white_36dp.png │ ├── ic_file_download_white_36dp.png │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── ic_done_white_36dp.png │ ├── ic_file_download_white_36dp.png │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── ic_done_white_36dp.png │ ├── ic_file_download_white_36dp.png │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── ic_done_white_36dp.png │ ├── ic_file_download_white_36dp.png │ └── ic_launcher.png │ ├── drawable │ ├── card_background.xml │ ├── gradient_black.xml │ └── ripple_round.xml │ ├── layout │ ├── activity_detail.xml │ ├── activity_main.xml │ ├── fragment_books.xml │ └── item_book.xml │ ├── menu │ └── menu_main.xml │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── integers.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #Android generated 2 | bin 3 | gen 4 | lint.xml 5 | lint 6 | 7 | #Eclipse 8 | .project 9 | .classpath 10 | .settings 11 | .checkstyle 12 | 13 | #IntelliJ IDEA 14 | .idea 15 | *.iml 16 | *.ipr 17 | *.iws 18 | classes 19 | gen-external-apklibs 20 | 21 | #gradle 22 | .gradle 23 | local.properties 24 | gradlew 25 | gradlew.bat 26 | gradle/ 27 | build/ 28 | 29 | #vi 30 | *.swp 31 | 32 | #other editors 33 | *.bak 34 | 35 | #Maven 36 | target 37 | release.properties 38 | pom.xml.* 39 | 40 | #Ant 41 | build.xml 42 | ant.properties 43 | local.properties 44 | proguard.cfg 45 | proguard-project.txt 46 | 47 | #Other 48 | .DS_Store 49 | Thumbs.db 50 | tmp 51 | *.tgz 52 | *.lock 53 | *.lck 54 | com_crashlytics_export_strings.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenLibra-Material 2 | 3 | This repositores aims to show examples about material design in a real app, the application is a client of the webpage [OpenLibra](http://www.etnassoft.com/biblioteca/) a spanish web to download open source books. 4 | 5 | ## An example of how implement an adapter using Palette to tint each cell in a RecyclerView. 6 | 7 | This example is implemented with a `RecyclerView` a `GridLayoutManager` and an `Adapter` the adapter loads colors obtained from the `Palette` library of each cell to draw a `View` with the colors. 8 | 9 | ![](https://lh3.googleusercontent.com/-tSvezqWH5Sc/VGnprr1k1DI/AAAAAAAAxLc/U7-jN2Am5yo/w1200-h1064-no/palette_adapter.gif) 10 | 11 | ## Building my own choreography with SharedElements 12 | 13 | Implemented a detail activity working with shared elements and animations, setting the order of each animation that composes the choreograpy, you can follow the material desing principle 'Motion provides meaning', _'All action takes place in a single environment. Objects are presented to the user without breaking the continuity of experience even as they transform and reorganize'_ 14 | 15 | ![](https://lh3.googleusercontent.com/-3L0DTHYEdO8/VIhqAwbNBiI/AAAAAAAAy2Q/wD_yJXvK6Ho/w822-h1482-no/expand_animation.gif) 16 | 17 | ## Setting a RecyclerView insets and made translucent the Toolbar 18 | 19 | Set the RecyclerView insets, to show a little gap between columns and rows, and made the Toolbar translucent, as the last Field Trip app update, reaching a pretty effect. 20 | 21 | ![](https://camo.githubusercontent.com/8410b82d5ed9656c4e4500f7422779baaee3e1f4/68747470733a2f2f676f6f676c6564726976652e636f6d2f686f73742f30423632535a3357524d32523264586c575a6d46736433647653456b) 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.saulmm.openlibra" 9 | minSdkVersion 21 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile 'com.koushikdutta.ion:ion:2.0.0' 23 | compile 'com.android.support:support-v4:23.0.1' 24 | compile 'com.android.support:palette-v7:23.0.1' 25 | compile 'com.android.support:appcompat-v7:23.0.1' 26 | compile 'com.android.support:cardview-v7:23.0.1' 27 | compile 'com.android.support:recyclerview-v7:23.0.1' 28 | compile 'com.jakewharton:butterknife:6.0.0' 29 | 30 | compile fileTree(dir: 'libs', include: ['*.jar']) 31 | } 32 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/saulmm/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/assets/Lobster-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/assets/Lobster-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/OnItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra; 2 | 3 | import android.view.View; 4 | 5 | public interface OnItemClickListener { 6 | 7 | void onClick(View v, int position); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/ScrollViewListener.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra; 2 | 3 | 4 | import android.widget.ScrollView; 5 | 6 | public interface ScrollViewListener { 7 | 8 | void onScrollChanged(ScrollView scrollView, int x, int y, int oldx, int oldy); 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/activities/DetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.activities; 2 | 3 | import android.animation.Animator; 4 | import android.app.Activity; 5 | import android.graphics.Bitmap; 6 | import android.graphics.drawable.BitmapDrawable; 7 | import android.os.Bundle; 8 | import android.support.v7.graphics.Palette; 9 | import android.support.v7.widget.Toolbar; 10 | import android.transition.Transition; 11 | import android.view.View; 12 | import android.view.ViewPropertyAnimator; 13 | import android.view.animation.AnimationUtils; 14 | import android.widget.FrameLayout; 15 | import android.widget.ImageView; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | import com.saulmm.openlibra.R; 20 | import com.saulmm.openlibra.fragments.BooksFragment; 21 | import com.saulmm.openlibra.models.Book; 22 | import com.saulmm.openlibra.other.CustomAnimatorListener; 23 | import com.saulmm.openlibra.other.CustomTransitionListener; 24 | import com.saulmm.openlibra.views.Utils; 25 | 26 | import butterknife.ButterKnife; 27 | import butterknife.InjectView; 28 | 29 | 30 | public class DetailActivity extends Activity { 31 | 32 | // UI Stuff 33 | @InjectView(R.id.card_view) FrameLayout contentCard; 34 | @InjectView(R.id.activity_detail_fab) View fabButton; 35 | @InjectView(R.id.activity_detail_main_container) View mainContaienr; 36 | @InjectView(R.id.activity_detail_titles_container) View titlesContainer; 37 | @InjectView(R.id.activity_detail_toolbar) Toolbar toolbar; 38 | @InjectView(R.id.activity_detail_book_info) LinearLayout bookInfoLayout; 39 | @InjectView(R.id.activity_detail_content) TextView contentTextView; 40 | @InjectView(R.id.activity_detail_rating_title) TextView ratingTextView; 41 | @InjectView(R.id.activity_detail_rating_value) TextView ratingValueTextView; 42 | @InjectView(R.id.activity_detail_summary_title) TextView summaryTitle; 43 | @InjectView(R.id.activity_detail_title) TextView titleTextView; 44 | @InjectView(R.id.activity_detail_subtitle) TextView subtitleTextView; 45 | 46 | private Book selectedBook; 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | 51 | super.onCreate(savedInstanceState); 52 | setContentView(R.layout.activity_detail); 53 | 54 | ButterKnife.inject(this); 55 | 56 | // Recover items from the intent 57 | final int position = getIntent().getIntExtra("position", 0); 58 | selectedBook = (Book) getIntent().getSerializableExtra("selected_book"); 59 | 60 | // Recover book cover from BooksFragment cache 61 | Bitmap bookCoverBitmap = BooksFragment.photoCache.get(position); 62 | ImageView toolbarBookCover = (ImageView) findViewById(R.id.activity_detail_cover); 63 | toolbarBookCover.setImageBitmap(bookCoverBitmap); 64 | 65 | // Fab button 66 | fabButton.setScaleX(0); 67 | fabButton.setScaleY(0); 68 | 69 | // Configure the views setting animation start point 70 | Utils.configureHideYView(contentCard); 71 | Utils.configureHideYView(bookInfoLayout); 72 | Utils.configureHideYView(mainContaienr); 73 | 74 | // Define toolbar as the shared element 75 | toolbar.setBackground(new BitmapDrawable(getResources(), bookCoverBitmap)); 76 | toolbar.setTransitionName("cover" + position); 77 | 78 | // Add a listener to get noticed when the transition ends to animate the fab button 79 | getWindow().getSharedElementEnterTransition().addListener(sharedTransitionListener); 80 | 81 | // Generate palette colors 82 | Palette.generateAsync(bookCoverBitmap, paletteListener); 83 | } 84 | 85 | 86 | /** 87 | * I use a listener to get notified when the enter transition ends, and with that notifications 88 | * build my own coreography built with the elements of the UI 89 | * 90 | * Animations order: 91 | * 92 | * 1. The image is animated automatically by the SharedElementTransition 93 | * 2. The layout that contains the titles 94 | * 3. An alpha transition to show the text of the titles 95 | * 3. A scale animation to show the book info 96 | */ 97 | private CustomTransitionListener sharedTransitionListener = new CustomTransitionListener() { 98 | 99 | @Override 100 | public void onTransitionEnd(Transition transition) { 101 | 102 | super.onTransitionEnd(transition); 103 | 104 | ViewPropertyAnimator showTitleAnimator = Utils.showViewByScale(mainContaienr); 105 | showTitleAnimator.setListener(new CustomAnimatorListener() { 106 | 107 | @Override 108 | public void onAnimationEnd(Animator animation) { 109 | 110 | super.onAnimationEnd(animation); 111 | titlesContainer.startAnimation(AnimationUtils.loadAnimation(DetailActivity.this, R.anim.alpha_on)); 112 | titlesContainer.setVisibility(View.VISIBLE); 113 | 114 | Utils.showViewByScale(fabButton).start(); 115 | Utils.showViewByScale(bookInfoLayout).start(); 116 | } 117 | }); 118 | 119 | showTitleAnimator.start(); 120 | } 121 | }; 122 | 123 | @Override 124 | public void onBackPressed() { 125 | 126 | ViewPropertyAnimator hideTitleAnimator = Utils.hideViewByScaleXY(fabButton); 127 | 128 | titlesContainer.startAnimation(AnimationUtils.loadAnimation(DetailActivity.this, R.anim.alpha_off)); 129 | titlesContainer.setVisibility(View.INVISIBLE); 130 | 131 | Utils.hideViewByScaleY(bookInfoLayout); 132 | 133 | hideTitleAnimator.setListener(new CustomAnimatorListener() { 134 | 135 | @Override 136 | public void onAnimationEnd(Animator animation) { 137 | 138 | ViewPropertyAnimator hideFabAnimator = Utils.hideViewByScaleY(mainContaienr); 139 | hideFabAnimator.setListener(new CustomAnimatorListener() { 140 | 141 | @Override 142 | public void onAnimationEnd(Animator animation) { 143 | 144 | super.onAnimationEnd(animation); 145 | coolBack(); 146 | } 147 | }); 148 | } 149 | }); 150 | 151 | hideTitleAnimator.start(); 152 | } 153 | 154 | private Palette.PaletteAsyncListener paletteListener = new Palette.PaletteAsyncListener() { 155 | 156 | @Override 157 | public void onGenerated(Palette palette) { 158 | 159 | if (palette.getVibrantSwatch() != null) { 160 | 161 | Palette.Swatch vibrantSwatch = palette.getVibrantSwatch(); 162 | mainContaienr.setBackgroundColor(vibrantSwatch.getRgb()); 163 | 164 | getWindow().setStatusBarColor(vibrantSwatch.getRgb()); 165 | getWindow().setNavigationBarColor(vibrantSwatch.getRgb()); 166 | 167 | String content = selectedBook.getContent(); 168 | 169 | if (content != null) 170 | contentTextView.setText(content); 171 | 172 | titleTextView.setText(selectedBook.getTitle()); 173 | subtitleTextView.setTextColor(vibrantSwatch.getTitleTextColor()); 174 | subtitleTextView.setText(selectedBook.getAuthor()); 175 | ratingValueTextView.setText(selectedBook.getRating() + " / 10"); 176 | 177 | summaryTitle.setTextColor(vibrantSwatch.getRgb()); 178 | titleTextView.setTextColor(vibrantSwatch.getTitleTextColor()); 179 | subtitleTextView.setTextColor(vibrantSwatch.getTitleTextColor()); 180 | ratingTextView.setTextColor(vibrantSwatch.getTitleTextColor()); 181 | ratingTextView.setTextColor(vibrantSwatch.getRgb()); 182 | } 183 | } 184 | }; 185 | 186 | private void coolBack() { 187 | 188 | try { 189 | super.onBackPressed(); 190 | 191 | } catch (NullPointerException e) { 192 | 193 | // TODO: workaround 194 | } 195 | 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.activities; 2 | 3 | import android.os.Bundle; 4 | import android.os.Debug; 5 | import android.support.v7.app.ActionBarActivity; 6 | import android.support.v7.widget.Toolbar; 7 | 8 | import com.saulmm.openlibra.R; 9 | import com.saulmm.openlibra.fragments.BooksFragment; 10 | 11 | 12 | public class MainActivity extends ActionBarActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | Debug.startMethodTracing("app"); 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | 20 | Toolbar toolbar = (Toolbar) findViewById(R.id.activity_main_toolbar); 21 | toolbar.setTitle(""); 22 | 23 | BooksFragment booksFragment = (BooksFragment) getFragmentManager().findFragmentById(R.id.activity_main_last_books_fragment); 24 | booksFragment.setToolbar(toolbar); 25 | 26 | setSupportActionBar(toolbar); 27 | 28 | Debug.stopMethodTracing(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/fragments/BooksFragment.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.fragments; 2 | 3 | 4 | import android.app.ActivityOptions; 5 | import android.app.Fragment; 6 | import android.app.ProgressDialog; 7 | import android.content.Intent; 8 | import android.graphics.Bitmap; 9 | import android.os.Bundle; 10 | import android.support.v7.widget.GridLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.support.v7.widget.Toolbar; 13 | import android.util.Log; 14 | import android.util.Pair; 15 | import android.util.SparseArray; 16 | import android.view.LayoutInflater; 17 | import android.view.MotionEvent; 18 | import android.view.View; 19 | import android.view.ViewGroup; 20 | import android.view.animation.AnimationUtils; 21 | import android.widget.ImageView; 22 | 23 | import com.google.gson.Gson; 24 | import com.google.gson.stream.JsonReader; 25 | import com.koushikdutta.async.future.FutureCallback; 26 | import com.koushikdutta.ion.Ion; 27 | import com.saulmm.openlibra.OnItemClickListener; 28 | import com.saulmm.openlibra.R; 29 | import com.saulmm.openlibra.activities.DetailActivity; 30 | import com.saulmm.openlibra.models.Book; 31 | import com.saulmm.openlibra.models.BookList; 32 | import com.saulmm.openlibra.network.Api; 33 | import com.saulmm.openlibra.views.adapters.BookAdapter; 34 | import com.saulmm.openlibra.views.views.RecyclerInsetsDecoration; 35 | 36 | import java.io.StringReader; 37 | import java.util.ArrayList; 38 | 39 | import static android.support.v7.widget.RecyclerView.OnScrollListener; 40 | 41 | public class BooksFragment extends Fragment { 42 | 43 | private static int COLUMNS = 2; 44 | public static SparseArray photoCache = new SparseArray(1); 45 | 46 | private ProgressDialog loadingDialog; 47 | private ArrayList books; 48 | private RecyclerView bookRecycler; 49 | private Toolbar toolbar; 50 | private int toolBarDefaultColor; 51 | private int toolBarSemiTransColor; 52 | 53 | public void setToolbar(Toolbar toolbar) { 54 | 55 | this.toolbar = toolbar; 56 | } 57 | 58 | @Override 59 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 60 | 61 | View rootView = inflater.inflate(R.layout.fragment_books, container, false); 62 | 63 | // Configure the recyclerview 64 | bookRecycler = (RecyclerView) rootView.findViewById(R.id.fragment_last_books_recycler); 65 | bookRecycler.setLayoutManager(new GridLayoutManager(getActivity(), COLUMNS)); 66 | bookRecycler.addItemDecoration(new RecyclerInsetsDecoration(getActivity())); 67 | bookRecycler.setOnScrollListener(recyclerScrollListener); 68 | bookRecycler.setOnTouchListener(new View.OnTouchListener() { 69 | @Override 70 | public boolean onTouch(View v, MotionEvent event) { 71 | return false; 72 | } 73 | }); 74 | 75 | // Init and show progress dialog 76 | loadingDialog = new ProgressDialog(getActivity()); 77 | loadingDialog.setMessage(getResources().getString(R.string.loading_books)); 78 | loadingDialog.show(); 79 | 80 | toolBarDefaultColor = getResources().getColor(R.color.color_primary); 81 | toolBarSemiTransColor = getResources().getColor(R.color.color_primary_trans); 82 | 83 | // Load books from API 84 | Ion.with(getActivity()) 85 | .load(Api.getLastBooks()) 86 | .asString() 87 | .setCallback(booksCallback); 88 | 89 | return rootView; 90 | } 91 | 92 | // In future series this will be implemented with a view model presenter model 93 | private FutureCallback booksCallback = new FutureCallback() { 94 | 95 | 96 | @Override 97 | public void onCompleted(Exception e, String result) { 98 | 99 | if (e == null) { 100 | 101 | // Set the malformed JSON as lenient JsonReader 102 | result = Api.cleanJSON(result); 103 | JsonReader reader = new JsonReader(new StringReader(result)); 104 | reader.setLenient(true); 105 | 106 | // Serialize reader into objects 107 | Gson gson = new Gson(); 108 | BookList bookList = gson.fromJson(reader, BookList.class); 109 | books = bookList.getBooks(); 110 | 111 | BookAdapter recyclerAdapter = new BookAdapter(books); 112 | recyclerAdapter.setOnItemClickListener(recyclerRowClickListener); 113 | 114 | // Update adapter 115 | bookRecycler.setAdapter(recyclerAdapter); 116 | 117 | // Dismiss loading dialog 118 | loadingDialog.dismiss(); 119 | 120 | 121 | } else { 122 | Log.d("[DEBUG]", "BooksFragment onCompleted - ERROR: " + e.getMessage()); 123 | } 124 | } 125 | }; 126 | 127 | private OnItemClickListener recyclerRowClickListener = new OnItemClickListener() { 128 | 129 | @Override 130 | public void onClick(View v, int position) { 131 | 132 | Book selectedBook = books.get(position); 133 | 134 | Intent detailIntent = new Intent(getActivity(), DetailActivity.class); 135 | detailIntent.putExtra("position", position); 136 | detailIntent.putExtra("selected_book", selectedBook); 137 | 138 | ImageView coverImage = (ImageView) v.findViewById(R.id.item_book_img); 139 | ((ViewGroup) coverImage.getParent()).setTransitionGroup(false); 140 | photoCache.put(position, coverImage.getDrawingCache()); 141 | 142 | // Setup the transition to the detail activity 143 | ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(getActivity(), 144 | new Pair(coverImage, "cover" + position)); 145 | 146 | startActivity(detailIntent, options.toBundle()); 147 | } 148 | }; 149 | 150 | 151 | private OnScrollListener recyclerScrollListener = new OnScrollListener() { 152 | public int lastDy; 153 | public boolean flag; 154 | 155 | @Override 156 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 157 | 158 | super.onScrolled(recyclerView, dx, dy); 159 | 160 | if (toolbar == null) 161 | throw new IllegalStateException("BooksFragment has not a reference of the main toolbar"); 162 | 163 | // Is scrolling up 164 | if (dy > 10) { 165 | 166 | if (!flag) { 167 | 168 | showToolbar(); 169 | flag = true; 170 | } 171 | 172 | // is scrolling down 173 | } else if (dy < -10) { 174 | 175 | if (flag) { 176 | 177 | hideToolbar(); 178 | flag = false; 179 | } 180 | } 181 | 182 | lastDy = dy; 183 | } 184 | }; 185 | 186 | private void showToolbar() { 187 | 188 | toolbar.startAnimation(AnimationUtils.loadAnimation(getActivity(), 189 | R.anim.translate_up_off)); 190 | } 191 | 192 | private void hideToolbar() { 193 | 194 | toolbar.startAnimation(AnimationUtils.loadAnimation(getActivity(), 195 | R.anim.translate_up_on)); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/models/Book.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.models; 2 | 3 | import java.io.Serializable; 4 | 5 | public class Book implements Serializable { 6 | 7 | private String title; 8 | private String cover; 9 | private String author; 10 | private String content; 11 | private String publisher; 12 | private String publisher_date; 13 | private String pages; 14 | private String rating; 15 | 16 | public String getContent() { 17 | 18 | if (content != null) { 19 | 20 | content = replaceHtmlEntities(content); 21 | } 22 | 23 | return content; 24 | } 25 | 26 | public String replaceHtmlEntities (String content) { 27 | 28 | return content.replace("á","á") 29 | .replace("é","e") 30 | .replace("copy;","e") 31 | .replace("í","i") 32 | .replace(";shy","i") 33 | .replace("ó","ó") 34 | .replace("sup3;","ó") 35 | .replace("sup3;","ó") 36 | .replace("ñ","ñ") 37 | .replace("Ã&","") 38 | .replace("<ul>", "") 39 | .replace("</li>", "") 40 | .replace("</ul>", "") 41 | .replace("<li>", "\n\t\t"); 42 | 43 | } 44 | 45 | public String getPublisher() { 46 | return publisher; 47 | } 48 | 49 | public String getPublisher_date() { 50 | return publisher_date; 51 | } 52 | 53 | public String getPages() { 54 | return pages; 55 | } 56 | 57 | public String getRating() { 58 | return rating; 59 | } 60 | 61 | public String getTitle() { 62 | return title; 63 | } 64 | 65 | public void setTitle(String title) { 66 | this.title = title; 67 | } 68 | 69 | public String getCover() { 70 | return cover; 71 | } 72 | 73 | public void setCover(String cover) { 74 | this.cover = cover; 75 | } 76 | 77 | public String getAuthor() { 78 | return author; 79 | } 80 | 81 | public void setAuthor(String author) { 82 | this.author = author; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/models/BookList.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.models; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class BookList { 6 | private ArrayList books; 7 | 8 | 9 | public ArrayList getBooks() { 10 | return books; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/network/Api.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.network; 2 | 3 | import android.text.Html; 4 | import android.util.Log; 5 | 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * Created by saulmm on 15/11/14. 11 | */ 12 | public class Api { 13 | 14 | private final static String ENDPOINT = "http://openlibra.com/api/v1/get/"; 15 | 16 | public static String getMostVotedBooks (int n) { 17 | 18 | return String.format("%s%s%d", ENDPOINT, "?criteria=most_voted&num_items=", n); 19 | } 20 | 21 | public static String getLastBooks () { 22 | 23 | return "http://www.etnassoft.com/api/v1/get/?since=last_month&num_items=50&content"; 24 | } 25 | 26 | 27 | public static String cleanJSON(String content) { 28 | 29 | // replace bad json params 30 | content = content.replaceFirst("\\(","{\"books\":"); 31 | content = content.replace(")", "}"); 32 | 33 | // // translate html entities 34 | // content = content 35 | // .replaceAll("á", "á") 36 | // .replaceAll("é", "é") 37 | // .replaceAll("í", "í") 38 | // .replaceAll("ó", "ó") 39 | // .replaceAll("ú", "ú") 40 | // .replaceAll("ñ", "ñ") 41 | // .replaceAll("Â", ""); 42 | // 43 | // // Expression to extract href links 44 | // String hrefExpression = ""; 45 | // 46 | // // replace entities and a tags 47 | // content = String.valueOf(Html.fromHtml(content)); 48 | // content = extractGroups(content, hrefExpression); 49 | 50 | return content; 51 | } 52 | 53 | 54 | public static String extractGroups (String content, String regEx) { 55 | Pattern patron = Pattern.compile(regEx); 56 | Matcher matcher = patron.matcher(content); 57 | 58 | try { 59 | while (matcher.find()) 60 | content= content.replaceFirst(matcher.group(0), matcher.group(1)); 61 | } catch (IndexOutOfBoundsException e) { 62 | Log.e("[ERROR] saulmm.open_libra.other.Utils.extractGroups ", "Index of bounds"); 63 | } 64 | 65 | return content; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/other/CustomAnimatorListener.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.other; 2 | 3 | import android.animation.Animator; 4 | 5 | public class CustomAnimatorListener implements Animator.AnimatorListener { 6 | @Override 7 | public void onAnimationStart(Animator animation) { 8 | 9 | } 10 | 11 | @Override 12 | public void onAnimationEnd(Animator animation) { 13 | 14 | } 15 | 16 | @Override 17 | public void onAnimationCancel(Animator animation) { 18 | 19 | } 20 | 21 | @Override 22 | public void onAnimationRepeat(Animator animation) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/other/CustomTransitionListener.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.other; 2 | 3 | import android.transition.Transition; 4 | 5 | /** 6 | * Created by saulmm on 17/11/14. 7 | */ 8 | public class CustomTransitionListener implements Transition.TransitionListener { 9 | 10 | @Override 11 | public void onTransitionStart(Transition transition) { 12 | 13 | } 14 | 15 | @Override 16 | public void onTransitionEnd(Transition transition) { 17 | 18 | } 19 | 20 | @Override 21 | public void onTransitionCancel(Transition transition) { 22 | 23 | } 24 | 25 | @Override 26 | public void onTransitionPause(Transition transition) { 27 | 28 | } 29 | 30 | @Override 31 | public void onTransitionResume(Transition transition) { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/views/Utils.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.views; 2 | 3 | import android.animation.ArgbEvaluator; 4 | import android.animation.ObjectAnimator; 5 | import android.view.View; 6 | import android.view.ViewPropertyAnimator; 7 | import android.view.animation.PathInterpolator; 8 | 9 | @SuppressWarnings("UnnecessaryLocalVariable") 10 | public class Utils { 11 | 12 | public final static int COLOR_ANIMATION_DURATION = 1000; 13 | public final static int DEFAULT_DELAY = 0; 14 | 15 | /** 16 | * Change the color of a view with an animation 17 | * 18 | * @param v the view to change the color 19 | * @param startColor the color to start animation 20 | * @param endColor the color to end the animation 21 | */ 22 | public static void animateViewColor (View v, int startColor, int endColor) { 23 | 24 | ObjectAnimator animator = ObjectAnimator.ofObject(v, "backgroundColor", 25 | new ArgbEvaluator(), startColor, endColor); 26 | 27 | animator.setInterpolator(new PathInterpolator(0.4f,0f,1f,1f)); 28 | animator.setDuration(COLOR_ANIMATION_DURATION); 29 | animator.start(); 30 | } 31 | 32 | /** 33 | * Scale and set the pivot when the animation will start from 34 | * 35 | * @param v the view to set the pivot 36 | */ 37 | public static void configureHideYView(View v) { 38 | 39 | v.setScaleY(0); 40 | v.setPivotY(0); 41 | } 42 | 43 | /** 44 | * Reduces the X & Y from a view 45 | * 46 | * @param v the view to be scaled 47 | * 48 | * @return the ViewPropertyAnimation to manage the animation 49 | */ 50 | public static ViewPropertyAnimator hideViewByScaleXY(View v) { 51 | 52 | return hideViewByScale(v, DEFAULT_DELAY, 0, 0); 53 | } 54 | 55 | /** 56 | * Reduces the Y from a view 57 | * 58 | * @param v the view to be scaled 59 | * 60 | * @return the ViewPropertyAnimation to manage the animation 61 | */ 62 | public static ViewPropertyAnimator hideViewByScaleY(View v) { 63 | 64 | return hideViewByScale(v, DEFAULT_DELAY, 1, 0); 65 | } 66 | 67 | /** 68 | * Reduces the X from a view 69 | * 70 | * @param v the view to be scaled 71 | * 72 | * @return the ViewPropertyAnimation to manage the animation 73 | */ 74 | public static ViewPropertyAnimator hideViewByScalyInX(View v) { 75 | 76 | return hideViewByScale(v, DEFAULT_DELAY, 0, 1); 77 | } 78 | 79 | /** 80 | * Reduces the X & Y 81 | * 82 | * @param v the view to be scaled 83 | * @param delay to start the animation 84 | * @param x integer to scale 85 | * @param y integer to scale 86 | * 87 | * @return the ViewPropertyAnimation to manage the animation 88 | */ 89 | private static ViewPropertyAnimator hideViewByScale (View v, int delay, int x, int y) { 90 | 91 | ViewPropertyAnimator propertyAnimator = v.animate().setStartDelay(delay) 92 | .scaleX(x).scaleY(y); 93 | 94 | return propertyAnimator; 95 | } 96 | 97 | /** 98 | * Shows a view by scaling 99 | * 100 | * @param v the view to be scaled 101 | * 102 | * @return the ViewPropertyAnimation to manage the animation 103 | */ 104 | public static ViewPropertyAnimator showViewByScale (View v) { 105 | 106 | ViewPropertyAnimator propertyAnimator = v.animate().setStartDelay(DEFAULT_DELAY) 107 | .scaleX(1).scaleY(1); 108 | 109 | return propertyAnimator; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/views/adapters/BookAdapter.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.views.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.support.v7.graphics.Palette; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.RelativeLayout; 14 | import android.widget.TextView; 15 | 16 | import com.koushikdutta.async.future.FutureCallback; 17 | import com.koushikdutta.ion.ImageViewBitmapInfo; 18 | import com.koushikdutta.ion.Ion; 19 | import com.saulmm.openlibra.OnItemClickListener; 20 | import com.saulmm.openlibra.R; 21 | import com.saulmm.openlibra.models.Book; 22 | import com.saulmm.openlibra.views.Utils; 23 | 24 | import java.util.ArrayList; 25 | 26 | /** 27 | * Created by saulmm on 08/12/14. 28 | */ 29 | public class BookAdapter extends RecyclerView.Adapter { 30 | 31 | private final ArrayList books; 32 | private Context context; 33 | private int defaultBackgroundcolor; 34 | private OnItemClickListener onItemClickListener; 35 | private int lastPosition = -1; 36 | private static final int SCALE_DELAY = 30; 37 | 38 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 39 | this.onItemClickListener = onItemClickListener; 40 | } 41 | 42 | public BookAdapter(ArrayList books) { 43 | 44 | this.books = books; 45 | } 46 | 47 | @Override 48 | public BooksViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) { 49 | 50 | View rowView = LayoutInflater.from(viewGroup.getContext()) 51 | .inflate(R.layout.item_book, viewGroup, false); 52 | 53 | this.context = viewGroup.getContext(); 54 | defaultBackgroundcolor = context.getResources().getColor(R.color.book_without_palette); 55 | 56 | return new BooksViewHolder(rowView, onItemClickListener); 57 | } 58 | 59 | @Override 60 | public void onBindViewHolder(final BooksViewHolder booksViewHolder, final int position) { 61 | 62 | final Book currentBook = books.get(position); 63 | booksViewHolder.bookTitle.setText(currentBook.getTitle()); 64 | booksViewHolder.bookAuthor.setText(currentBook.getAuthor()); 65 | booksViewHolder.bookCover.setDrawingCacheEnabled(true); 66 | 67 | Ion.with(context) 68 | .load(books.get(position).getCover()) 69 | .intoImageView(booksViewHolder.bookCover) 70 | .withBitmapInfo() 71 | .setCallback(new FutureCallback() { 72 | @Override 73 | public void onCompleted(Exception e, ImageViewBitmapInfo result) { 74 | 75 | if (e == null && result != null) { 76 | 77 | setCellColors(result.getBitmapInfo().bitmap, booksViewHolder, position); 78 | amimateCell(booksViewHolder); 79 | } 80 | } 81 | }); 82 | } 83 | private static final int MAX_PHOTO_ANIMATION_DELAY = 100; 84 | private long profileHeaderAnimationStartTime = 0; 85 | 86 | private void amimateCell(BooksViewHolder booksViewHolder) { 87 | 88 | int cellPosition = booksViewHolder.getPosition(); 89 | 90 | if (!booksViewHolder.animated) { 91 | 92 | booksViewHolder.animated = true; 93 | booksViewHolder.bookContainer.setScaleY(0); 94 | booksViewHolder.bookContainer.setScaleX(0); 95 | booksViewHolder.bookContainer.animate() 96 | .scaleY(1).scaleX(1) 97 | .setDuration(200) 98 | .setStartDelay(SCALE_DELAY * cellPosition) 99 | .start(); 100 | } 101 | 102 | } 103 | 104 | 105 | public void setCellColors (Bitmap b, final BooksViewHolder viewHolder, final int position) { 106 | 107 | if (b != null) { 108 | Palette.generateAsync(b, new Palette.PaletteAsyncListener() { 109 | 110 | @Override 111 | public void onGenerated(Palette palette) { 112 | 113 | Palette.Swatch vibrantSwatch = palette.getVibrantSwatch(); 114 | 115 | if (vibrantSwatch != null) { 116 | 117 | viewHolder.bookTitle.setTextColor(vibrantSwatch.getTitleTextColor()); 118 | viewHolder.bookAuthor.setTextColor(vibrantSwatch.getTitleTextColor()); 119 | viewHolder.bookCover.setTransitionName("cover" + position); 120 | viewHolder.bookTextcontainer.setOnClickListener(new View.OnClickListener() { 121 | @Override 122 | public void onClick(View v) { 123 | onItemClickListener.onClick(v, position); 124 | } 125 | }); 126 | 127 | Utils.animateViewColor(viewHolder.bookTextcontainer, defaultBackgroundcolor, 128 | vibrantSwatch.getRgb()); 129 | 130 | } else { 131 | 132 | Log.e("[ERROR]", "BookAdapter onGenerated - The VibrantSwatch were null at: " + position); 133 | } 134 | } 135 | }); 136 | } 137 | } 138 | 139 | @Override 140 | public int getItemCount() { 141 | 142 | return books.size(); 143 | } 144 | } 145 | 146 | class BooksViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 147 | 148 | protected LinearLayout bookContainer; 149 | protected RelativeLayout bookTextcontainer; 150 | protected ImageView bookCover; 151 | protected TextView bookTitle; 152 | protected TextView bookAuthor; 153 | private OnItemClickListener onItemClickListener; 154 | protected boolean animated = false; 155 | 156 | public BooksViewHolder(View itemView, OnItemClickListener onItemClickListener) { 157 | 158 | super(itemView); 159 | this.onItemClickListener = onItemClickListener; 160 | 161 | bookContainer = (LinearLayout) itemView.findViewById(R.id.item_book_container); 162 | bookTextcontainer = (RelativeLayout) itemView.findViewById(R.id.item_book_text_container); 163 | bookCover = (ImageView) itemView.findViewById(R.id.item_book_img); 164 | bookTitle = (TextView) itemView.findViewById(R.id.item_book_title); 165 | bookAuthor = (TextView) itemView.findViewById(R.id.item_book_author); 166 | bookCover.setOnClickListener(this); 167 | 168 | } 169 | 170 | @Override 171 | public void onClick(View v) { 172 | 173 | onItemClickListener.onClick(v, getPosition()); 174 | 175 | } 176 | } 177 | 178 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/views/views/LobstertextView.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.views.views; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.util.AttributeSet; 6 | import android.widget.TextView; 7 | 8 | public class LobsterTextView extends TextView { 9 | public LobsterTextView(Context context) { 10 | super(context); 11 | init(context); 12 | } 13 | 14 | public LobsterTextView(Context context, AttributeSet attrs) { 15 | super(context, attrs); 16 | init(context); 17 | } 18 | 19 | public LobsterTextView(Context context, AttributeSet attrs, int defStyleAttr) { 20 | super(context, attrs, defStyleAttr); 21 | init(context); 22 | } 23 | 24 | private void init(Context context) { 25 | Typeface t = Typeface.createFromAsset(context.getAssets(), "Lobster-Regular.ttf"); 26 | this.setTypeface(t); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/views/views/ObservableScrollView.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.views.views; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ScrollView; 6 | 7 | import com.saulmm.openlibra.ScrollViewListener; 8 | 9 | 10 | public class ObservableScrollView extends ScrollView { 11 | 12 | private ScrollViewListener scrollViewListener = null; 13 | 14 | public ObservableScrollView(Context context) { 15 | super(context); 16 | } 17 | 18 | public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) { 19 | super(context, attrs, defStyle); 20 | } 21 | 22 | public ObservableScrollView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | } 25 | 26 | public void setScrollViewListener(ScrollViewListener scrollViewListener) { 27 | this.scrollViewListener = scrollViewListener; 28 | } 29 | 30 | @Override 31 | protected void onScrollChanged(int x, int y, int oldx, int oldy) { 32 | super.onScrollChanged(x, y, oldx, oldy); 33 | if(scrollViewListener != null) { 34 | scrollViewListener.onScrollChanged(this, x, y, oldx, oldy); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/saulmm/openlibra/views/views/RecyclerInsetsDecoration.java: -------------------------------------------------------------------------------- 1 | package com.saulmm.openlibra.views.views; 2 | 3 | import android.content.Context; 4 | import android.graphics.Rect; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | import com.saulmm.openlibra.R; 9 | 10 | 11 | /** 12 | * ItemDecoration implementation that applies an inset margin 13 | * around each child of the RecyclerView. The inset value is controlled 14 | * by a dimension resource. 15 | * 16 | * by Dave Smith at: https://github.com/devunwired/recyclerview-playground 17 | */ 18 | public class RecyclerInsetsDecoration extends RecyclerView.ItemDecoration { 19 | 20 | private int mInsets; 21 | 22 | public RecyclerInsetsDecoration(Context context) { 23 | mInsets = context.getResources().getDimensionPixelSize(R.dimen.insets); 24 | } 25 | 26 | @Override 27 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 28 | 29 | //We can supply forced insets for each item view here in the Rect 30 | super.getItemOffsets(outRect, view, parent, state); 31 | outRect.set(mInsets, mInsets, mInsets, mInsets); 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/alpha_off.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/anim/alpha_on.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fab_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 13 | 14 | 15 | 18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_up_off.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_up_on.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_done_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-hdpi/ic_done_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_file_download_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-hdpi/ic_file_download_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_done_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-mdpi/ic_done_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_file_download_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-mdpi/ic_file_download_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_done_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-xhdpi/ic_done_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_file_download_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-xhdpi/ic_file_download_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_done_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-xxhdpi/ic_done_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_file_download_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-xxhdpi/ic_file_download_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saulmm/OpenLibra-Material/285fcca8fe50a0deb6f3034c5f4918537a4edb5c/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/card_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient_black.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ripple_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 16 | 17 | 23 | 24 | 30 | 31 | 39 | 40 | 49 | 50 | 60 | 61 | 73 | 74 | 75 | 76 | 77 | 78 | 92 | 93 | 104 | 105 | 106 | 107 | 117 | 118 | 125 | 126 | 135 | 136 | 143 | 144 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 18 | 19 | 26 | 27 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_books.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_book.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 22 | 23 | 30 | 31 | 42 | 43 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #1C1D23 5 | #ffeae1da 6 | 7 | 8 | #F50057 9 | #33f50057 10 | #AE0554 11 | #6734BA 12 | #ff232323 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 200dp 6 | 55dp 7 | 1dp 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 300 4 | 300 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenLibra 5 | Settings 6 | 7 | 8 | Loading books\nplease wait 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc mollis nunc massa, quis suscipit orci mattis a. Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum. Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum. Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum, Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum, Morbi ac dui in lacus finibus hendrerit. Ut vestibulum, Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum, Ut vestibulum, Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum, Morbi ac dui in lacus finibus hendrerit. Ut vestibulum, Nulla rutrum fringilla finibus. Morbi ac dui in lacus finibus hendrerit. Ut vestibulum 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------