RepoPilotOpen in app →

rubensousa/ViewPagerCards

ViewPager cards inspired by Duolingo

Mixed

Solo project — review before adopting

weakest axis
Use as dependencyMixed

single-maintainer (no co-maintainers visible); no CI workflows detected

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 8w ago
  • Apache-2.0 licensed
  • Tests present
Show all 5 evidence items →
  • Solo or near-solo (1 contributor active in recent commits)
  • No CI workflows detected
What would change the summary?
  • Use as dependency MixedHealthy if: onboard a second core maintainer

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/rubensousa/viewpagercards?axis=fork)](https://repopilot.app/r/rubensousa/viewpagercards)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/rubensousa/viewpagercards on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: rubensousa/ViewPagerCards

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rubensousa/ViewPagerCards shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Solo project — review before adopting

  • Last commit 8w ago
  • Apache-2.0 licensed
  • Tests present
  • ⚠ Solo or near-solo (1 contributor active in recent commits)
  • ⚠ No CI workflows detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live rubensousa/ViewPagerCards repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/rubensousa/ViewPagerCards.

What it runs against: a local clone of rubensousa/ViewPagerCards — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in rubensousa/ViewPagerCards | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 89 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>rubensousa/ViewPagerCards</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rubensousa/ViewPagerCards. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/rubensousa/ViewPagerCards.git
#   cd ViewPagerCards
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of rubensousa/ViewPagerCards and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rubensousa/ViewPagerCards(\\.git)?\\b" \\
  && ok "origin remote is rubensousa/ViewPagerCards" \\
  || miss "origin remote is not rubensousa/ViewPagerCards (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java" \\
  && ok "app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java" \\
  || miss "missing critical file: app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java"
test -f "app/src/main/java/com/github/rubensousa/viewpagercards/ShadowTransformer.java" \\
  && ok "app/src/main/java/com/github/rubensousa/viewpagercards/ShadowTransformer.java" \\
  || miss "missing critical file: app/src/main/java/com/github/rubensousa/viewpagercards/ShadowTransformer.java"
test -f "app/src/main/java/com/github/rubensousa/viewpagercards/CardPagerAdapter.java" \\
  && ok "app/src/main/java/com/github/rubensousa/viewpagercards/CardPagerAdapter.java" \\
  || miss "missing critical file: app/src/main/java/com/github/rubensousa/viewpagercards/CardPagerAdapter.java"
test -f "app/src/main/java/com/github/rubensousa/viewpagercards/CardFragmentPagerAdapter.java" \\
  && ok "app/src/main/java/com/github/rubensousa/viewpagercards/CardFragmentPagerAdapter.java" \\
  || miss "missing critical file: app/src/main/java/com/github/rubensousa/viewpagercards/CardFragmentPagerAdapter.java"
test -f "app/src/main/res/layout/activity_main.xml" \\
  && ok "app/src/main/res/layout/activity_main.xml" \\
  || miss "missing critical file: app/src/main/res/layout/activity_main.xml"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 89 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~59d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/rubensousa/ViewPagerCards"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

ViewPagerCards is an Android library that implements a card-based ViewPager UI pattern inspired by Duolingo's lesson cards. It provides smooth scrolling animations with shadow effects and scaling transformations as users swipe through cards, supporting both Fragment-based (CardFragmentPagerAdapter) and View-based (CardPagerAdapter) implementations. Single-module Android app structure with core logic in app/src/main/java/com/github/rubensousa/viewpagercards/: adapters (CardPagerAdapter, CardFragmentPagerAdapter, CardAdapter), a ShadowTransformer for view animations, CardItem data model, and demo implementations (CardFragment, MainActivity). Layout XMLs in app/src/main/res/layout/ define the card views.

👥Who it's for

Android app developers building premium feature showcases, app intros, or image galleries who want Duolingo-style card animations without building the transformation logic from scratch.

🌱Maturity & risk

This project is abandoned and no longer maintained, as stated in the README. It targets API 14+ with compileSdkVersion 25 (Android 7.1), lacks any visible test automation (only one empty ApplicationTest.java), and has no CI/CD setup. It should be treated as a reference implementation or starting point, not production code.

High risk: single maintainer with no recent commits (project explicitly unmaintained), outdated Support Library dependencies (v25.1.0 from 2016), no test coverage, and zero dependency pinning in build.gradle. The Android support libraries have since been replaced with AndroidX. Not suitable for new projects without significant refactoring.

Active areas of work

Nothing—project is dormant. The README explicitly states 'this project is no longer being maintained'. No active development, pull requests, or issue resolution is occurring.

🚀Get running

Clone the repo: git clone https://github.com/rubensousa/ViewPagerCards.git && cd ViewPagerCards. Open in Android Studio. Sync gradle dependencies by running ./gradlew build. Run on an emulator/device via ./gradlew installDebug or press Run in Android Studio.

Daily commands: Open the project in Android Studio (API 25+ emulator or Android 7.1+ device recommended). Click 'Run' or execute ./gradlew installDebug from terminal, then launch the app. MainActivity displays two demo scenarios: View-based paging (CardPagerAdapter) and Fragment-based paging (CardFragmentPagerAdapter) at app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java.

🗺️Map of the codebase

  • app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java — Entry point that initializes the ViewPager with card adapters and demonstrates the core UI pattern
  • app/src/main/java/com/github/rubensousa/viewpagercards/ShadowTransformer.java — Core PageTransformer that applies the stacked card effect and shadow animations—the defining visual feature
  • app/src/main/java/com/github/rubensousa/viewpagercards/CardPagerAdapter.java — Adapter for regular Views; developers must extend this to populate card content
  • app/src/main/java/com/github/rubensousa/viewpagercards/CardFragmentPagerAdapter.java — Adapter for Fragments; alternative adapter pattern for dynamic card-based UIs
  • app/src/main/res/layout/activity_main.xml — Layout template showing proper ViewPager setup for the card effect pattern
  • app/build.gradle — Defines SDK target (25), support library versions, and CardView dependency required for cards

🧩Components & responsibilities

  • MainActivity (Activity, ViewPager, PagerAdapter) — Orchestrates lifecycle, creates adapters, attaches ViewPager and transformer, handles user interactions
    • Failure mode: App crashes if adapter is null or ViewPager not found in layout
  • CardPagerAdapter / CardFragmentPagerAdapter (PagerAdapter, FragmentPagerAdapter, CardItem data model) — Populates ViewPager with View or Fragment instances; manages card data and recycling
    • Failure mode: Blank or corrupted card display if instantiateItem/getItem returns null or malformed Views
  • ShadowTransformer (PageTransformer, View.setScale) — Animates card scale, translation, and shadow elevation as user swipes between pages

🛠️How to make changes

Add a new card (View-based)

  1. Create a new CardItem data class in CardItem.java with your card content properties (app/src/main/java/com/github/rubensousa/viewpagercards/CardItem.java)
  2. Extend CardPagerAdapter and override instantiateItem() to inflate and populate your card layout (app/src/main/java/com/github/rubensousa/viewpagercards/CardPagerAdapter.java)
  3. Create a new layout resource file (e.g., adapter.xml) with your card UI structure (app/src/main/res/layout/adapter.xml)
  4. In MainActivity, instantiate your adapter and set it on the ViewPager; attach ShadowTransformer (app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java)

Add a new card (Fragment-based)

  1. Create a new Fragment class extending CardFragment with your card content (app/src/main/java/com/github/rubensousa/viewpagercards/CardFragment.java)
  2. Design the Fragment layout in fragment_adapter.xml (app/src/main/res/layout/fragment_adapter.xml)
  3. Extend CardFragmentPagerAdapter and override getItem() to return your Fragment instances (app/src/main/java/com/github/rubensousa/viewpagercards/CardFragmentPagerAdapter.java)
  4. Set the adapter on the ViewPager in MainActivity and apply ShadowTransformer (app/src/main/java/com/github/rubensousa/viewpagercards/MainActivity.java)

Customize the card stack animation

  1. Open ShadowTransformer and modify the scale factors, translation values, and shadow elevations (app/src/main/java/com/github/rubensousa/viewpagercards/ShadowTransformer.java)
  2. Adjust card dimensions and margins in dimens.xml to control spacing (app/src/main/res/values/dimens.xml)
  3. Update colors.xml if you want different shadow or card background colors (app/src/main/res/values/colors.xml)

🔧Why these technologies

  • ViewPager (Android Support Library v25) — Enables smooth horizontal card swiping and page-by-page navigation with built-in lifecycle management
  • PageTransformer (ShadowTransformer) — Applies per-page transformations during scroll to create the stacked card illusion without custom ViewGroup logic
  • CardView — Provides elevation and shadow rendering for the card appearance
  • PagerAdapter / FragmentPagerAdapter — Abstracts card instantiation and lifecycle; supports both View and Fragment-based cards

⚖️Trade-offs already made

  • Single Activity with ViewPager instead of Fragment-based nav

    • Why: Simpler demo; ViewPager handles horizontal pagination naturally
    • Consequence: Limited back-stack management; all card state lives in adapters
  • PageTransformer-based animation over custom LayoutManager

    • Why: Reuses framework APIs; less code; intuitive offset-based math
    • Consequence: Less control over recycling; harder to do complex per-card animation sequences
  • Support API 14+ (minSdkVersion 14)

    • Why: Broad device coverage for a 2016 sample project
    • Consequence: Cannot use modern Jetpack Compose or newer animation APIs

🚫Non-goals (don't propose these)

  • Not a library—this is a sample/demo project (as stated in README)
  • Does not persist card state across app restarts
  • Does not handle real-time data streaming or live updates
  • No built-in accessibility features or screen-reader support
  • Not designed for infinite scroll or lazy loading (fixed card list only)

🪤Traps & gotchas

  1. Android Support Library deprecation: This uses support-v7, design, and cardview v25.1.0 (from 2016). Modern Android requires AndroidX migration. 2. API level mismatch: minSdkVersion 14 (Android 4.0) is far below modern Play Store requirements (API 24+). 3. ProGuard/R8 not tested: minifyEnabled=false in release build; actual obfuscation untested. 4. Fragment transaction issues: CardFragmentPagerAdapter uses old FragmentPagerAdapter (can leak Fragments); no explicit transaction handling visible. 5. No lifecycle awareness: ShadowTransformer and adapters don't integrate with Android lifecycle—potential memory leaks if ViewPager persists after Fragment destruction.

🏗️Architecture

💡Concepts to learn

  • PageTransformer — Core mechanism in ViewPagerCards—allows custom per-page animations (scale, alpha, translation) as user swipes between cards. ShadowTransformer implements this to achieve Duolingo-like effects.
  • CardView and Elevation — ViewPagerCards uses CardView for Material shadow rendering and Z-depth effects that enhance the card flip/scale animation visually.
  • FragmentPagerAdapter lifecycle — CardFragmentPagerAdapter relies on FragmentPagerAdapter to manage Fragment creation, destruction, and state—critical to understand for avoiding memory leaks and transaction issues.
  • View interpolation and transformation matrices — ShadowTransformer applies scale factors and alpha values calculated from ViewPager scroll position using linear interpolation—fundamental to smooth card animations.
  • RecyclerView-style Adapter pattern — CardPagerAdapter and CardFragmentPagerAdapter follow ViewPager adapter paradigm (similar to RecyclerView.Adapter) with holder binding and efficient view reuse—key pattern for Android list/pager UIs.
  • nickbutcher/plaid — Large-scale Android Material Design app with custom ViewPager transitions and card-based UI; production-ready reference for card animations.
  • Google/material-components-android — Official Material Design library successor to support libraries used here; needed for modern card and transition components.
  • androidx/androidx — AndroidX repository providing ViewPager2, FragmentContainerView, and modern lifecycle-aware paging alternatives to support-v7.
  • chrisbanes/ViewPagerIndicator — Complementary library for adding dot indicators or title strips to ViewPager; often paired with card-based pagers like this one.
  • daimajia/AndroidViewAnimations — General-purpose Android animation library that could extend ShadowTransformer's capabilities with more transition effects.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for ShadowTransformer.java

The ShadowTransformer class is a core component that handles page transformations and animations for the card effect, but app/src/test/java/com/github/rubensousa/viewpagercards/ExampleUnitTest.java is empty. Adding tests would ensure the transformation logic works correctly across different scroll positions and prevents regressions when the animation behavior is modified.

  • [ ] Create tests in app/src/test/java/com/github/rubensousa/viewpagercards/ShadowTransformerTest.java
  • [ ] Add test cases for transformPage() method with various page positions (0, 0.5, 1.0, -1.0, etc.)
  • [ ] Test scale, alpha, and translation transformations are applied correctly
  • [ ] Add edge case tests for extreme scroll positions and velocity values
  • [ ] Verify shadow elevation changes appropriately during page transitions

Add instrumented tests for CardPagerAdapter and CardFragmentPagerAdapter

The app/src/androidTest/ directory only contains a placeholder ApplicationTest.java. The two adapter classes (CardPagerAdapter.java and CardFragmentPagerAdapter.java) are critical for rendering cards but lack automated UI tests. This would verify adapters work correctly with ViewPager and ShadowTransformer across different Android API levels.

  • [ ] Create app/src/androidTest/java/com/github/rubensousa/viewpagercards/CardPagerAdapterTest.java
  • [ ] Create app/src/androidTest/java/com/github/rubensousa/viewpagercards/CardFragmentPagerAdapterTest.java
  • [ ] Test adapter initialization with CardItem objects and proper view binding
  • [ ] Test page transitions trigger correct ShadowTransformer animations
  • [ ] Verify adapter handles configuration changes (rotation) correctly
  • [ ] Test with minSdkVersion 14 compatibility (as specified in build.gradle)

Add instrumented tests for landscape layout variants

The repo includes landscape-specific layout files (app/src/main/res/layout-land/) but the test suite doesn't verify they work correctly. Currently no tests validate the different layouts in app/src/main/res/layout-land/activity_main.xml, app/src/main/res/layout-land/adapter.xml, and app/src/main/res/layout-land/fragment_adapter.xml function properly during orientation changes.

  • [ ] Create app/src/androidTest/java/com/github/rubensousa/viewpagercards/OrientationChangeTest.java
  • [ ] Test MainActivity loads and renders correctly in portrait orientation
  • [ ] Test MainActivity properly transitions to landscape layout with app/src/main/res/layout-land/activity_main.xml
  • [ ] Verify ViewPager state and card positions are preserved during rotation
  • [ ] Test that adapter layout files (adapter.xml and fragment_adapter.xml) work in both orientations
  • [ ] Validate dimens from app/src/main/res/values-land/dimens.xml are applied correctly

🌿Good first issues

  • Add unit tests for CardItem and CardAdapter: app/src/test/ is empty. Create CardAdapterTest.java to verify item binding and CardItemTest.java to validate data models.
  • Migrate from deprecated Support Library to AndroidX: update build.gradle to use androidx.viewpager.widget.ViewPager, androidx.cardview.widget.CardView, and androidx.fragment.app.FragmentPagerAdapter; update import statements across all Java files.
  • Document ShadowTransformer animation parameters: add JavaDoc to ShadowTransformer explaining the scale range, shadow properties, and how to customize the transformation (e.g., speed, max/min scale values).

Top contributors

Click to expand

📝Recent commits

Click to expand
  • e1d8b18 — Fix blog link and add maintenance notice in README (rubensousa)
  • 75d8004 — Merge remote-tracking branch 'origin/master' (rubensousa)
  • 8717fd5 — Add example with custom title and text (rubensousa)
  • dfa5bff — Update dependencies (rubensousa)
  • e5663a5 — Update README.md (rubensousa)
  • a9e6cd8 — Update README.md (rubensousa)
  • 3441334 — Update README.md (rubensousa)
  • 267e6c1 — Update README.md (rubensousa)
  • 17c3886 — Remove unused import (rubensousa)
  • 976ab80 — Add scaling sample (rubensousa)

🔒Security observations

This codebase has significant security concerns due to extreme age and lack of maintenance. The project uses Android SDK and support libraries from 2016-2017 with known vulnerabilities. Minification is disabled, increasing reverse-engineering risk. The minimum SDK version of 14 is obsolete. Most critically, the project is explicitly no longer maintained, meaning no security updates will be applied. This should only be used as a reference implementation or learning example, not for production applications. Comprehensive modernization is required including SDK updates, AndroidX migration, dependency updates, and enabling code obfuscation.

  • High · Outdated Android SDK and Build Tools — app/build.gradle. The project uses compileSdkVersion 25 and buildToolsVersion 25.0.1, which are from 2016. These versions are significantly outdated and lack critical security patches. Android SDK 25 (Android 7.1) reached end-of-life and contains known vulnerabilities. Fix: Update to the latest stable compileSdkVersion (34+) and buildToolsVersion. Use modern Android SDK versions to receive security updates and patches.
  • High · Outdated Android Support Libraries with Known Vulnerabilities — app/build.gradle - dependencies section. The project uses Android Support Libraries version 25.1.0 (released January 2017). These libraries are deprecated and contain known security vulnerabilities. Google ended support for the Android Support Library in favor of AndroidX. Fix: Migrate to AndroidX libraries and update to latest stable versions. Replace 'com.android.support::25.1.0' with 'androidx.' equivalents and update to current versions (e.g., androidx.appcompat:appcompat:1.6.1).
  • High · Minification Disabled in Release Build — app/build.gradle - buildTypes.release section. The release build configuration has 'minifyEnabled false', meaning ProGuard/R8 code shrinking and obfuscation is disabled. This makes the app vulnerable to reverse engineering and increases APK size. Fix: Enable minification by setting 'minifyEnabled true' in release builds to enable code obfuscation and shrinking via R8 (successor to ProGuard).
  • Medium · Low Minimum SDK Version — app/build.gradle - defaultConfig section. The minSdkVersion is set to 14 (Android 4.0), released in 2011. Supporting such old versions increases attack surface and requires maintaining compatibility with outdated, unpatched Android versions. Fix: Increase minSdkVersion to at least 21 (Android 5.0) or higher. This reduces compatibility burden and allows use of modern security APIs. Modern best practice is minSdkVersion 24+.
  • Medium · Project No Longer Maintained — README.md. The README explicitly states 'this project is no longer being maintained'. This means security vulnerabilities and issues will not be patched, and dependencies will not be updated. Fix: If using this code in production, fork the repository and maintain it independently. Regularly update dependencies and apply security patches. Consider using this only as a learning reference.
  • Low · Missing Security Configuration — app/src/main/AndroidManifest.xml or app/src/main/res/. No evidence of network security configuration (network_security_config.xml) which is important for controlling HTTPS/certificate pinning policies and cleartext traffic restrictions. Fix: Implement a network security configuration to restrict cleartext traffic and enforce HTTPS. Add network_security_config.xml with appropriate security policies.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · rubensousa/ViewPagerCards — RepoPilot