├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── gallery │ │ └── templates │ │ └── contentful │ │ ├── App.java │ │ ├── activities │ │ ├── AboutActivity.java │ │ ├── GalleryActivity.java │ │ └── MainActivity.java │ │ ├── fragments │ │ ├── GalleryInfoFragment.java │ │ └── SlideFragment.java │ │ ├── gallery │ │ ├── GalleryAdapter.java │ │ ├── SlideFragmentAdapter.java │ │ └── SlideImageAdapter.java │ │ ├── lib │ │ ├── ClientProvider.java │ │ ├── Const.java │ │ ├── Holder.java │ │ ├── Intents.java │ │ ├── LoaderId.java │ │ ├── Preferences.java │ │ ├── TargetAdapter.java │ │ ├── TransitionListenerAdapter.java │ │ └── Utils.java │ │ ├── loaders │ │ ├── AbsLoader.java │ │ └── GalleryListLoader.java │ │ ├── ui │ │ ├── AnimativeToolBar.java │ │ ├── FloatingActionButton.java │ │ ├── GalleryBottomView.java │ │ ├── GridImageView.java │ │ ├── LockableViewPager.java │ │ └── ViewUtils.java │ │ └── vault │ │ ├── Author.java │ │ ├── Gallery.java │ │ ├── GallerySpace.java │ │ └── Image.java │ └── res │ ├── anim │ └── button_raise.xml │ ├── drawable-hdpi │ ├── box.png │ ├── ic_fab_star.png │ └── logo.png │ ├── drawable-xhdpi │ ├── box.png │ ├── ic_fab_star.png │ └── logo.png │ ├── drawable-xxhdpi │ ├── box.png │ ├── ic_fab_star.png │ └── logo.png │ ├── drawable │ ├── about_buttons_divider.xml │ ├── grid_placeholder.xml │ └── info_background.xml │ ├── layout │ ├── activity_about.xml │ ├── activity_gallery.xml │ ├── activity_main.xml │ ├── fragment_gallery_info.xml │ ├── fragment_slide.xml │ ├── grid_item.xml │ └── grid_section.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── transition │ └── change_image_transform.xml │ ├── values-v21 │ ├── integers.xml │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── config.xml │ ├── dimens.xml │ ├── integers.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── sc1.png └── sc2.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | .idea/ 8 | *.iml 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Contentful GmbH - https://www.contentful.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gallery Showcase 2 | 3 | This is an Android application example for the [Contentful][1] gallery space template. 4 | 5 | ## Intro: 6 | 7 | > [Contentful][1] is a content management platform for web applications, mobile apps and connected devices. It allows you to create, edit & manage content in the cloud and publish it anywhere via powerful API. Contentful offers tools for managing editorial teams and enabling cooperation between organizations. 8 | 9 | ## Screenshots 10 | 11 | ![Screenshots](screenshots/sc1.png) ![Screenshots](screenshots/sc2.png) 12 | 13 | ## Usage 14 | 15 | - Create a space with the "Gallery" space template on [Contentful][1] 16 | - Clone this repo 17 | - Enter your Delivery API credentials in the [Const.java][const] file 18 | - \o/ 19 | 20 | ## License 21 | 22 | Copyright (c) 2015 Contentful GmbH. See [LICENSE][2] for further details. 23 | 24 | 25 | [1]: https://www.contentful.com 26 | [2]: LICENSE 27 | [const]: app/src/main/java/gallery/templates/contentful/lib/Const.java -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "gallery.templates.contentful" 7 | minSdkVersion 24 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'androidx.appcompat:appcompat:1.0.0-rc02' 24 | implementation 'androidx.cardview:cardview:1.0.0-rc02' 25 | implementation 'androidx.recyclerview:recyclerview:1.0.0-rc02' 26 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 27 | implementation 'androidx.preference:preference:1.0.0-rc02' 28 | implementation 'androidx.appcompat:appcompat:1.0.0-rc02' 29 | implementation 'androidx.palette:palette:1.0.0-rc02' 30 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0-rc02' 31 | 32 | implementation 'com.squareup.okhttp:okhttp:2.7.5' 33 | implementation 'com.squareup.okhttp:okhttp-urlconnection:2.7.5' 34 | 35 | implementation 'com.jakewharton:butterknife:9.0.0-SNAPSHOT' 36 | annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-SNAPSHOT' 37 | 38 | implementation 'com.squareup.picasso:picasso:2.71828' 39 | 40 | annotationProcessor 'com.contentful.vault:compiler:3.2.1' 41 | annotationProcessor 'com.contentful.vault:core:3.2.1' 42 | 43 | implementation 'com.contentful.vault:core:3.2.1' 44 | 45 | annotationProcessor 'org.parceler:parceler:1.1.11' 46 | implementation 'org.parceler:parceler-api:1.1.11' 47 | } 48 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | 43 | 44 | 45 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/App.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful; 2 | 3 | import android.app.Application; 4 | 5 | import com.contentful.vault.SyncConfig; 6 | import com.contentful.vault.Vault; 7 | 8 | import gallery.templates.contentful.lib.ClientProvider; 9 | import gallery.templates.contentful.vault.GallerySpace; 10 | 11 | public class App extends Application { 12 | private static App instance; 13 | 14 | @Override public void onCreate() { 15 | super.onCreate(); 16 | instance = this; 17 | requestSync(); 18 | } 19 | 20 | public static App get() { 21 | return instance; 22 | } 23 | 24 | public static void requestSync() { 25 | requestSync(false); 26 | } 27 | 28 | public static void requestSync(boolean invalidate) { 29 | Vault.with(get(), GallerySpace.class).requestSync( 30 | SyncConfig.builder() 31 | .setClient(ClientProvider.get()) 32 | .setInvalidate(invalidate) 33 | .build()); 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/activities/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.activities; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import butterknife.ButterKnife; 10 | import butterknife.OnClick; 11 | import gallery.templates.contentful.R; 12 | 13 | public class AboutActivity extends AppCompatActivity { 14 | @Override protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_about); 17 | ButterKnife.bind(this); 18 | 19 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 20 | } 21 | 22 | @SuppressWarnings("unused") 23 | @OnClick({R.id.btn_faq, R.id.btn_feedback, R.id.btn_contact, R.id.btn_license}) 24 | void onClickButton(View view) { 25 | int urlResId; 26 | 27 | switch (view.getId()) { 28 | case R.id.btn_faq: 29 | urlResId = R.string.url_faq; 30 | break; 31 | 32 | case R.id.btn_feedback: 33 | urlResId = R.string.url_feedback; 34 | break; 35 | 36 | case R.id.btn_contact: 37 | urlResId = R.string.url_contact; 38 | break; 39 | 40 | case R.id.btn_license: 41 | urlResId = R.string.url_licensing; 42 | break; 43 | 44 | default: 45 | return; 46 | } 47 | 48 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(urlResId)))); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/activities/GalleryActivity.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.activities; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ArgbEvaluator; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.content.res.ColorStateList; 10 | import android.content.res.Configuration; 11 | import android.graphics.PorterDuff; 12 | import android.graphics.drawable.Drawable; 13 | import android.graphics.drawable.RippleDrawable; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.transition.Transition; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | import android.view.ViewPropertyAnimator; 20 | import android.widget.ImageView; 21 | 22 | import org.parceler.Parcels; 23 | 24 | import androidx.appcompat.app.AppCompatActivity; 25 | import androidx.core.content.ContextCompat; 26 | import butterknife.BindView; 27 | import butterknife.ButterKnife; 28 | import butterknife.OnClick; 29 | import butterknife.OnPageChange; 30 | import gallery.templates.contentful.R; 31 | import gallery.templates.contentful.fragments.GalleryInfoFragment; 32 | import gallery.templates.contentful.fragments.SlideFragment; 33 | import gallery.templates.contentful.gallery.SlideFragmentAdapter; 34 | import gallery.templates.contentful.gallery.SlideImageAdapter; 35 | import gallery.templates.contentful.lib.Const; 36 | import gallery.templates.contentful.lib.Holder; 37 | import gallery.templates.contentful.lib.Intents; 38 | import gallery.templates.contentful.lib.TransitionListenerAdapter; 39 | import gallery.templates.contentful.lib.Utils; 40 | import gallery.templates.contentful.ui.FloatingActionButton; 41 | import gallery.templates.contentful.ui.LockableViewPager; 42 | import gallery.templates.contentful.ui.ViewUtils; 43 | import gallery.templates.contentful.vault.Gallery; 44 | import gallery.templates.contentful.vault.Image; 45 | 46 | public class GalleryActivity extends AppCompatActivity { 47 | private static final ArgbEvaluator EVALUATOR = new ArgbEvaluator(); 48 | 49 | private SlideImageAdapter imageAdapter; 50 | 51 | private SlideFragmentAdapter fragmentAdapter; 52 | 53 | private GalleryInfoFragment galleryInfoFragment; 54 | 55 | private BroadcastReceiver colorizeReceiver; 56 | 57 | private Gallery gallery; 58 | 59 | private Image image; 60 | 61 | @BindView(R.id.photo) ImageView photo; 62 | 63 | @BindView(R.id.star) FloatingActionButton star; 64 | 65 | @BindView(R.id.pager) LockableViewPager viewPager; 66 | 67 | @BindView(R.id.info_container) ViewGroup infoContainer; 68 | 69 | @Override protected void onCreate(Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | setContentView(R.layout.activity_gallery); 72 | ButterKnife.bind(this); 73 | 74 | extractIntentArguments(); 75 | attachGalleryInfoFragment(); 76 | createAdapterInstances(); 77 | setViewPagerAdapter(); 78 | initializeViews(); 79 | setupColorizeReceiver(true); 80 | } 81 | 82 | @Override protected void onDestroy() { 83 | setupColorizeReceiver(false); 84 | super.onDestroy(); 85 | } 86 | 87 | @Override public void onConfigurationChanged(Configuration newConfig) { 88 | super.onConfigurationChanged(newConfig); 89 | int index = viewPager.getCurrentItem(); 90 | setViewPagerAdapter(); 91 | viewPager.setCurrentItem(index); 92 | } 93 | 94 | @Override public void onBackPressed() { 95 | if (Const.HAS_L) { 96 | if (isShowingInitialImage()) { 97 | createAlphaAnimator(star, false).withEndAction(new Runnable() { 98 | @Override public void run() { 99 | GalleryActivity.super.onBackPressed(); 100 | } 101 | }).start(); 102 | } else { 103 | getWindow().setSharedElementReturnTransition(null); 104 | finish(); 105 | } 106 | 107 | return; 108 | } 109 | 110 | super.onBackPressed(); 111 | } 112 | 113 | @OnPageChange(value = R.id.pager, callback = OnPageChange.Callback.PAGE_SCROLLED) 114 | void onPageSelected(int position, float positionOffset, int positionOffsetPixels) { 115 | if (positionOffsetPixels == 0) { 116 | return; 117 | } 118 | 119 | SlideFragment leftFragment = fragmentAdapter.fragmentForPosition(position); 120 | SlideFragment rightFragment = fragmentAdapter.fragmentForPosition(position + 1); 121 | 122 | if (leftFragment != null && rightFragment != null) { 123 | int leftVibrant = leftFragment.getColorVibrant(); 124 | int rightVibrant = rightFragment.getColorVibrant(); 125 | 126 | int leftDarkMuted = leftFragment.getColorDarkMuted(); 127 | int rightDarkMuted = rightFragment.getColorDarkMuted(); 128 | 129 | int vibrant = (Integer) EVALUATOR.evaluate( 130 | positionOffset, leftVibrant, rightVibrant); 131 | 132 | int darkMuted = (Integer) EVALUATOR.evaluate( 133 | positionOffset, leftDarkMuted, rightDarkMuted); 134 | 135 | colorizeButton(vibrant, darkMuted); 136 | } 137 | } 138 | 139 | @OnClick(R.id.star) void onClickStar(View v) { 140 | toggleInfoContainer(v); 141 | } 142 | 143 | private void initializeViews() { 144 | viewPager.setCurrentItem(gallery.images().indexOf(image)); 145 | viewPager.setOffscreenPageLimit(getResources().getInteger( 146 | R.integer.gallery_pager_offscreen_limit)); 147 | 148 | repositionStar(); 149 | ViewUtils.setViewHeight(infoContainer, Const.IMAGE_HEIGHT, true); 150 | 151 | if (Const.HAS_L && Utils.isPortrait(this)) { 152 | ViewUtils.setViewHeight(photo, Const.IMAGE_HEIGHT, false); 153 | photo.setImageDrawable(Holder.get()); 154 | 155 | getWindow().getSharedElementEnterTransition().addListener(new TransitionListenerAdapter() { 156 | @Override public void onTransitionEnd(Transition transition) { 157 | getWindow().getSharedElementEnterTransition().removeListener(this); 158 | photo.setVisibility(View.GONE); 159 | } 160 | }); 161 | } 162 | } 163 | 164 | private void attachGalleryInfoFragment() { 165 | galleryInfoFragment = (GalleryInfoFragment) GalleryInfoFragment 166 | .instantiate(this, GalleryInfoFragment.class.getName(), getIntent().getExtras()); 167 | 168 | getSupportFragmentManager().beginTransaction() 169 | .add(R.id.info_container, galleryInfoFragment) 170 | .commit(); 171 | } 172 | 173 | private void extractIntentArguments() { 174 | Intent intent = getIntent(); 175 | gallery = Parcels.unwrap(intent.getParcelableExtra(Intents.EXTRA_GALLERY)); 176 | image = Parcels.unwrap(intent.getParcelableExtra(Intents.EXTRA_IMAGE)); 177 | } 178 | 179 | private void createAdapterInstances() { 180 | fragmentAdapter = new SlideFragmentAdapter(this, getSupportFragmentManager(), gallery); 181 | imageAdapter = new SlideImageAdapter(gallery.images()); 182 | } 183 | 184 | private void setViewPagerAdapter() { 185 | if (Utils.isPortrait(this)) { 186 | viewPager.setAdapter(fragmentAdapter); 187 | } else { 188 | viewPager.setAdapter(imageAdapter); 189 | } 190 | } 191 | 192 | private void toggleInfoContainer(final View view) { 193 | final boolean show = ViewUtils.isGone(infoContainer); 194 | 195 | if (show) { 196 | SlideFragment slideFragment = fragmentAdapter.fragmentForPosition(viewPager.getCurrentItem()); 197 | 198 | galleryInfoFragment.colorize(slideFragment.getColorDarkMuted(), 199 | slideFragment.getColorVibrant(), slideFragment.getColorLightMuted()); 200 | } 201 | 202 | Animator animator; 203 | Runnable startRunnable = new Runnable() { 204 | @Override public void run() { 205 | star.setEnabled(false); 206 | viewPager.setLocked(show); 207 | } 208 | }; 209 | 210 | Runnable endRunnable = new Runnable() { 211 | @Override public void run() { 212 | star.setEnabled(true); 213 | viewPager.setLocked(show); 214 | } 215 | }; 216 | 217 | int duration = getResources().getInteger(R.integer.toggle_animation_duration); 218 | if (Const.HAS_L) { 219 | float radius = Math.max(photo.getWidth(), photo.getHeight()) * 2.0f; 220 | animator = ViewUtils.toggleViewCircular(view, infoContainer, show, radius, duration, 221 | startRunnable, endRunnable); 222 | } else { 223 | animator = ViewUtils.toggleViewFade(infoContainer, show, duration, 224 | startRunnable, endRunnable); 225 | } 226 | 227 | animator.start(); 228 | } 229 | 230 | private void repositionStar() { 231 | int fabSize = getResources().getDimensionPixelSize(R.dimen.fab_size); 232 | star.setTranslationY(Const.IMAGE_HEIGHT - (fabSize / 2.0f)); 233 | } 234 | 235 | private boolean isShowingInitialImage() { 236 | int originalIndex = gallery.images().indexOf(image); 237 | return originalIndex == viewPager.getCurrentItem(); 238 | } 239 | 240 | private void colorizeButton(int colorNormal, int colorPressed) { 241 | Drawable drawable = ContextCompat.getDrawable(this, R.drawable.info_background); 242 | drawable.setColorFilter(colorNormal, PorterDuff.Mode.MULTIPLY); 243 | 244 | if (Const.HAS_L) { 245 | ColorStateList stateList = new ColorStateList( 246 | new int[][] { 247 | new int[] { } 248 | }, 249 | new int[] { 250 | colorPressed 251 | }); 252 | 253 | RippleDrawable rippleDrawable = new RippleDrawable(stateList, drawable, null); 254 | ViewUtils.setBackground(star, rippleDrawable); 255 | } else { 256 | ViewUtils.setBackground(star, drawable); 257 | } 258 | } 259 | 260 | private void setupColorizeReceiver(boolean register) { 261 | if (register) { 262 | if (colorizeReceiver == null) { 263 | colorizeReceiver = new BroadcastReceiver() { 264 | @Override public void onReceive(Context context, Intent intent) { 265 | Image image = Parcels.unwrap(intent.getParcelableExtra(Intents.EXTRA_IMAGE)); 266 | int vibrant = intent.getIntExtra(Intents.EXTRA_CLR_VIBRANT, 0); 267 | int darkMuted = intent.getIntExtra(Intents.EXTRA_CLR_DARK_MUTED, 0); 268 | colorize(image, vibrant, darkMuted); 269 | } 270 | }; 271 | } 272 | 273 | registerReceiver(colorizeReceiver, new IntentFilter(Intents.ACTION_COLORIZE)); 274 | } else { 275 | unregisterReceiver(colorizeReceiver); 276 | } 277 | } 278 | 279 | private void colorize(Image image, int colorVibrant, int colorDarkMuted) { 280 | if (image.equals(fragmentAdapter.getImage(viewPager.getCurrentItem()))) { 281 | colorizeButton(colorVibrant, colorDarkMuted); 282 | 283 | if (star.getAlpha() == 0) { 284 | long startDelay = getResources().getInteger(R.integer.toggle_animation_duration); 285 | createAlphaAnimator(star, true).setStartDelay(startDelay).start(); 286 | } 287 | } 288 | } 289 | 290 | private ViewPropertyAnimator createAlphaAnimator(View v, boolean show) { 291 | int duration = getResources().getInteger(R.integer.gallery_alpha_duration); 292 | float alpha = show ? 1.0f : 0.0f; 293 | return v.animate().alpha(alpha).setDuration(duration); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.activities; 2 | 3 | import android.app.ActivityOptions; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.graphics.Point; 9 | import android.graphics.drawable.Drawable; 10 | import android.os.Bundle; 11 | import android.view.Display; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.view.WindowManager; 16 | 17 | import com.contentful.vault.Vault; 18 | 19 | import org.parceler.Parcels; 20 | 21 | import java.util.List; 22 | 23 | import androidx.appcompat.app.AppCompatActivity; 24 | import androidx.core.app.ActivityCompat; 25 | import androidx.loader.app.LoaderManager; 26 | import androidx.loader.content.Loader; 27 | import androidx.recyclerview.widget.GridLayoutManager; 28 | import androidx.recyclerview.widget.RecyclerView; 29 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 30 | import butterknife.BindView; 31 | import butterknife.ButterKnife; 32 | import gallery.templates.contentful.App; 33 | import gallery.templates.contentful.R; 34 | import gallery.templates.contentful.gallery.GalleryAdapter; 35 | import gallery.templates.contentful.lib.Const; 36 | import gallery.templates.contentful.lib.Holder; 37 | import gallery.templates.contentful.lib.Intents; 38 | import gallery.templates.contentful.lib.LoaderId; 39 | import gallery.templates.contentful.lib.Utils; 40 | import gallery.templates.contentful.loaders.GalleryListLoader; 41 | import gallery.templates.contentful.ui.AnimativeToolBar; 42 | import gallery.templates.contentful.vault.Gallery; 43 | import gallery.templates.contentful.vault.Image; 44 | 45 | import static gallery.templates.contentful.gallery.GalleryAdapter.ItemViewHolder; 46 | 47 | public class MainActivity extends AppCompatActivity 48 | implements LoaderManager.LoaderCallbacks> { 49 | 50 | private static final int GRID_SPAN_COUNT = App.get().getResources().getInteger(R.integer.gallery_grid_span_count); 51 | 52 | private static final int LOADER_ID = LoaderId.forClass(MainActivity.class); 53 | 54 | private GalleryAdapter adapter; 55 | 56 | private GridLayoutManager layoutManager; 57 | 58 | private BroadcastReceiver reloadReceiver; 59 | 60 | @BindView(R.id.toolbar) AnimativeToolBar toolbar; 61 | 62 | @BindView(R.id.swipe_refresh) SwipeRefreshLayout swipeRefreshLayout; 63 | 64 | @BindView(R.id.recycler) RecyclerView recyclerView; 65 | 66 | @Override protected void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | setContentView(R.layout.activity_main); 69 | ButterKnife.bind(this); 70 | 71 | setSupportActionBar(toolbar); 72 | calculateImageHeight(); 73 | createAdapter(); 74 | initSwipeRefresh(); 75 | initRecycler(); 76 | setupReloadReceiver(true); 77 | getSupportLoaderManager().initLoader(LOADER_ID, null, this); 78 | } 79 | 80 | @Override protected void onResume() { 81 | super.onResume(); 82 | Holder.set(null); 83 | } 84 | 85 | @Override protected void onPause() { 86 | super.onPause(); 87 | } 88 | 89 | @Override public void onDestroy() { 90 | setupReloadReceiver(false); 91 | super.onDestroy(); 92 | } 93 | 94 | @Override public boolean onCreateOptionsMenu(Menu menu) { 95 | getMenuInflater().inflate(R.menu.menu_main, menu); 96 | return true; 97 | } 98 | 99 | @Override public boolean onOptionsItemSelected(MenuItem item) { 100 | int id = item.getItemId(); 101 | 102 | if (id == R.id.action_about) { 103 | startActivity(new Intent(this, AboutActivity.class)); 104 | return true; 105 | } 106 | 107 | return super.onOptionsItemSelected(item); 108 | } 109 | 110 | @Override public Loader> onCreateLoader(int id, Bundle args) { 111 | return new GalleryListLoader(getApplicationContext()); 112 | } 113 | 114 | @Override public void onLoadFinished(Loader> loader, final List data) { 115 | if (swipeRefreshLayout.isRefreshing()) { 116 | swipeRefreshLayout.setRefreshing(false); 117 | } 118 | 119 | if (data != null) { 120 | adapter.setData(data); 121 | adapter.notifyDataSetChanged(); 122 | } 123 | } 124 | 125 | @Override public void onLoaderReset(Loader> loader) { 126 | 127 | } 128 | 129 | private void initSwipeRefresh() { 130 | swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 131 | @Override public void onRefresh() { 132 | App.requestSync(); 133 | } 134 | }); 135 | } 136 | 137 | private void setupReloadReceiver(boolean register) { 138 | if (register) { 139 | if (reloadReceiver == null) { 140 | reloadReceiver = new BroadcastReceiver() { 141 | @Override public void onReceive(Context context, Intent intent) { 142 | getSupportLoaderManager().restartLoader(LOADER_ID, null, MainActivity.this); 143 | } 144 | }; 145 | } 146 | 147 | registerReceiver(reloadReceiver, new IntentFilter(Vault.ACTION_SYNC_COMPLETE)); 148 | } else { 149 | unregisterReceiver(reloadReceiver); 150 | } 151 | } 152 | 153 | private void initRecycler() { 154 | createLayoutManager(); 155 | 156 | recyclerView.setHasFixedSize(true); 157 | recyclerView.setLayoutManager(layoutManager); 158 | recyclerView.setAdapter(adapter); 159 | recyclerView.addOnScrollListener(createRecyclerScrollListener()); 160 | } 161 | 162 | private void createLayoutManager() { 163 | layoutManager = new GridLayoutManager(this, GRID_SPAN_COUNT); 164 | 165 | layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 166 | @Override public int getSpanSize(int position) { 167 | return adapter.isSection(position) ? GRID_SPAN_COUNT : 1; 168 | } 169 | }); 170 | } 171 | 172 | private void createAdapter() { 173 | int imageSize = Utils.getDisplayDimensions(this).x / GRID_SPAN_COUNT; 174 | adapter = new GalleryAdapter(createAdapterClickListener(), imageSize); 175 | } 176 | 177 | private void onClickImage(ItemViewHolder holder, Gallery gallery, Image image) { 178 | Intent intent = new Intent(this, GalleryActivity.class) 179 | .putExtra(Intents.EXTRA_GALLERY, Parcels.wrap(gallery)) 180 | .putExtra(Intents.EXTRA_IMAGE, Parcels.wrap(image)); 181 | 182 | Bundle options = null; 183 | if (Const.HAS_L) { 184 | Drawable drawable = holder.photo.getDrawable(); 185 | if (drawable != null) { 186 | Holder.set(drawable); 187 | 188 | options = ActivityOptions.makeSceneTransitionAnimation(this, holder.photo, 189 | getString(R.string.gallery_photo_hero)).toBundle(); 190 | } 191 | } 192 | 193 | ActivityCompat.startActivity(this, intent, options); 194 | } 195 | 196 | private void calculateImageHeight() { 197 | if (Const.IMAGE_WIDTH == null || Const.IMAGE_HEIGHT == null) { 198 | WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); 199 | Display defaultDisplay = windowManager.getDefaultDisplay(); 200 | Point p = new Point(); 201 | defaultDisplay.getSize(p); 202 | Const.IMAGE_WIDTH = p.x; 203 | Const.IMAGE_HEIGHT = (int) (p.y * 2 / 3.0f); 204 | } 205 | } 206 | 207 | private View.OnClickListener createAdapterClickListener() { 208 | return new View.OnClickListener() { 209 | @Override public void onClick(View v) { 210 | int position = recyclerView.getChildAdapterPosition(v); 211 | int viewType = adapter.getItemViewType(position); 212 | RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(v); 213 | Object item = adapter.getItem(position); 214 | 215 | if (GalleryAdapter.VIEW_TYPE_ITEM == viewType) { 216 | onClickImage((ItemViewHolder) viewHolder, adapter.sectionFor(position), (Image) item); 217 | } 218 | } 219 | }; 220 | } 221 | 222 | private RecyclerView.OnScrollListener createRecyclerScrollListener() { 223 | return new RecyclerView.OnScrollListener() { 224 | @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 225 | } 226 | 227 | @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 228 | if (RecyclerView.SCROLL_STATE_DRAGGING != recyclerView.getScrollState()) { 229 | if (dy > 0 && toolbar.visible()) { 230 | toolbar.hide(); 231 | } else if (dy < 0 && !toolbar.visible()) { 232 | toolbar.show(); 233 | } 234 | } 235 | } 236 | }; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/fragments/GalleryInfoFragment.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.fragments; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import org.parceler.Parcels; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.Fragment; 14 | import butterknife.BindView; 15 | import butterknife.ButterKnife; 16 | import gallery.templates.contentful.R; 17 | import gallery.templates.contentful.lib.Intents; 18 | import gallery.templates.contentful.vault.Gallery; 19 | 20 | public class GalleryInfoFragment extends Fragment { 21 | private Gallery gallery; 22 | 23 | @BindView(R.id.root) ViewGroup root; 24 | 25 | @BindView(R.id.title) TextView title; 26 | 27 | @BindView(R.id.description) TextView description; 28 | 29 | @Override public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | extractIntentArguments(); 32 | } 33 | 34 | @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 35 | @Nullable Bundle savedInstanceState) { 36 | super.onCreateView(inflater, container, savedInstanceState); 37 | return inflater.inflate(R.layout.fragment_gallery_info, container, false); 38 | } 39 | 40 | @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 41 | super.onViewCreated(view, savedInstanceState); 42 | ButterKnife.bind(this, view); 43 | initializeViews(); 44 | } 45 | 46 | private void extractIntentArguments() { 47 | gallery = Parcels.unwrap(getArguments().getParcelable(Intents.EXTRA_GALLERY)); 48 | } 49 | 50 | private void initializeViews() { 51 | title.setText(gallery.title()); 52 | description.setText(gallery.description()); 53 | } 54 | 55 | public void colorize(int colorDarkMuted, int colorVibrant, int colorLightMuted) { 56 | int red = Color.red(colorDarkMuted); 57 | int green = Color.green(colorDarkMuted); 58 | int blue = Color.blue(colorDarkMuted); 59 | 60 | root.setBackgroundColor(Color.argb( 61 | getResources().getInteger(R.integer.gallery_info_bg_alpha), 62 | red, green, blue)); 63 | 64 | title.setTextColor(colorVibrant); 65 | description.setTextColor(colorLightMuted); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/fragments/SlideFragment.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.fragments; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.os.AsyncTask; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import com.squareup.picasso.Picasso; 15 | import com.squareup.picasso.Target; 16 | 17 | import org.parceler.Parcels; 18 | 19 | import androidx.annotation.Nullable; 20 | import androidx.fragment.app.Fragment; 21 | import androidx.palette.graphics.Palette; 22 | import butterknife.ButterKnife; 23 | import butterknife.BindView; 24 | import gallery.templates.contentful.R; 25 | import gallery.templates.contentful.lib.Const; 26 | import gallery.templates.contentful.lib.Intents; 27 | import gallery.templates.contentful.lib.TargetAdapter; 28 | import gallery.templates.contentful.lib.Utils; 29 | import gallery.templates.contentful.ui.ViewUtils; 30 | import gallery.templates.contentful.vault.Image; 31 | 32 | public class SlideFragment extends Fragment implements Palette.PaletteAsyncListener { 33 | private Image image; 34 | 35 | private AsyncTask paletteTask; 36 | 37 | private Target target; 38 | 39 | private Bitmap bitmap; 40 | 41 | private boolean hasPalette; 42 | 43 | private int colorLightMuted; 44 | 45 | private int colorDarkMuted; 46 | 47 | private int colorVibrant; 48 | 49 | @BindView(R.id.photo) ImageView photo; 50 | 51 | @BindView(R.id.bottom) ViewGroup bottomContainer; 52 | 53 | @BindView(R.id.title) TextView title; 54 | 55 | @BindView(R.id.caption) TextView caption; 56 | 57 | public static SlideFragment newSlide(Context context, Image image) { 58 | Bundle b = new Bundle(); 59 | b.putParcelable(Intents.EXTRA_IMAGE, Parcels.wrap(image)); 60 | return (SlideFragment) SlideFragment.instantiate(context, SlideFragment.class.getName(), b); 61 | } 62 | 63 | @Override public void onCreate(Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | extractIntentArguments(); 66 | displayPhoto(); 67 | } 68 | 69 | @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 70 | @Nullable Bundle savedInstanceState) { 71 | super.onCreateView(inflater, container, savedInstanceState); 72 | return inflater.inflate(R.layout.fragment_slide, container, false); 73 | } 74 | 75 | @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 76 | super.onViewCreated(view, savedInstanceState); 77 | ButterKnife.bind(this, view); 78 | ViewUtils.setViewHeight(photo, Const.IMAGE_HEIGHT, true); 79 | title.setText(image.title()); 80 | caption.setText(image.caption()); 81 | applyColor(); 82 | applyImage(); 83 | } 84 | 85 | @Override public void onDestroy() { 86 | cancelPaletteTask(); 87 | if (target != null) { 88 | Picasso.get().cancelRequest(target); 89 | target = null; 90 | } 91 | 92 | bitmap = null; 93 | super.onDestroy(); 94 | } 95 | 96 | @Override public void onGenerated(Palette palette) { 97 | hasPalette = true; 98 | target = null; 99 | int black = getResources().getColor(android.R.color.black); 100 | int white = getResources().getColor(android.R.color.white); 101 | colorDarkMuted = palette.getDarkMutedColor(black); 102 | colorVibrant = palette.getVibrantColor(white); 103 | colorLightMuted = palette.getLightMutedColor(white); 104 | applyColor(); 105 | sendPalette(); 106 | } 107 | 108 | private void extractIntentArguments() { 109 | Bundle b = getArguments(); 110 | image = Parcels.unwrap(b.getParcelable(Intents.EXTRA_IMAGE)); 111 | } 112 | 113 | private void cancelPaletteTask() { 114 | if (paletteTask != null) { 115 | paletteTask.cancel(true); 116 | paletteTask = null; 117 | } 118 | } 119 | 120 | private void sendPalette() { 121 | getActivity().sendBroadcast(attachColors( 122 | new Intent(Intents.ACTION_COLORIZE) 123 | .putExtra(Intents.EXTRA_IMAGE, Parcels.wrap(image)))); 124 | } 125 | 126 | private Intent attachColors(Intent intent) { 127 | intent.putExtra(Intents.EXTRA_CLR_LIGHT_MUTED, colorLightMuted); 128 | intent.putExtra(Intents.EXTRA_CLR_DARK_MUTED, colorDarkMuted); 129 | intent.putExtra(Intents.EXTRA_CLR_VIBRANT, colorVibrant); 130 | return intent; 131 | } 132 | 133 | private void applyImage() { 134 | if (bitmap != null && photo != null) { 135 | photo.setImageBitmap(bitmap); 136 | } 137 | } 138 | 139 | private void applyColor() { 140 | if (hasPalette && bottomContainer != null) { 141 | bottomContainer.setBackgroundColor(colorDarkMuted); 142 | title.setTextColor(colorVibrant); 143 | caption.setTextColor(colorLightMuted); 144 | bottomContainer.animate().alpha(1.0f).setDuration(200).start(); 145 | sendPalette(); 146 | } 147 | } 148 | 149 | private void displayPhoto() { 150 | Picasso.get().load(Utils.imageUrl(image.photo().url())) 151 | .resize(Const.IMAGE_WIDTH, Const.IMAGE_HEIGHT) 152 | .centerCrop() 153 | .into(target = new TargetAdapter() { 154 | @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { 155 | SlideFragment.this.bitmap = bitmap; 156 | applyImage(); 157 | paletteTask = new Palette.Builder(bitmap) 158 | .maximumColorCount(32) 159 | .generate(SlideFragment.this); 160 | } 161 | }); 162 | } 163 | 164 | public int getColorVibrant() { 165 | return colorVibrant; 166 | } 167 | 168 | public int getColorDarkMuted() { 169 | return colorDarkMuted; 170 | } 171 | 172 | public int getColorLightMuted() { 173 | return colorLightMuted; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/gallery/GalleryAdapter.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.gallery; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import com.squareup.picasso.Picasso; 10 | 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import androidx.recyclerview.widget.RecyclerView; 17 | import butterknife.BindView; 18 | import butterknife.ButterKnife; 19 | import gallery.templates.contentful.R; 20 | import gallery.templates.contentful.lib.Utils; 21 | import gallery.templates.contentful.vault.Gallery; 22 | import gallery.templates.contentful.vault.Image; 23 | 24 | public class GalleryAdapter extends RecyclerView.Adapter { 25 | public static final int VIEW_TYPE_SECTION = R.layout.grid_section; 26 | 27 | public static final int VIEW_TYPE_ITEM = R.layout.grid_item; 28 | 29 | private final Map sections; 30 | 31 | private final ArrayList data; 32 | 33 | private final View.OnClickListener itemClickListener; 34 | 35 | private final int imageSize; 36 | 37 | public GalleryAdapter(View.OnClickListener itemClickListener, int imageSize) { 38 | this.itemClickListener = itemClickListener; 39 | this.imageSize = imageSize; 40 | sections = new HashMap<>(); 41 | data = new ArrayList<>(); 42 | } 43 | 44 | @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 45 | View v = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); 46 | v.setOnClickListener(itemClickListener); 47 | 48 | if (VIEW_TYPE_SECTION == viewType) { 49 | return new SectionViewHolder(v); 50 | } 51 | 52 | return new ItemViewHolder(v); 53 | } 54 | 55 | @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 56 | if (isSection(position)) { 57 | onBindSection((SectionViewHolder) holder, position); 58 | } else { 59 | onBindItem((ItemViewHolder) holder, position); 60 | } 61 | } 62 | 63 | @Override public int getItemViewType(int position) { 64 | if (isSection(position)) { 65 | return VIEW_TYPE_SECTION; 66 | } 67 | 68 | return VIEW_TYPE_ITEM; 69 | } 70 | 71 | @Override public int getItemCount() { 72 | return data.size(); 73 | } 74 | 75 | @SuppressWarnings("unchecked") 76 | public T getItem(int position) { 77 | return (T) data.get(position); 78 | } 79 | 80 | public Gallery sectionFor(int position) { 81 | while (position > 0) { 82 | position -= 1; 83 | Gallery gallery = sections.get(position); 84 | if (gallery != null) { 85 | return gallery; 86 | } 87 | } 88 | 89 | return null; 90 | } 91 | 92 | public boolean isSection(int position) { 93 | return sections.get(position) != null; 94 | } 95 | 96 | public void setData(List galleryList) { 97 | clear(); 98 | 99 | for (Gallery gallery : galleryList) { 100 | sections.put(data.size(), gallery); 101 | data.add(gallery); 102 | data.addAll(gallery.images()); 103 | } 104 | } 105 | 106 | public void clear() { 107 | data.clear(); 108 | sections.clear(); 109 | } 110 | 111 | private void onBindSection(SectionViewHolder holder, int position) { 112 | Gallery gallery = (Gallery) data.get(position); 113 | holder.title.setText(gallery.title()); 114 | loadPhoto(holder.cover, gallery.coverImage().url()); 115 | } 116 | 117 | private void onBindItem(ItemViewHolder holder, int position) { 118 | loadPhoto(holder.photo, ((Image) data.get(position)).photo().url()); 119 | } 120 | 121 | private void loadPhoto(ImageView imageView, String url) { 122 | Picasso.get() 123 | .load(Utils.imageUrl(url, imageSize, imageSize)) 124 | .fit() 125 | .centerCrop() 126 | .placeholder(R.drawable.grid_placeholder) 127 | .into(imageView); 128 | } 129 | 130 | public static class ItemViewHolder extends RecyclerView.ViewHolder { 131 | public final View rootView; 132 | 133 | public @BindView(R.id.photo) ImageView photo; 134 | 135 | ItemViewHolder(View itemView) { 136 | super(itemView); 137 | this.rootView = itemView; 138 | ButterKnife.bind(this, itemView); 139 | } 140 | } 141 | 142 | public static class SectionViewHolder extends RecyclerView.ViewHolder { 143 | public final View rootView; 144 | 145 | public @BindView(R.id.title) TextView title; 146 | 147 | public @BindView(R.id.cover) ImageView cover; 148 | 149 | SectionViewHolder(View itemView) { 150 | super(itemView); 151 | this.rootView = itemView; 152 | ButterKnife.bind(this, itemView); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/gallery/SlideFragmentAdapter.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.gallery; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import androidx.fragment.app.Fragment; 9 | import androidx.fragment.app.FragmentManager; 10 | import androidx.fragment.app.FragmentPagerAdapter; 11 | import gallery.templates.contentful.fragments.SlideFragment; 12 | import gallery.templates.contentful.vault.Gallery; 13 | import gallery.templates.contentful.vault.Image; 14 | 15 | public class SlideFragmentAdapter extends FragmentPagerAdapter { 16 | private final Gallery data; 17 | 18 | private final Map fragments; 19 | 20 | public SlideFragmentAdapter(Context context, FragmentManager fm, Gallery gallery) { 21 | super(fm); 22 | this.data = gallery; 23 | this.fragments = new HashMap<>(); 24 | 25 | for (Image image : gallery.images()) { 26 | fragments.put(image, SlideFragment.newSlide(context, image)); 27 | } 28 | } 29 | 30 | public Image getImage(int position) { 31 | return data.images().get(position); 32 | } 33 | 34 | public SlideFragment fragmentForPosition(int position) { 35 | return (SlideFragment) fragments.get(getImage(position)); 36 | } 37 | 38 | @Override public Fragment getItem(int position) { 39 | return fragmentForPosition(position); 40 | } 41 | 42 | @Override public int getCount() { 43 | return data.images().size(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/gallery/SlideImageAdapter.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.gallery; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | 8 | import com.squareup.picasso.Picasso; 9 | 10 | import java.util.List; 11 | 12 | import androidx.viewpager.widget.PagerAdapter; 13 | import gallery.templates.contentful.lib.Utils; 14 | import gallery.templates.contentful.vault.Image; 15 | 16 | public class SlideImageAdapter extends PagerAdapter { 17 | private final List images; 18 | 19 | public SlideImageAdapter(List images) { 20 | this.images = images; 21 | } 22 | 23 | @Override public int getCount() { 24 | return images.size(); 25 | } 26 | 27 | @Override public boolean isViewFromObject(View view, Object object) { 28 | return view == object; 29 | } 30 | 31 | @Override public Object instantiateItem(ViewGroup container, int position) { 32 | Context context = container.getContext(); 33 | ImageView imageView = new ImageView(context); 34 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 35 | Picasso.get().load(Utils.imageUrl(images.get(position).photo().url())).into(imageView); 36 | container.addView(imageView); 37 | return imageView; 38 | } 39 | 40 | @Override public void destroyItem(ViewGroup container, int position, Object object) { 41 | container.removeView((View) object); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/ClientProvider.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import com.contentful.java.cda.CDAClient; 4 | 5 | import static org.apache.commons.lang3.StringUtils.defaultIfBlank; 6 | 7 | public class ClientProvider { 8 | private static final Object LOCK = new Object(); 9 | 10 | private static CDAClient instance; 11 | 12 | private ClientProvider() { 13 | throw new AssertionError(); 14 | } 15 | 16 | public static CDAClient get() { 17 | synchronized (LOCK) { 18 | if (instance == null) { 19 | // Extract credentials from SharedPreferences 20 | String spaceId = defaultIfBlank(Preferences.getSpaceId(), Const.SPACE_ID); 21 | 22 | String accessToken = defaultIfBlank(Preferences.getAccessToken(), Const.ACCESS_TOKEN); 23 | 24 | instance = CDAClient.builder() 25 | .setSpace(spaceId) 26 | .setToken(accessToken) 27 | .build(); 28 | } 29 | 30 | return instance; 31 | } 32 | } 33 | 34 | public static void reset() { 35 | synchronized (LOCK) { 36 | instance = null; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/Const.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import android.os.Build; 4 | 5 | import gallery.templates.contentful.App; 6 | import gallery.templates.contentful.R; 7 | 8 | public class Const { 9 | private Const() { 10 | throw new AssertionError(); 11 | } 12 | 13 | public static final String SPACE_ID = "fbr4i5aajb0w"; 14 | 15 | public static final String ACCESS_TOKEN = 16 | "bb7868adae619627d086f309f987e7765da1fc75336dea1f06c01dc959fc19a3"; 17 | 18 | public static final String CONTENT_TYPE_AUTHOR = "38nK0gXXIccQ2IEosyAg6C"; 19 | 20 | public static final String CONTENT_TYPE_GALLERY = "7leLzv8hW06amGmke86y8G"; 21 | 22 | public static final String CONTENT_TYPE_IMAGE = "1xYw5JsIecuGE68mmGMg20"; 23 | 24 | // CDN Asset 25 | public static final Integer CDN_ASSET_SIZE = 26 | App.get().getResources().getInteger(R.integer.cdn_asset_size); 27 | 28 | public static final boolean HAS_L = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 29 | 30 | public static Integer IMAGE_WIDTH; 31 | 32 | public static Integer IMAGE_HEIGHT; 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/Holder.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import android.graphics.drawable.Drawable; 4 | 5 | public class Holder { 6 | private static Drawable sDrawable; 7 | 8 | private Holder() { 9 | throw new AssertionError(); 10 | } 11 | 12 | public static synchronized void set(Drawable drawable) { 13 | sDrawable = drawable; 14 | } 15 | 16 | public static synchronized Drawable get() { 17 | return sDrawable; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/Intents.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import gallery.templates.contentful.App; 4 | 5 | public class Intents { 6 | private Intents() { 7 | throw new AssertionError(); 8 | } 9 | 10 | private static final String PACKAGE_NAME = App.get().getPackageName(); 11 | 12 | public static final String ACTION_RELOAD = PACKAGE_NAME + ".ACTION_RELOAD"; 13 | 14 | public static final String ACTION_COLORIZE = PACKAGE_NAME + ".ACTION_COLORIZE"; 15 | 16 | public static final String EXTRA_GALLERY = PACKAGE_NAME + ".EXTRA_GALLERY"; 17 | 18 | public static final String EXTRA_IMAGE = PACKAGE_NAME + ".EXTRA_IMAGE"; 19 | 20 | public static final String EXTRA_CLR_LIGHT_MUTED = PACKAGE_NAME + ".EXTRA_CLR_LIGHT_MUTED"; 21 | 22 | public static final String EXTRA_CLR_DARK_MUTED = PACKAGE_NAME + ".EXTRA_CLR_DARK_MUTED"; 23 | 24 | public static final String EXTRA_CLR_VIBRANT = PACKAGE_NAME + ".EXTRA_CLR_VIBRANT"; 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/LoaderId.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class LoaderId { 7 | private static final Map loaders = new HashMap<>(); 8 | 9 | public synchronized static int forClass(Class clazz) { 10 | Integer id = loaders.get(clazz); 11 | if (id == null) { 12 | id = loaders.size(); 13 | loaders.put(clazz, id); 14 | } 15 | return id; 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/Preferences.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import android.content.SharedPreferences; 4 | import android.preference.PreferenceManager; 5 | 6 | import gallery.templates.contentful.App; 7 | 8 | public class Preferences { 9 | public static final String KEY_SPACE_ID = "SPACE_ID"; 10 | 11 | public static final String KEY_ACCESS_TOKEN = "ACCESS_TOKEN"; 12 | 13 | private Preferences() { 14 | throw new AssertionError(); 15 | } 16 | 17 | public static SharedPreferences get() { 18 | return PreferenceManager.getDefaultSharedPreferences(App.get()); 19 | } 20 | 21 | public static String getSpaceId() { 22 | return get().getString(KEY_SPACE_ID, null); 23 | } 24 | 25 | public static String getAccessToken() { 26 | return get().getString(KEY_ACCESS_TOKEN, null); 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/TargetAdapter.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import com.squareup.picasso.Picasso; 7 | import com.squareup.picasso.Target; 8 | 9 | public class TargetAdapter implements Target { 10 | @Override public void onPrepareLoad(Drawable placeHolderDrawable) { 11 | 12 | } 13 | 14 | @Override public void onBitmapFailed(Exception exception, Drawable errorDrawable) { 15 | 16 | } 17 | 18 | @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/TransitionListenerAdapter.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import android.transition.Transition; 4 | 5 | public class TransitionListenerAdapter implements Transition.TransitionListener { 6 | @Override public void onTransitionStart(Transition transition) { 7 | 8 | } 9 | 10 | @Override public void onTransitionEnd(Transition transition) { 11 | 12 | } 13 | 14 | @Override public void onTransitionCancel(Transition transition) { 15 | 16 | } 17 | 18 | @Override public void onTransitionPause(Transition transition) { 19 | 20 | } 21 | 22 | @Override public void onTransitionResume(Transition transition) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/lib/Utils.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.lib; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.res.Configuration; 6 | import android.graphics.Point; 7 | 8 | public class Utils { 9 | private Utils() { 10 | throw new AssertionError(); 11 | } 12 | 13 | public static String imageUrl(String url) { 14 | int size = Const.CDN_ASSET_SIZE; 15 | return imageUrl(url, size, size); 16 | } 17 | 18 | public static String imageUrl(String url, int width, int height) { 19 | String prefix = "?"; 20 | 21 | if (url.contains("?")) { 22 | prefix = "&"; 23 | } 24 | 25 | return url + prefix + "w=" + width + "&h=" + height; 26 | } 27 | 28 | public static boolean isPortrait(Context context) { 29 | return context.getResources().getConfiguration().orientation == 30 | Configuration.ORIENTATION_PORTRAIT; 31 | } 32 | 33 | public static Point getDisplayDimensions(Activity activity) { 34 | Point point = new Point(); 35 | activity.getWindowManager().getDefaultDisplay().getSize(point); 36 | return point; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/loaders/AbsLoader.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.loaders; 2 | 3 | import androidx.loader.content.AsyncTaskLoader; 4 | import gallery.templates.contentful.App; 5 | 6 | /** 7 | * Base loader implementation. 8 | * 9 | * @param result type 10 | */ 11 | public abstract class AbsLoader extends AsyncTaskLoader { 12 | private T result; 13 | 14 | AbsLoader() { 15 | super(App.get()); 16 | } 17 | 18 | @Override public T loadInBackground() { 19 | return result = performLoad(); 20 | } 21 | 22 | @Override protected void onStartLoading() { 23 | if (result != null) { 24 | deliverResult(result); 25 | } 26 | 27 | if (takeContentChanged() || result == null) { 28 | forceLoad(); 29 | } 30 | } 31 | 32 | @Override protected void onReset() { 33 | super.onReset(); 34 | 35 | result = null; 36 | } 37 | 38 | protected abstract T performLoad(); 39 | } -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/loaders/GalleryListLoader.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.loaders; 2 | 3 | import android.content.Context; 4 | 5 | import com.contentful.vault.Vault; 6 | 7 | import java.util.List; 8 | 9 | import gallery.templates.contentful.vault.Gallery; 10 | import gallery.templates.contentful.vault.GallerySpace; 11 | 12 | public class GalleryListLoader extends AbsLoader> { 13 | 14 | Context context; 15 | 16 | public GalleryListLoader(Context context) { 17 | this.context = context; 18 | } 19 | 20 | @Override protected List performLoad() { 21 | return Vault.with(context, GallerySpace.class).fetch(Gallery.class).all(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/ui/AnimativeToolBar.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.ui; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | 7 | import androidx.appcompat.widget.Toolbar; 8 | import gallery.templates.contentful.R; 9 | 10 | public class AnimativeToolBar extends Toolbar { 11 | private boolean visible = true; 12 | 13 | private ValueAnimator animator; 14 | 15 | public AnimativeToolBar(Context context) { 16 | super(context); 17 | } 18 | 19 | public AnimativeToolBar(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public AnimativeToolBar(Context context, AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | public void show() { 28 | animateToolbar(true); 29 | } 30 | 31 | public void hide() { 32 | animateToolbar(false); 33 | } 34 | 35 | public boolean visible() { 36 | return visible; 37 | } 38 | 39 | private void animateToolbar(boolean show) { 40 | if (animator != null) { 41 | animator.cancel(); 42 | } 43 | 44 | final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams(); 45 | int targetMargin = show ? 0 : -getHeight(); 46 | 47 | animator = ValueAnimator.ofInt(layoutParams.topMargin, targetMargin); 48 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 49 | @Override public void onAnimationUpdate(ValueAnimator animation) { 50 | layoutParams.topMargin = (Integer) animation.getAnimatedValue(); 51 | requestLayout(); 52 | } 53 | }); 54 | 55 | animator.setDuration(getResources().getInteger(R.integer.toolbar_animation_duration)).start(); 56 | visible = show; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/ui/FloatingActionButton.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.ui; 2 | 3 | import android.content.Context; 4 | import android.graphics.Outline; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | import android.view.ViewOutlineProvider; 8 | import android.widget.ImageButton; 9 | 10 | import gallery.templates.contentful.lib.Const; 11 | 12 | public class FloatingActionButton extends ImageButton { 13 | public FloatingActionButton(Context context) { 14 | super(context); 15 | } 16 | 17 | public FloatingActionButton(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) { 22 | super(context, attrs, defStyleAttr); 23 | } 24 | 25 | public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr, 26 | int defStyleRes) { 27 | super(context, attrs, defStyleAttr, defStyleRes); 28 | } 29 | 30 | @Override protected void onFinishInflate() { 31 | super.onFinishInflate(); 32 | 33 | if (Const.HAS_L) { 34 | setOutlineProvider(new ViewOutlineProvider() { 35 | @Override 36 | public void getOutline(View view, Outline outline) { 37 | outline.setOval(0, 0, getWidth(), getHeight()); 38 | } 39 | }); 40 | 41 | setClipToOutline(true); 42 | } 43 | } 44 | 45 | @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { 46 | super.onSizeChanged(w, h, oldw, oldh); 47 | 48 | if (Const.HAS_L) { 49 | invalidateOutline(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/ui/GalleryBottomView.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.LinearLayout; 6 | 7 | public class GalleryBottomView extends LinearLayout { 8 | public GalleryBottomView(Context context) { 9 | super(context); 10 | } 11 | 12 | public GalleryBottomView(Context context, AttributeSet attrs) { 13 | super(context, attrs); 14 | } 15 | 16 | public GalleryBottomView(Context context, AttributeSet attrs, int defStyleAttr) { 17 | super(context, attrs, defStyleAttr); 18 | } 19 | 20 | public GalleryBottomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 21 | super(context, attrs, defStyleAttr, defStyleRes); 22 | } 23 | 24 | @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 25 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 26 | int height = getMeasuredHeight(); 27 | 28 | int hms = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); 29 | super.onMeasure(widthMeasureSpec, hms); 30 | 31 | int unspecifiedHeight = getMeasuredHeight(); 32 | if (unspecifiedHeight > height) { 33 | height = unspecifiedHeight; 34 | } 35 | 36 | setMeasuredDimension(getMeasuredWidth(), height); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/ui/GridImageView.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ImageView; 6 | 7 | public class GridImageView extends ImageView { 8 | public GridImageView(Context context) { 9 | super(context); 10 | } 11 | 12 | public GridImageView(Context context, AttributeSet attrs) { 13 | super(context, attrs); 14 | } 15 | 16 | public GridImageView(Context context, AttributeSet attrs, int defStyleAttr) { 17 | super(context, attrs, defStyleAttr); 18 | } 19 | 20 | public GridImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 21 | super(context, attrs, defStyleAttr, defStyleRes); 22 | } 23 | 24 | @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 25 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 26 | setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/ui/LockableViewPager.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.ui; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | 7 | import androidx.viewpager.widget.ViewPager; 8 | 9 | public class LockableViewPager extends ViewPager { 10 | private boolean locked; 11 | 12 | public LockableViewPager(Context context) { 13 | super(context); 14 | } 15 | 16 | public LockableViewPager(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | @Override public boolean onInterceptTouchEvent(MotionEvent ev) { 21 | return !locked && super.onInterceptTouchEvent(ev); 22 | } 23 | 24 | public void setLocked(boolean locked) { 25 | this.locked = locked; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/ui/ViewUtils.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.ui; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ObjectAnimator; 6 | import android.graphics.drawable.Drawable; 7 | import android.os.Build; 8 | import android.view.View; 9 | import android.view.ViewAnimationUtils; 10 | import android.view.animation.AccelerateInterpolator; 11 | import android.view.animation.DecelerateInterpolator; 12 | import android.view.animation.Interpolator; 13 | 14 | import androidx.annotation.Nullable; 15 | 16 | public class ViewUtils { 17 | private static final float INTERPOLATOR_FACTOR = 2.0f; 18 | 19 | private ViewUtils() { 20 | throw new AssertionError(); 21 | } 22 | 23 | @SuppressWarnings("deprecation") 24 | public static void setBackground(View view, Drawable drawable) { 25 | if (Build.VERSION.SDK_INT >= 16) { 26 | view.setBackground(drawable); 27 | } else { 28 | view.setBackgroundDrawable(drawable); 29 | } 30 | } 31 | 32 | public static boolean isGone(View view) { 33 | return view.getVisibility() == View.GONE; 34 | } 35 | 36 | public static void setViewHeight(View view, int height, boolean layout) { 37 | view.getLayoutParams().height = height; 38 | if (layout) { 39 | view.requestLayout(); 40 | } 41 | } 42 | 43 | public static Animator toggleViewFade( 44 | final View target, final boolean show, int duration, 45 | @Nullable final Runnable startRunnable, @Nullable final Runnable endRunnable) { 46 | 47 | Animator animator; 48 | float alpha = show ? 1.0f : 0.0f; 49 | animator = ObjectAnimator.ofFloat(target, "alpha", alpha); 50 | animator.setDuration(duration); 51 | 52 | animator.addListener(new AnimatorListenerAdapter() { 53 | @Override public void onAnimationStart(Animator animation) { 54 | if (show) { 55 | target.setVisibility(View.VISIBLE); 56 | 57 | if (startRunnable != null) { 58 | startRunnable.run(); 59 | } 60 | } 61 | } 62 | 63 | @Override public void onAnimationEnd(Animator animation) { 64 | if (!show) { 65 | target.setVisibility(View.GONE); 66 | } 67 | 68 | if (endRunnable != null) { 69 | endRunnable.run(); 70 | } 71 | } 72 | }); 73 | 74 | return animator; 75 | } 76 | 77 | public static Animator toggleViewCircular(View source, final View target, final boolean show, 78 | float radius, int duration, @Nullable final Runnable startRunnable, @Nullable final Runnable endRunnable) { 79 | 80 | Animator animator; 81 | 82 | // get the center for the clipping circle 83 | int cx = (source.getLeft() + source.getRight()) / 2; 84 | int cy = (source.getTop() + (int) source.getTranslationY() + source.getBottom()); 85 | 86 | float radiusStart = show ? 0 : radius; 87 | float radiusEnd = show ? radius : 0; 88 | 89 | animator = ViewAnimationUtils.createCircularReveal(target, cx, cy, radiusStart, radiusEnd); 90 | animator.setDuration(duration); 91 | animator.setInterpolator(createInterpolator(show)); 92 | 93 | animator.addListener(new AnimatorListenerAdapter() { 94 | @Override public void onAnimationStart(Animator animation) { 95 | if (show) { 96 | target.setVisibility(View.VISIBLE); 97 | 98 | if (startRunnable != null) { 99 | startRunnable.run(); 100 | } 101 | } 102 | } 103 | 104 | @Override public void onAnimationEnd(Animator animation) { 105 | if (!show) { 106 | target.setVisibility(View.GONE); 107 | } 108 | 109 | if (endRunnable != null) { 110 | endRunnable.run(); 111 | } 112 | } 113 | }); 114 | 115 | return animator; 116 | } 117 | 118 | private static Interpolator createInterpolator(boolean show) { 119 | if (show) { 120 | return new AccelerateInterpolator(INTERPOLATOR_FACTOR); 121 | } 122 | 123 | return new DecelerateInterpolator(INTERPOLATOR_FACTOR); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/vault/Author.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.vault; 2 | 3 | import com.contentful.vault.Asset; 4 | import com.contentful.vault.ContentType; 5 | import com.contentful.vault.Field; 6 | import com.contentful.vault.Resource; 7 | 8 | import org.parceler.Parcel; 9 | 10 | import gallery.templates.contentful.lib.Const; 11 | 12 | @Parcel 13 | @ContentType(Const.CONTENT_TYPE_AUTHOR) 14 | public class Author extends Resource { 15 | @Field String name; 16 | 17 | @Field String twitterHandle; 18 | 19 | @Field Asset profilePhoto; 20 | 21 | @Field String biography; 22 | 23 | public String name() { 24 | return name; 25 | } 26 | 27 | public String twitterHandle() { 28 | return twitterHandle; 29 | } 30 | 31 | public Asset profilePhoto() { 32 | return profilePhoto; 33 | } 34 | 35 | public String biography() { 36 | return biography; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/vault/Gallery.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.vault; 2 | 3 | import com.contentful.vault.Asset; 4 | import com.contentful.vault.ContentType; 5 | import com.contentful.vault.Field; 6 | import com.contentful.vault.Resource; 7 | 8 | import org.parceler.Parcel; 9 | 10 | import java.util.List; 11 | 12 | import gallery.templates.contentful.lib.Const; 13 | 14 | @Parcel 15 | @ContentType(Const.CONTENT_TYPE_GALLERY) 16 | public class Gallery extends Resource { 17 | @Field String title; 18 | 19 | @Field String slug; 20 | 21 | @Field Author author; 22 | 23 | @Field Asset coverImage; 24 | 25 | @Field String description; 26 | 27 | @Field List images; 28 | 29 | @Field List tags; 30 | 31 | @Field String date; 32 | 33 | public String title() { 34 | return title; 35 | } 36 | 37 | public String slug() { 38 | return slug; 39 | } 40 | 41 | public Author author() { 42 | return author; 43 | } 44 | 45 | public Asset coverImage() { 46 | return coverImage; 47 | } 48 | 49 | public String description() { 50 | return description; 51 | } 52 | 53 | public List images() { 54 | return images; 55 | } 56 | 57 | public List tags() { 58 | return tags; 59 | } 60 | 61 | public String date() { 62 | return date; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/vault/GallerySpace.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.vault; 2 | 3 | import com.contentful.vault.Space; 4 | 5 | import gallery.templates.contentful.lib.Const; 6 | 7 | @Space(value = Const.SPACE_ID, models = {Author.class, Gallery.class, Image.class}, locales = {"en-US"}) 8 | public class GallerySpace { 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/gallery/templates/contentful/vault/Image.java: -------------------------------------------------------------------------------- 1 | package gallery.templates.contentful.vault; 2 | 3 | import com.contentful.vault.Asset; 4 | import com.contentful.vault.ContentType; 5 | import com.contentful.vault.Field; 6 | import com.contentful.vault.Resource; 7 | 8 | import org.parceler.Parcel; 9 | 10 | import gallery.templates.contentful.lib.Const; 11 | 12 | @Parcel 13 | @ContentType(Const.CONTENT_TYPE_IMAGE) 14 | public class Image extends Resource { 15 | @Field String title; 16 | 17 | @Field Asset photo; 18 | 19 | @Field("imageCaption") 20 | String caption; 21 | 22 | @Field("imageCredits") 23 | String credits; 24 | 25 | public String title() { 26 | return title; 27 | } 28 | 29 | public Asset photo() { 30 | return photo; 31 | } 32 | 33 | public String caption() { 34 | return caption; 35 | } 36 | 37 | public String credits() { 38 | return credits; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/anim/button_raise.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 12 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-hdpi/box.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_fab_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-hdpi/ic_fab_star.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-xhdpi/box.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_fab_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-xhdpi/ic_fab_star.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-xxhdpi/box.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_fab_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-xxhdpi/ic_fab_star.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/gallery-app-android/5188472929d0ac240cee437d86ad60595fd03404/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/about_buttons_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/grid_placeholder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/info_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 25 | 26 | 32 | 33 | 40 | 41 | 42 | 43 |