diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..1afd053
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,19 @@
+name: CI
+
+on:
+ push:
+ branches: [master, main, "cursor/**"]
+ pull_request:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: "17"
+ - uses: android-actions/setup-android@v3
+ - name: Assemble and unit test
+ run: ./gradlew assembleDebug testDebugUnitTest --stacktrace
diff --git a/.gitignore b/.gitignore
index 9c4de58..51fb2f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,14 @@
+*.iml
.gradle
/local.properties
-/.idea/workspace.xml
-/.idea/libraries
+/.idea
.DS_Store
/build
+**/build/
/captures
+.externalNativeBuild
+.cxx
+*.apk
+*.ap_
+.kotlin
+*.hprof
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 4bb741d..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-TinderView
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 96cc43e..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf3..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index da49c43..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 5d19981..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 0c4afc9..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..62fff32
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,18 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ Copyright 2015 Aradh Pillai
+ Copyright 2026 TinderView contributors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index 654e757..f3fd0d7 100644
--- a/README.md
+++ b/README.md
@@ -1,46 +1,93 @@
-TinderView
-========
+# TinderView
-Created A Simple and Beautiful Tinder like card deck & Captain Train like toolbar.
+A lightweight Jetpack Compose card-stack for Android. Drag, fling, rewind — the 2026 swipe feel without a 2015 View hierarchy.
-This is heavily based on [AndroidSwipeableCardStack](https://github.com/wenchaojiang/AndroidSwipeableCardStack), wenchaojiang has written the library very well but i faced a lot of difficulty integrating it & that's the reason i built this app so that you dont waste time. Use this repo as a base for your app & build on top :).
+
-API 16 and UP. (Support for API 16 is now live thanks to [#4] (https://github.com/GadgetCheck/TinderView/pull/4))
+The 24s clip walks the sample: pulse reveal, a below-threshold drag that springs back, like / nope / super-like throws, rewind, action-button squash, profile sheet, match burst, a real last-card swipe, empty deck, Explore, and Messages.
-In Action
-=========
+## Usage
-[Click here to watch the app in action on youtube!](https://youtu.be/ccnkq9HmyVY)
+```kotlin
+val state = rememberCardStackState { profiles.size }
-
+CardStack(
+ items = profiles,
+ key = { it.id },
+ state = state,
+ onSwiped = { profile, direction -> /* persist like / pass */ },
+ onTopCardClick = { profile -> /* open sheet */ },
+) { profile ->
+ ProfileCard(profile)
+}
-
+// Same physics as a finger fling
+scope.launch { state.swipe(SwipeDirection.Right) }
+scope.launch { state.rewind() }
+```
-The demo is now available, dont forget to give it 5 stats if it helped you. Oh if you can contribute to development of the library in anyway i would love to see your pull request.
+### Install
-Happy Coding!
+```kotlin
+implementation(project(":cardstack"))
+```
-Developed By
-============
+The sample app (`:app`) is the reference integration. Maven Central publishing is a follow-up.
-* Aradh Pillai
+### Properties
+| Property | Default | Meaning |
+| --- | --- | --- |
+| `visibleCount` | `3` | Cards composed at once |
+| `stackOffset` | `10.dp` | Depth offset for back cards |
+| `stackScaleStep` | `0.04` | Scale drop per depth |
+| `maxRotationZ` | `14f` | Peak tilt in degrees |
+| `thresholdFraction` | `0.35` | Commit distance as a fraction of card width |
+| `flingVelocity` | `900.dp` | Velocity (per second) that commits a throw |
+| `enabledDirections` | Left, Right, Up | Physical axes that can discard |
+| `enableHaptics` | `true` | One tick on threshold, one on commit |
+| `enableColorWash` | `true` | Leading-edge tint while dragging |
+| `snapSpring` / `throwSpring` | tuned springs | Snap-back vs exit |
-License
-=======
+Override via `CardStackProperties` or `CardStackDefaults.properties()`.
- Copyright 2015 Aradh Pillai
+### Overlays
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+LIKE / NOPE / SUPER stamps receive **0f..1f** progress. Replace the defaults:
- http://www.apache.org/licenses/LICENSE-2.0
+```kotlin
+CardStack(
+ items = profiles,
+ key = { it.id },
+ likeOverlay = { progress -> MyLikeStamp(progress) },
+ passOverlay = { progress -> MyNopeStamp(progress) },
+ superLikeOverlay = { progress -> MySuperStamp(progress) },
+) { ProfileCard(it) }
+```
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+### State
+| Field / API | Role |
+| --- | --- |
+| `currentIndex` | Top card in the stable list |
+| `canSwipe` / `canRewind` | Drive action buttons |
+| `isAnimating` | Ignore extra input mid-throw |
+| `swipe(direction)` | Programmatic discard |
+| `rewind()` | Fly the last card back in |
+The list is **not** mutated by the library. Swipe advances `currentIndex`; rewind decrements it. Use `onSwiped` for side effects only.
+
+## Sample
+
+`:app` is a Material 3 demo: full-bleed cards, liquid-glass action bar, Super Like, match burst, Discover grid, and Chat from liked profiles.
+
+```
+./gradlew :app:assembleDebug
+```
+
+## License
+
+Copyright 2015 Aradh Pillai
+Copyright 2026 TinderView contributors
+
+Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).
diff --git a/TinderView.iml b/TinderView.iml
deleted file mode 100644
index fa96218..0000000
--- a/TinderView.iml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000..a715da2
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,68 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+}
+
+android {
+ namespace = "com.tinderview"
+ compileSdk = 36
+
+ defaultConfig {
+ applicationId = "com.tinderview"
+ minSdk = 26
+ targetSdk = 36
+ versionCode = 2
+ versionName = "1.0.0"
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ isShrinkResources = true
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = "17"
+ }
+
+ buildFeatures {
+ compose = true
+ }
+
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+}
+
+dependencies {
+ implementation(project(":cardstack"))
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.compose.bom))
+ implementation(libs.compose.ui)
+ implementation(libs.compose.ui.graphics)
+ implementation(libs.compose.ui.tooling.preview)
+ implementation(libs.compose.material3)
+ implementation(libs.compose.animation)
+ debugImplementation(libs.compose.ui.tooling)
+ debugImplementation(libs.compose.ui.test.manifest)
+ androidTestImplementation(platform(libs.compose.bom))
+ androidTestImplementation(libs.compose.ui.test.junit4)
+ androidTestImplementation(libs.androidx.junit)
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..d578c68
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1 @@
+# Sample app. Compose and the cardstack consumer rules cover the hot path.
diff --git a/app/src/androidTest/java/com/tinderview/CardStackSwipeTest.kt b/app/src/androidTest/java/com/tinderview/CardStackSwipeTest.kt
new file mode 100644
index 0000000..f06bdc8
--- /dev/null
+++ b/app/src/androidTest/java/com/tinderview/CardStackSwipeTest.kt
@@ -0,0 +1,45 @@
+package com.tinderview
+
+import androidx.compose.material3.Text
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.tinderview.cardstack.CardStack
+import com.tinderview.cardstack.CardStackState
+import com.tinderview.cardstack.SwipeDirection
+import com.tinderview.cardstack.rememberCardStackState
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CardStackSwipeTest {
+
+ @get:Rule
+ val composeRule = createComposeRule()
+
+ @Test
+ fun swipeRightAdvancesCurrentIndex() {
+ val holder = arrayOfNulls(1)
+ composeRule.setContent {
+ val items = listOf("a", "b", "c")
+ val state = rememberCardStackState { items.size }
+ holder[0] = state
+ CardStack(
+ items = items,
+ key = { it },
+ state = state,
+ ) { label ->
+ Text(label)
+ }
+ }
+ composeRule.onNodeWithContentDescription("Top card").assertExists()
+ composeRule.runOnIdle {
+ runBlocking { holder[0]!!.swipe(SwipeDirection.Right) }
+ }
+ composeRule.waitForIdle()
+ assertEquals(1, holder[0]!!.currentIndex)
+ }
+}
diff --git a/wear/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
similarity index 57%
rename from wear/src/main/AndroidManifest.xml
rename to app/src/main/AndroidManifest.xml
index 7d68a15..c9fbeec 100644
--- a/wear/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,23 +1,25 @@
-
-
+ xmlns:tools="http://schemas.android.com/tools">
+ android:supportsRtl="true"
+ android:theme="@style/Theme.TinderView"
+ android:enableOnBackInvokedCallback="true"
+ tools:targetApi="33">
+ android:exported="true"
+ android:screenOrientation="portrait"
+ android:theme="@style/Theme.TinderView"
+ android:windowSoftInputMode="adjustResize">
-
-
diff --git a/app/src/main/java/com/tinderview/MainActivity.kt b/app/src/main/java/com/tinderview/MainActivity.kt
new file mode 100644
index 0000000..c889168
--- /dev/null
+++ b/app/src/main/java/com/tinderview/MainActivity.kt
@@ -0,0 +1,20 @@
+package com.tinderview
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import com.tinderview.ui.TinderViewApp
+import com.tinderview.ui.theme.TinderViewTheme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+ setContent {
+ TinderViewTheme {
+ TinderViewApp()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/tinderview/data/SampleProfiles.kt b/app/src/main/java/com/tinderview/data/SampleProfiles.kt
new file mode 100644
index 0000000..e9bc10d
--- /dev/null
+++ b/app/src/main/java/com/tinderview/data/SampleProfiles.kt
@@ -0,0 +1,90 @@
+package com.tinderview.data
+
+import androidx.compose.ui.graphics.Color
+
+data class Profile(
+ val id: String,
+ val name: String,
+ val age: Int,
+ val bio: String,
+ val city: String,
+ val gradient: List,
+ val interests: List,
+ val isMatch: Boolean = false,
+)
+
+val SampleProfiles: List = listOf(
+ Profile(
+ id = "nova",
+ name = "Nova",
+ age = 27,
+ bio = "Designs typefaces, climbs granite, never skips golden hour.",
+ city = "Lisbon",
+ gradient = listOf(Color(0xFFFF7A59), Color(0xFFE85D4C), Color(0xFF3A1D18)),
+ interests = listOf("Type", "Granite", "Film"),
+ isMatch = true,
+ ),
+ Profile(
+ id = "julian",
+ name = "Julian",
+ age = 31,
+ bio = "Jazz pianist who makes a serious case for weeknight dancing.",
+ city = "Brooklyn",
+ gradient = listOf(Color(0xFF7B8CFF), Color(0xFF5A3D8A), Color(0xFF1A1228)),
+ interests = listOf("Jazz", "Dance"),
+ ),
+ Profile(
+ id = "mira",
+ name = "Mira",
+ age = 24,
+ bio = "Marine biologist. Will talk about tide pools until the sun comes up.",
+ city = "San Diego",
+ gradient = listOf(Color(0xFF3EE0C2), Color(0xFF0A7EA8), Color(0xFF062430)),
+ interests = listOf("Tide pools", "Coffee"),
+ ),
+ Profile(
+ id = "theo",
+ name = "Theo",
+ age = 29,
+ bio = "Chef. Makes pasta from scratch and playlists that start too loud.",
+ city = "Austin",
+ gradient = listOf(Color(0xFFFFC857), Color(0xFFE07A2F), Color(0xFF3A1E0A)),
+ interests = listOf("Pasta", "Vinyl"),
+ ),
+ Profile(
+ id = "sienna",
+ name = "Sienna",
+ age = 26,
+ bio = "Photo essays, thrifted denim, and a motorcycle named Pearl.",
+ city = "Portland",
+ gradient = listOf(Color(0xFFF2B6C6), Color(0xFFC46B7A), Color(0xFF3A1A22)),
+ interests = listOf("35mm", "Pearl"),
+ ),
+ Profile(
+ id = "kai",
+ name = "Kai",
+ age = 28,
+ bio = "Builds furniture by day, stargazes from rooftops by night.",
+ city = "Denver",
+ gradient = listOf(Color(0xFF6DD5ED), Color(0xFF1A6B8A), Color(0xFF0B1E26)),
+ interests = listOf("Oak", "Stars"),
+ ),
+ Profile(
+ id = "lina",
+ name = "Lina",
+ age = 25,
+ bio = "Runs sunrise 10Ks and still has energy for a second espresso.",
+ city = "Berlin",
+ gradient = listOf(Color(0xFFE36AA5), Color(0xFF5B2C6F), Color(0xFF160C22)),
+ interests = listOf("10K", "Espresso"),
+ ),
+ Profile(
+ id = "omar",
+ name = "Omar",
+ age = 33,
+ bio = "Documentary editor. Best first date is a flea market plus tacos.",
+ city = "Chicago",
+ gradient = listOf(Color(0xFF5EEAD4), Color(0xFF0F766E), Color(0xFF06201D)),
+ interests = listOf("Cuts", "Tacos"),
+ ),
+)
diff --git a/app/src/main/java/com/tinderview/ui/TinderViewApp.kt b/app/src/main/java/com/tinderview/ui/TinderViewApp.kt
new file mode 100644
index 0000000..f2363db
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/TinderViewApp.kt
@@ -0,0 +1,148 @@
+package com.tinderview.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.selected
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import com.tinderview.data.Profile
+import com.tinderview.ui.chat.ChatScreen
+import com.tinderview.ui.connect.ConnectScreen
+import com.tinderview.ui.discover.DiscoverScreen
+import com.tinderview.ui.theme.CardsIcon
+import com.tinderview.ui.theme.ChatIcon
+import com.tinderview.ui.theme.Ink
+import com.tinderview.ui.theme.SearchIcon
+
+private enum class Dest(val label: String) {
+ Connect("Connect"),
+ Discover("Explore"),
+ Chat("Chat"),
+}
+
+@Composable
+fun TinderViewApp() {
+ var selected by remember { mutableIntStateOf(0) }
+ val liked = remember { mutableStateListOf() }
+ val destinations = Dest.entries
+
+ Scaffold(
+ containerColor = Ink.Night,
+ bottomBar = {
+ AppNavigationBar(
+ selected = selected,
+ destinations = destinations,
+ onSelect = { selected = it },
+ )
+ },
+ ) { padding ->
+ val dest = destinations[selected]
+ Box(
+ Modifier
+ .padding(padding)
+ .fillMaxSize()
+ .background(Ink.Night),
+ ) {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .zIndex(if (dest == Dest.Connect) 1f else 0f)
+ .graphicsLayer { alpha = if (dest == Dest.Connect) 1f else 0f }
+ .then(if (dest != Dest.Connect) Modifier.clearAndSetSemantics { } else Modifier),
+ ) {
+ ConnectScreen(
+ onLiked = { profile ->
+ if (liked.none { it.id == profile.id }) liked.add(0, profile)
+ },
+ )
+ }
+ if (dest == Dest.Discover) {
+ DiscoverScreen(modifier = Modifier.fillMaxSize())
+ }
+ if (dest == Dest.Chat) {
+ ChatScreen(modifier = Modifier.fillMaxSize(), liked = liked)
+ }
+ }
+ }
+}
+
+@Composable
+private fun AppNavigationBar(
+ selected: Int,
+ destinations: List,
+ onSelect: (Int) -> Unit,
+) {
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .background(Ink.Ink)
+ .navigationBarsPadding(),
+ ) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(1.dp)
+ .background(Ink.Hairline),
+ )
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(64.dp)
+ .padding(horizontal = 8.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ destinations.forEachIndexed { index, dest ->
+ val active = selected == index
+ val tint = if (active) Ink.Coral else Ink.CreamMuted
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .clickable(
+ interactionSource = remember(index) { MutableInteractionSource() },
+ indication = null,
+ onClick = { onSelect(index) },
+ )
+ .semantics { this.selected = active }
+ .padding(vertical = 6.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ when (dest) {
+ Dest.Connect -> CardsIcon(tint, size = 20.dp)
+ Dest.Discover -> SearchIcon(tint, size = 20.dp)
+ Dest.Chat -> ChatIcon(tint, size = 20.dp)
+ }
+ Text(
+ text = dest.label,
+ color = tint,
+ style = androidx.compose.material3.MaterialTheme.typography.labelMedium,
+ modifier = Modifier.padding(top = 4.dp),
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/chat/ChatScreen.kt b/app/src/main/java/com/tinderview/ui/chat/ChatScreen.kt
new file mode 100644
index 0000000..a00f7da
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/chat/ChatScreen.kt
@@ -0,0 +1,110 @@
+package com.tinderview.ui.chat
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.tinderview.data.Profile
+import com.tinderview.ui.theme.Ink
+
+@Composable
+fun ChatScreen(
+ liked: List,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier
+ .fillMaxSize()
+ .background(Ink.Night),
+ ) {
+ Text(
+ text = "Messages",
+ style = MaterialTheme.typography.headlineMedium,
+ color = Ink.Cream,
+ modifier = Modifier.padding(start = 22.dp, end = 22.dp, top = 18.dp),
+ )
+ Text(
+ text = if (liked.isEmpty()) "Likes become conversations" else "${liked.size} open threads",
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(start = 22.dp, end = 22.dp, top = 4.dp, bottom = 10.dp),
+ )
+ if (liked.isEmpty()) {
+ Text(
+ text = "Like someone on Connect to start a conversation.",
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.padding(horizontal = 22.dp, vertical = 12.dp),
+ )
+ } else {
+ LazyColumn(
+ contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ ) {
+ items(liked, key = { it.id }) { profile ->
+ val brush = remember(profile.id) { Brush.linearGradient(profile.gradient) }
+ val shape = RoundedCornerShape(20.dp)
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(shape)
+ .background(Ink.Raised)
+ .border(1.dp, Ink.Hairline, shape)
+ .padding(horizontal = 14.dp, vertical = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(
+ Modifier
+ .size(52.dp)
+ .clip(CircleShape)
+ .background(brush)
+ .border(
+ width = if (profile.isMatch) 1.5.dp else 0.dp,
+ color = if (profile.isMatch) Ink.Coral else Color.Transparent,
+ shape = CircleShape,
+ ),
+ )
+ Column(Modifier.padding(start = 14.dp).weight(1f)) {
+ Text(
+ text = profile.name,
+ style = MaterialTheme.typography.titleMedium,
+ color = Ink.Cream,
+ )
+ Text(
+ text = if (profile.isMatch) {
+ "You matched · say hello"
+ } else {
+ "You liked ${profile.name}"
+ },
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(top = 2.dp),
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/connect/ActionBar.kt b/app/src/main/java/com/tinderview/ui/connect/ActionBar.kt
new file mode 100644
index 0000000..760eefd
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/connect/ActionBar.kt
@@ -0,0 +1,117 @@
+package com.tinderview.ui.connect
+
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.draw.shadow
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.tinderview.ui.theme.CloseIcon
+import com.tinderview.ui.theme.HeartIcon
+import com.tinderview.ui.theme.Ink
+import com.tinderview.ui.theme.RewindIcon
+import com.tinderview.ui.theme.StarIcon
+
+@Composable
+fun GlassActionBar(
+ canRewind: Boolean,
+ canSwipe: Boolean,
+ onRewind: () -> Unit,
+ onPass: () -> Unit,
+ onSuperLike: () -> Unit,
+ onLike: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val pill = RoundedCornerShape(percent = 50)
+ Box(
+ modifier = modifier
+ .fillMaxWidth()
+ .shadow(22.dp, pill, ambientColor = Color.Black.copy(alpha = 0.45f), spotColor = Color.Black.copy(alpha = 0.55f))
+ .clip(pill)
+ .background(
+ Brush.verticalGradient(
+ 0f to Color(0xCC2A2724),
+ 1f to Color(0xE6141210),
+ ),
+ )
+ .border(1.dp, Color.White.copy(alpha = 0.16f), pill)
+ .padding(horizontal = 10.dp, vertical = 8.dp),
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(68.dp),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ ActionButton(enabled = canRewind, tint = Ink.Gold, size = 50.dp, onClick = onRewind) {
+ RewindIcon(Ink.Gold.copy(alpha = if (canRewind) 1f else 0.35f), size = 20.dp)
+ }
+ ActionButton(enabled = canSwipe, tint = Ink.Nope, size = 60.dp, onClick = onPass) {
+ CloseIcon(Ink.Nope.copy(alpha = if (canSwipe) 1f else 0.35f), size = 22.dp)
+ }
+ ActionButton(enabled = canSwipe, tint = Ink.Ice, size = 50.dp, onClick = onSuperLike) {
+ StarIcon(Ink.Ice.copy(alpha = if (canSwipe) 1f else 0.35f), size = 20.dp)
+ }
+ ActionButton(enabled = canSwipe, tint = Ink.Mint, size = 60.dp, onClick = onLike) {
+ HeartIcon(Ink.Mint.copy(alpha = if (canSwipe) 1f else 0.35f), size = 22.dp)
+ }
+ }
+ }
+}
+
+@Composable
+private fun ActionButton(
+ enabled: Boolean,
+ tint: Color,
+ size: Dp,
+ onClick: () -> Unit,
+ icon: @Composable () -> Unit,
+) {
+ val interaction = remember { MutableInteractionSource() }
+ val pressed by interaction.collectIsPressedAsState()
+ val scale by animateFloatAsState(
+ targetValue = if (pressed) 0.88f else 1f,
+ animationSpec = spring(stiffness = 700f, dampingRatio = 0.55f),
+ label = "action-scale",
+ )
+ Box(
+ modifier = Modifier
+ .size(size)
+ .scale(scale)
+ .clip(CircleShape)
+ .background(tint.copy(alpha = if (enabled) 0.16f else 0.06f))
+ .border(1.25.dp, tint.copy(alpha = if (enabled) 0.85f else 0.22f), CircleShape)
+ .clickable(
+ enabled = enabled,
+ interactionSource = interaction,
+ indication = null,
+ onClick = onClick,
+ ),
+ contentAlignment = Alignment.Center,
+ ) {
+ icon()
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/connect/ConnectScreen.kt b/app/src/main/java/com/tinderview/ui/connect/ConnectScreen.kt
new file mode 100644
index 0000000..5cb4ac4
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/connect/ConnectScreen.kt
@@ -0,0 +1,235 @@
+package com.tinderview.ui.connect
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.scaleIn
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.tinderview.cardstack.CardStack
+import com.tinderview.cardstack.SwipeDirection
+import com.tinderview.cardstack.rememberCardStackState
+import com.tinderview.data.Profile
+import com.tinderview.data.SampleProfiles
+import com.tinderview.ui.theme.Ink
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ConnectScreen(
+ onLiked: (Profile) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val profiles = remember { SampleProfiles }
+ val state = rememberCardStackState { profiles.size }
+ val scope = rememberCoroutineScope()
+ var ready by remember { mutableStateOf(false) }
+ var selected by remember { mutableStateOf(null) }
+ var match by remember { mutableStateOf(null) }
+ val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
+
+ LaunchedEffect(Unit) {
+ delay(1100)
+ ready = true
+ }
+
+ Box(modifier.fillMaxSize().background(Ink.Night)) {
+ if (!ready) {
+ PulseLoader()
+ }
+
+ AnimatedVisibility(
+ visible = ready,
+ enter = fadeIn() + scaleIn(initialScale = 0.975f),
+ exit = fadeOut(),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Wordmark()
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .widthIn(max = 480.dp)
+ .padding(horizontal = 16.dp, vertical = 6.dp),
+ ) {
+ if (state.currentIndex >= profiles.size) {
+ EmptyDeck(
+ canRewind = state.canRewind,
+ onRewind = { scope.launch { state.rewind() } },
+ )
+ } else {
+ CardStack(
+ items = profiles,
+ key = { it.id },
+ state = state,
+ modifier = Modifier.fillMaxSize(),
+ onSwiped = { profile, direction ->
+ if (direction != SwipeDirection.Left) {
+ onLiked(profile)
+ if (profile.isMatch && direction == SwipeDirection.Right) {
+ match = profile
+ }
+ }
+ },
+ onTopCardClick = { selected = it },
+ ) { profile ->
+ ProfileCard(profile)
+ }
+ }
+ }
+ GlassActionBar(
+ canRewind = state.canRewind,
+ canSwipe = state.canSwipe,
+ onRewind = { scope.launch { state.rewind() } },
+ onPass = { scope.launch { state.swipe(SwipeDirection.Left) } },
+ onSuperLike = { scope.launch { state.swipe(SwipeDirection.Up) } },
+ onLike = { scope.launch { state.swipe(SwipeDirection.Right) } },
+ modifier = Modifier
+ .padding(horizontal = 28.dp)
+ .padding(bottom = 16.dp, top = 8.dp),
+ )
+ }
+ }
+
+ match?.let { profile ->
+ MatchBurst(profile = profile, onDismiss = { match = null })
+ }
+ }
+
+ selected?.let { profile ->
+ ModalBottomSheet(
+ onDismissRequest = { selected = null },
+ sheetState = sheetState,
+ containerColor = Ink.Raised,
+ contentColor = Ink.Cream,
+ ) {
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 22.dp, vertical = 8.dp),
+ ) {
+ ProfileCard(
+ profile = profile,
+ showBio = false,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(300.dp),
+ )
+ Text(
+ text = profile.name,
+ style = MaterialTheme.typography.headlineMedium,
+ modifier = Modifier.padding(top = 20.dp),
+ )
+ Text(
+ text = "${profile.age} · ${profile.city}",
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(top = 4.dp),
+ )
+ Row(Modifier.padding(top = 14.dp)) {
+ profile.interests.forEach { interest ->
+ InfoChip(interest)
+ Spacer(Modifier.width(8.dp))
+ }
+ }
+ Text(
+ text = profile.bio,
+ style = MaterialTheme.typography.bodyLarge,
+ color = Ink.Cream.copy(alpha = 0.9f),
+ modifier = Modifier.padding(top = 16.dp),
+ )
+ TextButton(
+ onClick = { selected = null },
+ modifier = Modifier.align(Alignment.End),
+ ) {
+ Text("Close", color = Ink.Coral, fontWeight = FontWeight.SemiBold)
+ }
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+ }
+}
+
+@Composable
+private fun Wordmark() {
+ Row(
+ modifier = Modifier.padding(top = 6.dp, bottom = 2.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(
+ Modifier
+ .padding(end = 8.dp)
+ .size(7.dp)
+ .clip(CircleShape)
+ .background(Ink.Coral),
+ )
+ Text(
+ text = "tinderview",
+ color = Ink.Cream,
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.SemiBold,
+ )
+ }
+}
+
+@Composable
+private fun EmptyDeck(
+ canRewind: Boolean,
+ onRewind: () -> Unit,
+) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Spacer(Modifier.weight(1f))
+ Text(
+ text = "That's everyone",
+ style = MaterialTheme.typography.displayMedium,
+ color = Ink.Cream,
+ )
+ Text(
+ text = "Rewind the last card, or see who's nearby.",
+ modifier = Modifier.padding(top = 10.dp, bottom = 18.dp),
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ if (canRewind) {
+ TextButton(onClick = onRewind) {
+ Text("Rewind last swipe", color = Ink.Coral, fontWeight = FontWeight.SemiBold)
+ }
+ }
+ Spacer(Modifier.weight(1f))
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/connect/MatchBurst.kt b/app/src/main/java/com/tinderview/ui/connect/MatchBurst.kt
new file mode 100644
index 0000000..aa20eac
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/connect/MatchBurst.kt
@@ -0,0 +1,104 @@
+package com.tinderview.ui.connect
+
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.ui.unit.dp
+import com.tinderview.data.Profile
+import com.tinderview.ui.theme.Ink
+import kotlinx.coroutines.delay
+
+@Composable
+fun MatchBurst(
+ profile: Profile,
+ onDismiss: () -> Unit,
+) {
+ var visible by remember { mutableStateOf(false) }
+ LaunchedEffect(profile.id) {
+ visible = true
+ delay(2300)
+ onDismiss()
+ }
+ val scale by animateFloatAsState(
+ targetValue = if (visible) 1f else 0.72f,
+ animationSpec = spring(stiffness = 220f, dampingRatio = 0.68f),
+ label = "match-scale",
+ )
+ val alpha by animateFloatAsState(
+ targetValue = if (visible) 1f else 0f,
+ label = "match-alpha",
+ )
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Ink.Wash.copy(alpha = 0.92f * alpha))
+ .clickable(onClick = onDismiss),
+ contentAlignment = Alignment.Center,
+ ) {
+ Box(
+ Modifier
+ .size(340.dp)
+ .scale(scale)
+ .background(
+ Brush.radialGradient(listOf(Ink.Coral.copy(alpha = 0.28f * alpha), Color.Transparent)),
+ CircleShape,
+ ),
+ )
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.scale(scale),
+ ) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Box(
+ Modifier
+ .size(72.dp)
+ .offset(x = 10.dp)
+ .clip(CircleShape)
+ .background(Brush.linearGradient(listOf(Ink.Coral, Ink.CoralDeep))),
+ )
+ Box(
+ Modifier
+ .size(72.dp)
+ .offset(x = (-10).dp)
+ .clip(CircleShape)
+ .background(Brush.linearGradient(profile.gradient)),
+ )
+ }
+ Text(
+ text = "It's a match",
+ color = Ink.Cream,
+ style = MaterialTheme.typography.displayMedium,
+ modifier = Modifier.padding(top = 22.dp),
+ )
+ Text(
+ text = "You and ${profile.name} liked each other",
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.padding(top = 8.dp),
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/connect/ProfileCard.kt b/app/src/main/java/com/tinderview/ui/connect/ProfileCard.kt
new file mode 100644
index 0000000..0ffac80
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/connect/ProfileCard.kt
@@ -0,0 +1,118 @@
+package com.tinderview.ui.connect
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.tinderview.data.Profile
+import com.tinderview.ui.theme.Ink
+
+@Composable
+fun ProfileCard(
+ profile: Profile,
+ modifier: Modifier = Modifier,
+ showBio: Boolean = true,
+) {
+ val shape = remember { RoundedCornerShape(28.dp) }
+ val photo = remember(profile.id) { Brush.linearGradient(profile.gradient) }
+ val scrim = remember {
+ Brush.verticalGradient(
+ 0f to Color.Transparent,
+ 0.42f to Color.Transparent,
+ 0.72f to Color(0x66000000),
+ 1f to Color(0xCC000000),
+ )
+ }
+ val sheen = remember {
+ Brush.linearGradient(
+ 0f to Color.White.copy(alpha = 0.16f),
+ 0.38f to Color.Transparent,
+ )
+ }
+ Box(
+ modifier = modifier
+ .fillMaxSize()
+ .clip(shape)
+ .background(photo)
+ .border(1.dp, Color.White.copy(alpha = 0.12f), shape),
+ ) {
+ Box(Modifier.fillMaxSize().background(sheen))
+ Box(Modifier.fillMaxSize().background(scrim))
+ Column(
+ modifier = Modifier
+ .align(Alignment.BottomStart)
+ .fillMaxWidth()
+ .padding(horizontal = 22.dp, vertical = 20.dp),
+ ) {
+ Row(verticalAlignment = Alignment.Bottom) {
+ Text(
+ text = profile.name,
+ color = Ink.Cream,
+ style = MaterialTheme.typography.displayMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.weight(1f, fill = false),
+ )
+ Text(
+ text = " ${profile.age}",
+ color = Ink.Cream.copy(alpha = 0.78f),
+ style = MaterialTheme.typography.titleLarge,
+ fontWeight = FontWeight.Medium,
+ modifier = Modifier.padding(bottom = 3.dp),
+ )
+ }
+ Row(
+ modifier = Modifier.padding(top = 10.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ InfoChip(profile.city)
+ profile.interests.take(2).forEach { InfoChip(it) }
+ }
+ if (showBio) {
+ Text(
+ text = profile.bio,
+ color = Ink.Cream.copy(alpha = 0.88f),
+ style = MaterialTheme.typography.bodyMedium,
+ fontSize = 15.sp,
+ lineHeight = 21.sp,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(top = 12.dp),
+ )
+ }
+ }
+ }
+}
+
+@Composable
+internal fun InfoChip(label: String) {
+ Text(
+ text = label,
+ color = Ink.Cream,
+ style = MaterialTheme.typography.labelMedium,
+ modifier = Modifier
+ .clip(RoundedCornerShape(50))
+ .background(Color.White.copy(alpha = 0.14f))
+ .border(1.dp, Color.White.copy(alpha = 0.10f), RoundedCornerShape(50))
+ .padding(horizontal = 10.dp, vertical = 5.dp),
+ )
+}
diff --git a/app/src/main/java/com/tinderview/ui/connect/PulseLoader.kt b/app/src/main/java/com/tinderview/ui/connect/PulseLoader.kt
new file mode 100644
index 0000000..bfca9df
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/connect/PulseLoader.kt
@@ -0,0 +1,62 @@
+package com.tinderview.ui.connect
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.ui.unit.dp
+import com.tinderview.ui.theme.Ink
+
+@Composable
+fun PulseLoader(modifier: Modifier = Modifier) {
+ val transition = rememberInfiniteTransition(label = "pulse")
+ val t by transition.animateFloat(
+ initialValue = 0f,
+ targetValue = 1f,
+ animationSpec = infiniteRepeatable(
+ animation = tween(1600, easing = LinearEasing),
+ repeatMode = RepeatMode.Restart,
+ ),
+ label = "pulse-t",
+ )
+ Box(modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Canvas(Modifier.size(200.dp)) {
+ val center = Offset(size.width / 2f, size.height / 2f)
+ val maxR = size.minDimension / 2f
+ repeat(3) { i ->
+ val phase = ((t + i / 3f) % 1f)
+ drawCircle(
+ color = Ink.Coral.copy(alpha = (1f - phase) * 0.55f),
+ radius = maxR * (0.18f + phase * 0.78f),
+ center = center,
+ style = Stroke(width = 2.5.dp.toPx()),
+ )
+ }
+ drawCircle(color = Ink.Coral, radius = maxR * 0.12f, center = center)
+ }
+ Text(
+ text = "tinderview",
+ color = Ink.Cream.copy(alpha = 0.72f),
+ style = MaterialTheme.typography.titleMedium,
+ modifier = Modifier.padding(top = 8.dp),
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/discover/DiscoverScreen.kt b/app/src/main/java/com/tinderview/ui/discover/DiscoverScreen.kt
new file mode 100644
index 0000000..856eb4b
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/discover/DiscoverScreen.kt
@@ -0,0 +1,92 @@
+package com.tinderview.ui.discover
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.tinderview.data.SampleProfiles
+import com.tinderview.ui.theme.Ink
+
+@Composable
+fun DiscoverScreen(modifier: Modifier = Modifier) {
+ Column(
+ modifier
+ .fillMaxSize()
+ .background(Ink.Night),
+ ) {
+ Text(
+ text = "Nearby",
+ style = MaterialTheme.typography.headlineMedium,
+ color = Ink.Cream,
+ modifier = Modifier.padding(start = 22.dp, end = 22.dp, top = 18.dp),
+ )
+ Text(
+ text = "People around you tonight",
+ color = Ink.CreamMuted,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(start = 22.dp, end = 22.dp, top = 4.dp, bottom = 8.dp),
+ )
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(2),
+ contentPadding = PaddingValues(16.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ items(SampleProfiles, key = { it.id }) { profile ->
+ val brush = remember(profile.id) { Brush.linearGradient(profile.gradient) }
+ val scrim = remember {
+ Brush.verticalGradient(
+ 0.45f to Color.Transparent,
+ 1f to Color(0xB3000000),
+ )
+ }
+ val shape = RoundedCornerShape(22.dp)
+ Box(
+ modifier = Modifier
+ .aspectRatio(0.78f)
+ .clip(shape)
+ .background(brush)
+ .border(1.dp, Color.White.copy(alpha = 0.10f), shape),
+ ) {
+ Box(Modifier.fillMaxSize().background(scrim))
+ Column(
+ modifier = Modifier
+ .align(Alignment.BottomStart)
+ .padding(14.dp),
+ ) {
+ Text(
+ text = "${profile.name}, ${profile.age}",
+ color = Ink.Cream,
+ style = MaterialTheme.typography.titleMedium,
+ )
+ Text(
+ text = profile.city,
+ color = Ink.Cream.copy(alpha = 0.75f),
+ style = MaterialTheme.typography.labelMedium,
+ modifier = Modifier.padding(top = 2.dp),
+ )
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/theme/Color.kt b/app/src/main/java/com/tinderview/ui/theme/Color.kt
new file mode 100644
index 0000000..8e6ecce
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/theme/Color.kt
@@ -0,0 +1,19 @@
+package com.tinderview.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+internal object Ink {
+ val Night = Color(0xFF0B0A09)
+ val Ink = Color(0xFF121110)
+ val Raised = Color(0xFF1C1A18)
+ val Hairline = Color(0x33F4EFE6)
+ val Cream = Color(0xFFF4EFE6)
+ val CreamMuted = Color(0xFFB8B1A6)
+ val Coral = Color(0xFFFF385C)
+ val CoralDeep = Color(0xFFE31C5F)
+ val Mint = Color(0xFF3DDC97)
+ val Ice = Color(0xFF6BB4FF)
+ val Gold = Color(0xFFE8B86D)
+ val Nope = Color(0xFFFF6B6B)
+ val Wash = Color(0xCC0B0A09)
+}
diff --git a/app/src/main/java/com/tinderview/ui/theme/Icons.kt b/app/src/main/java/com/tinderview/ui/theme/Icons.kt
new file mode 100644
index 0000000..21301fc
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/theme/Icons.kt
@@ -0,0 +1,175 @@
+package com.tinderview.ui.theme
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import kotlin.math.cos
+import kotlin.math.sin
+
+private fun DrawScope.stroke(width: Float = size.minDimension * 0.09f) = Stroke(
+ width = width,
+ cap = StrokeCap.Round,
+ join = StrokeJoin.Round,
+)
+
+@Composable
+internal fun HeartIcon(
+ color: Color,
+ modifier: Modifier = Modifier,
+ size: Dp = 22.dp,
+ filled: Boolean = true,
+) {
+ Canvas(modifier.size(size)) {
+ val w = this.size.width
+ val h = this.size.height
+ val path = Path().apply {
+ moveTo(w * 0.50f, h * 0.84f)
+ cubicTo(w * 0.22f, h * 0.66f, w * 0.10f, h * 0.50f, w * 0.18f, h * 0.32f)
+ cubicTo(w * 0.24f, h * 0.18f, w * 0.40f, h * 0.16f, w * 0.50f, h * 0.30f)
+ cubicTo(w * 0.60f, h * 0.16f, w * 0.76f, h * 0.18f, w * 0.82f, h * 0.32f)
+ cubicTo(w * 0.90f, h * 0.50f, w * 0.78f, h * 0.66f, w * 0.50f, h * 0.84f)
+ close()
+ }
+ drawPath(path, color, style = if (filled) Fill else stroke())
+ }
+}
+
+@Composable
+internal fun CloseIcon(color: Color, modifier: Modifier = Modifier, size: Dp = 20.dp) {
+ Canvas(modifier.size(size)) {
+ val pad = this.size.minDimension * 0.24f
+ val s = stroke()
+ drawLine(color, Offset(pad, pad), Offset(this.size.width - pad, this.size.height - pad), s.width, s.cap)
+ drawLine(color, Offset(this.size.width - pad, pad), Offset(pad, this.size.height - pad), s.width, s.cap)
+ }
+}
+
+@Composable
+internal fun StarIcon(color: Color, modifier: Modifier = Modifier, size: Dp = 20.dp) {
+ Canvas(modifier.size(size)) {
+ val cx = this.size.width / 2f
+ val cy = this.size.height / 2f
+ val outer = this.size.minDimension * 0.46f
+ val inner = outer * 0.44f
+ val path = Path()
+ repeat(10) { i ->
+ val angle = Math.toRadians(-90.0 + i * 36.0)
+ val r = if (i % 2 == 0) outer else inner
+ val x = cx + (cos(angle) * r).toFloat()
+ val y = cy + (sin(angle) * r).toFloat()
+ if (i == 0) path.moveTo(x, y) else path.lineTo(x, y)
+ }
+ path.close()
+ drawPath(path, color)
+ }
+}
+
+@Composable
+internal fun RewindIcon(color: Color, modifier: Modifier = Modifier, size: Dp = 20.dp) {
+ Canvas(modifier.size(size)) {
+ val s = stroke()
+ val m = this.size.minDimension
+ val c = Offset(this.size.width * 0.56f, this.size.height * 0.52f)
+ val r = m * 0.30f
+ drawArc(
+ color = color,
+ startAngle = 50f,
+ sweepAngle = 250f,
+ useCenter = false,
+ topLeft = Offset(c.x - r, c.y - r),
+ size = Size(r * 2, r * 2),
+ style = s,
+ )
+ val tip = Offset(this.size.width * 0.24f, this.size.height * 0.26f)
+ drawPath(
+ Path().apply {
+ moveTo(tip.x - m * 0.02f, tip.y - m * 0.12f)
+ lineTo(tip.x - m * 0.14f, tip.y + m * 0.04f)
+ lineTo(tip.x + m * 0.10f, tip.y + m * 0.06f)
+ close()
+ },
+ color,
+ )
+ }
+}
+
+@Composable
+internal fun SearchIcon(color: Color, modifier: Modifier = Modifier, size: Dp = 18.dp) {
+ Canvas(modifier.size(size)) {
+ val s = stroke()
+ val m = this.size.minDimension
+ val r = m * 0.28f
+ val c = Offset(m * 0.42f, m * 0.42f)
+ drawCircle(color, r, c, style = s)
+ drawLine(
+ color,
+ Offset(c.x + r * 0.72f, c.y + r * 0.72f),
+ Offset(m * 0.82f, m * 0.82f),
+ s.width,
+ StrokeCap.Round,
+ )
+ }
+}
+
+@Composable
+internal fun CardsIcon(color: Color, modifier: Modifier = Modifier, size: Dp = 18.dp) {
+ Canvas(modifier.size(size)) {
+ val s = stroke(this.size.minDimension * 0.085f)
+ val m = this.size.minDimension
+ drawRoundRect(
+ color = color,
+ topLeft = Offset(m * 0.28f, m * 0.16f),
+ size = Size(m * 0.52f, m * 0.64f),
+ cornerRadius = androidx.compose.ui.geometry.CornerRadius(m * 0.10f),
+ style = s,
+ )
+ drawRoundRect(
+ color = color,
+ topLeft = Offset(m * 0.16f, m * 0.26f),
+ size = Size(m * 0.52f, m * 0.64f),
+ cornerRadius = androidx.compose.ui.geometry.CornerRadius(m * 0.10f),
+ style = s,
+ )
+ }
+}
+
+@Composable
+internal fun ChatIcon(color: Color, modifier: Modifier = Modifier, size: Dp = 18.dp) {
+ Canvas(modifier.size(size)) {
+ val s = stroke()
+ val m = this.size.minDimension
+ drawRoundRect(
+ color = color,
+ topLeft = Offset(m * 0.14f, m * 0.16f),
+ size = Size(m * 0.72f, m * 0.50f),
+ cornerRadius = androidx.compose.ui.geometry.CornerRadius(m * 0.16f),
+ style = s,
+ )
+ drawLine(
+ color,
+ Offset(m * 0.32f, m * 0.64f),
+ Offset(m * 0.24f, m * 0.84f),
+ s.width,
+ StrokeCap.Round,
+ )
+ drawLine(
+ color,
+ Offset(m * 0.24f, m * 0.84f),
+ Offset(m * 0.48f, m * 0.64f),
+ s.width,
+ StrokeCap.Round,
+ )
+ }
+}
diff --git a/app/src/main/java/com/tinderview/ui/theme/Theme.kt b/app/src/main/java/com/tinderview/ui/theme/Theme.kt
new file mode 100644
index 0000000..e9f9623
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/theme/Theme.kt
@@ -0,0 +1,28 @@
+package com.tinderview.ui.theme
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.runtime.Composable
+
+private val BrandDark = darkColorScheme(
+ primary = Ink.Coral,
+ onPrimary = Ink.Cream,
+ secondary = Ink.Mint,
+ tertiary = Ink.Ice,
+ background = Ink.Night,
+ surface = Ink.Ink,
+ surfaceVariant = Ink.Raised,
+ onBackground = Ink.Cream,
+ onSurface = Ink.Cream,
+ onSurfaceVariant = Ink.CreamMuted,
+ outline = Ink.Hairline,
+)
+
+@Composable
+fun TinderViewTheme(content: @Composable () -> Unit) {
+ MaterialTheme(
+ colorScheme = BrandDark,
+ typography = TinderViewTypography,
+ content = content,
+ )
+}
diff --git a/app/src/main/java/com/tinderview/ui/theme/Type.kt b/app/src/main/java/com/tinderview/ui/theme/Type.kt
new file mode 100644
index 0000000..8226467
--- /dev/null
+++ b/app/src/main/java/com/tinderview/ui/theme/Type.kt
@@ -0,0 +1,107 @@
+package com.tinderview.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontVariation
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+import com.tinderview.R
+
+@OptIn(ExperimentalTextApi::class)
+internal val Outfit = FontFamily(
+ Font(
+ resId = R.font.outfit,
+ weight = FontWeight.Normal,
+ variationSettings = FontVariation.Settings(FontVariation.weight(400)),
+ ),
+ Font(
+ resId = R.font.outfit,
+ weight = FontWeight.Medium,
+ variationSettings = FontVariation.Settings(FontVariation.weight(500)),
+ ),
+ Font(
+ resId = R.font.outfit,
+ weight = FontWeight.SemiBold,
+ variationSettings = FontVariation.Settings(FontVariation.weight(600)),
+ ),
+ Font(
+ resId = R.font.outfit,
+ weight = FontWeight.Bold,
+ variationSettings = FontVariation.Settings(FontVariation.weight(700)),
+ ),
+ Font(
+ resId = R.font.outfit,
+ weight = FontWeight.ExtraBold,
+ variationSettings = FontVariation.Settings(FontVariation.weight(800)),
+ ),
+)
+
+internal val TinderViewTypography = Typography(
+ displayLarge = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.ExtraBold,
+ fontSize = 40.sp,
+ lineHeight = 46.sp,
+ letterSpacing = (-0.8).sp,
+ ),
+ displayMedium = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.Bold,
+ fontSize = 34.sp,
+ lineHeight = 40.sp,
+ letterSpacing = (-0.5).sp,
+ ),
+ headlineMedium = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.Bold,
+ fontSize = 28.sp,
+ lineHeight = 34.sp,
+ letterSpacing = (-0.4).sp,
+ ),
+ titleLarge = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 20.sp,
+ lineHeight = 26.sp,
+ letterSpacing = (-0.2).sp,
+ ),
+ titleMedium = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 16.sp,
+ lineHeight = 22.sp,
+ ),
+ bodyLarge = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ ),
+ bodyMedium = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.Normal,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ ),
+ labelLarge = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 14.sp,
+ letterSpacing = 0.1.sp,
+ ),
+ labelMedium = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.Medium,
+ fontSize = 12.sp,
+ letterSpacing = 0.15.sp,
+ ),
+ labelSmall = TextStyle(
+ fontFamily = Outfit,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 11.sp,
+ letterSpacing = 0.4.sp,
+ ),
+)
diff --git a/app/src/main/res/font/outfit.ttf b/app/src/main/res/font/outfit.ttf
new file mode 100644
index 0000000..466d624
Binary files /dev/null and b/app/src/main/res/font/outfit.ttf differ
diff --git a/mobile/src/main/res/mipmap-hdpi/lv_ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from mobile/src/main/res/mipmap-hdpi/lv_ic_launcher.png
rename to app/src/main/res/mipmap-hdpi/ic_launcher.png
diff --git a/mobile/src/main/res/mipmap-mdpi/lv_ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from mobile/src/main/res/mipmap-mdpi/lv_ic_launcher.png
rename to app/src/main/res/mipmap-mdpi/ic_launcher.png
diff --git a/mobile/src/main/res/mipmap-xhdpi/lv_ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from mobile/src/main/res/mipmap-xhdpi/lv_ic_launcher.png
rename to app/src/main/res/mipmap-xhdpi/ic_launcher.png
diff --git a/mobile/src/main/res/mipmap-xxhdpi/lv_ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from mobile/src/main/res/mipmap-xxhdpi/lv_ic_launcher.png
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..40b0ca0
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ TinderView
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..5daca9f
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 1b7886d..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:1.3.0'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- jcenter()
- }
-}
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..81f6a88
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,6 @@
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.compose) apply false
+}
diff --git a/cardstack/build.gradle.kts b/cardstack/build.gradle.kts
new file mode 100644
index 0000000..85ad29a
--- /dev/null
+++ b/cardstack/build.gradle.kts
@@ -0,0 +1,51 @@
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+}
+
+android {
+ namespace = "com.tinderview.cardstack"
+ compileSdk = 36
+
+ defaultConfig {
+ minSdk = 26
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = "17"
+ }
+
+ buildFeatures {
+ compose = true
+ }
+}
+
+kotlin {
+ explicitApi()
+}
+
+dependencies {
+ implementation(platform(libs.compose.bom))
+ implementation(libs.compose.ui)
+ implementation(libs.compose.foundation)
+ implementation(libs.compose.animation)
+ implementation(libs.compose.material3)
+ testImplementation(libs.junit)
+}
diff --git a/cardstack/consumer-rules.pro b/cardstack/consumer-rules.pro
new file mode 100644
index 0000000..21a32bc
--- /dev/null
+++ b/cardstack/consumer-rules.pro
@@ -0,0 +1,2 @@
+# CardStack is Compose-only; keep public API types if a consumer shrinks this module.
+-keep class com.tinderview.cardstack.** { *; }
diff --git a/cardstack/proguard-rules.pro b/cardstack/proguard-rules.pro
new file mode 100644
index 0000000..3763157
--- /dev/null
+++ b/cardstack/proguard-rules.pro
@@ -0,0 +1 @@
+# Library-side rules are intentionally empty; consumers use consumer-rules.pro.
diff --git a/cardstack/src/main/AndroidManifest.xml b/cardstack/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8072ee0
--- /dev/null
+++ b/cardstack/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/cardstack/src/main/java/com/tinderview/cardstack/CardStack.kt b/cardstack/src/main/java/com/tinderview/cardstack/CardStack.kt
new file mode 100644
index 0000000..64ea378
--- /dev/null
+++ b/cardstack/src/main/java/com/tinderview/cardstack/CardStack.kt
@@ -0,0 +1,316 @@
+package com.tinderview.cardstack
+
+import android.provider.Settings
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key as composeKey
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.input.pointer.util.VelocityTracker
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.customActions
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
+import androidx.compose.ui.input.pointer.positionChange
+import kotlin.math.min
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.launch
+
+@Composable
+public fun CardStack(
+ items: List,
+ key: (T) -> Any,
+ modifier: Modifier = Modifier,
+ state: CardStackState = rememberCardStackState { items.size },
+ properties: CardStackProperties = CardStackDefaults.properties(),
+ onSwiped: (T, SwipeDirection) -> Unit = { _, _ -> },
+ onEmpty: () -> Unit = {},
+ onTopCardClick: (T) -> Unit = {},
+ likeOverlay: @Composable (Float) -> Unit = { CardStackDefaults.LikeStamp(it) },
+ passOverlay: @Composable (Float) -> Unit = { CardStackDefaults.PassStamp(it) },
+ superLikeOverlay: @Composable (Float) -> Unit = { CardStackDefaults.SuperLikeStamp(it) },
+ content: @Composable (T) -> Unit,
+) {
+ val context = LocalContext.current
+ val density = LocalDensity.current
+ val haptics = LocalHapticFeedback.current
+ val scope = rememberCoroutineScope()
+ val cardShape = remember { RoundedCornerShape(28.dp) }
+
+ val onSwipedState = rememberUpdatedState(onSwiped)
+ val onEmptyState = rememberUpdatedState(onEmpty)
+ val onClickState = rememberUpdatedState(onTopCardClick)
+ val itemsState = rememberUpdatedState(items)
+
+ SideEffect {
+ state.properties = properties
+ state.itemCountProvider = { items.size }
+ state.onSettledSwipe = { index, direction ->
+ itemsState.value.getOrNull(index)?.let { onSwipedState.value(it, direction) }
+ }
+ }
+ state.reduceMotion = remember(context) {
+ Settings.Global.getFloat(
+ context.contentResolver,
+ Settings.Global.ANIMATOR_DURATION_SCALE,
+ 1f,
+ ) == 0f
+ }
+
+ var notifiedEmpty by remember { mutableStateOf(false) }
+ LaunchedEffect(state.currentIndex, items.size) {
+ if (state.currentIndex >= items.size && items.isNotEmpty() && !notifiedEmpty) {
+ notifiedEmpty = true
+ onEmptyState.value()
+ }
+ if (state.currentIndex < items.size) {
+ notifiedEmpty = false
+ }
+ }
+
+ BoxWithConstraints(modifier = modifier) {
+ val widthPx = constraints.maxWidth.toFloat()
+ val heightPx = constraints.maxHeight.toFloat()
+ SideEffect {
+ state.updateCardSize(Size(widthPx, heightPx))
+ }
+
+ val start = state.currentIndex
+ val lastVisible = min(start + properties.visibleCount - 1, items.lastIndex)
+ val stackOffsetPx = with(density) { properties.stackOffset.toPx() }
+
+ if (items.isEmpty() || start > items.lastIndex) {
+ return@BoxWithConstraints
+ }
+
+ for (index in lastVisible downTo start) {
+ val item = items[index]
+ val depth = index - start
+ val isTop = depth == 0
+ composeKey(key(item)) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .zIndex((properties.visibleCount - depth).toFloat())
+ .graphicsLayer {
+ val progress = state.progressToward(
+ properties.enabledDirections,
+ properties.thresholdFraction,
+ ).second
+ val rise = if (isTop) 0f else progress
+ val scale = 1f - depth * properties.stackScaleStep + rise * properties.stackScaleStep
+ val y = stackOffsetPx * depth * (1f - rise)
+ translationY = y
+ scaleX = scale
+ scaleY = scale
+ if (isTop) {
+ translationX = state.dragOffset.x
+ translationY += state.dragOffset.y
+ rotationZ = CardStackMath.rotationZ(
+ translationX = state.dragOffset.x,
+ divisor = properties.rotationDivisor,
+ maxRotation = properties.maxRotationZ,
+ )
+ shadowElevation = CardStackMath.elevationDp(progress)
+ shape = cardShape
+ clip = false
+ } else {
+ shadowElevation = 2f
+ shape = cardShape
+ clip = false
+ }
+ }
+ .then(
+ if (isTop) {
+ Modifier
+ .semantics {
+ contentDescription = "Top card"
+ customActions = listOf(
+ CustomAccessibilityAction("Like") {
+ scope.launch { state.swipe(SwipeDirection.Right) }
+ true
+ },
+ CustomAccessibilityAction("Pass") {
+ scope.launch { state.swipe(SwipeDirection.Left) }
+ true
+ },
+ CustomAccessibilityAction("Super like") {
+ scope.launch { state.swipe(SwipeDirection.Up) }
+ true
+ },
+ CustomAccessibilityAction("Rewind") {
+ scope.launch { state.rewind() }
+ true
+ },
+ )
+ }
+ .pointerInput(properties, items.size, state.currentIndex) {
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ if (state.isAnimating) return@awaitEachGesture
+ val tracker = VelocityTracker()
+ tracker.addPosition(down.uptimeMillis, down.position)
+ var total = Offset.Zero
+ var dragging = false
+ var crossed = false
+ try {
+ while (true) {
+ val event = awaitPointerEvent()
+ val change = event.changes.firstOrNull { it.id == down.id }
+ if (change == null) {
+ if (dragging) scope.launch { state.snapBack() }
+ break
+ }
+ if (!change.pressed) {
+ if (change.changedToUpIgnoreConsumed()) {
+ if (!dragging) {
+ itemsState.value.getOrNull(state.currentIndex)?.let {
+ onClickState.value(it)
+ }
+ } else {
+ val velocity = tracker.calculateVelocity()
+ val (dir, progress) = state.progressToward(
+ properties.enabledDirections,
+ properties.thresholdFraction,
+ )
+ val commit = CardStackMath.shouldCommit(
+ direction = dir,
+ progress = progress,
+ velocityX = velocity.x,
+ velocityY = velocity.y,
+ flingVelocityPx = state.flingVelocityPx(properties, density),
+ enabled = properties.enabledDirections,
+ )
+ if (commit != null) {
+ if (properties.enableHaptics) {
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress)
+ }
+ scope.launch {
+ state.throwOff(commit, Offset(velocity.x, velocity.y))
+ }
+ } else {
+ scope.launch { state.snapBack() }
+ }
+ }
+ } else if (dragging) {
+ scope.launch { state.snapBack() }
+ }
+ break
+ }
+ val delta = change.positionChange()
+ total += delta
+ tracker.addPosition(change.uptimeMillis, change.position)
+ if (!dragging && total.getDistance() > viewConfiguration.touchSlop) {
+ dragging = true
+ }
+ if (dragging) {
+ change.consume()
+ val (cx, cy) = CardStackMath.constrainDrag(
+ total.x,
+ total.y,
+ properties.enabledDirections,
+ )
+ state.dragTo(Offset(cx, cy))
+ val progress = state.progressToward(
+ properties.enabledDirections,
+ properties.thresholdFraction,
+ ).second
+ if (properties.enableHaptics && !crossed && progress >= 1f) {
+ crossed = true
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove)
+ }
+ if (progress < 1f) crossed = false
+ }
+ }
+ } catch (_: CancellationException) {
+ scope.launch { state.snapBack() }
+ }
+ }
+ }
+ } else {
+ Modifier
+ },
+ ),
+ ) {
+ content(item)
+ if (isTop) {
+ OverlayLayer(
+ state = state,
+ properties = properties,
+ likeOverlay = likeOverlay,
+ passOverlay = passOverlay,
+ superLikeOverlay = superLikeOverlay,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun OverlayLayer(
+ state: CardStackState,
+ properties: CardStackProperties,
+ likeOverlay: @Composable (Float) -> Unit,
+ passOverlay: @Composable (Float) -> Unit,
+ superLikeOverlay: @Composable (Float) -> Unit,
+) {
+ val (direction, progress) = state.progressToward(
+ properties.enabledDirections,
+ properties.thresholdFraction,
+ )
+ if (properties.enableColorWash && direction != null && progress > 0f) {
+ val wash = when (direction) {
+ SwipeDirection.Right -> Color(0xFF2ECC71)
+ SwipeDirection.Left -> Color(0xFFE74C3C)
+ SwipeDirection.Up -> Color(0xFF3498DB)
+ }
+ Box(
+ Modifier
+ .fillMaxSize()
+ .graphicsLayer { alpha = progress * 0.12f }
+ .background(
+ Brush.horizontalGradient(
+ listOf(
+ if (direction == SwipeDirection.Left) wash else Color.Transparent,
+ if (direction == SwipeDirection.Up) wash else Color.Transparent,
+ if (direction == SwipeDirection.Right) wash else Color.Transparent,
+ ),
+ ),
+ ),
+ )
+ }
+ val likeProgress = if (direction == SwipeDirection.Right) progress else 0f
+ val passProgress = if (direction == SwipeDirection.Left) progress else 0f
+ val superProgress = if (direction == SwipeDirection.Up) progress else 0f
+ if (likeProgress > 0f) likeOverlay(likeProgress)
+ if (passProgress > 0f) passOverlay(passProgress)
+ if (superProgress > 0f) superLikeOverlay(superProgress)
+}
diff --git a/cardstack/src/main/java/com/tinderview/cardstack/CardStackDefaults.kt b/cardstack/src/main/java/com/tinderview/cardstack/CardStackDefaults.kt
new file mode 100644
index 0000000..f811752
--- /dev/null
+++ b/cardstack/src/main/java/com/tinderview/cardstack/CardStackDefaults.kt
@@ -0,0 +1,89 @@
+package com.tinderview.cardstack
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.rotate
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+public object CardStackDefaults {
+
+ public fun properties(): CardStackProperties = CardStackProperties()
+
+ @Composable
+ public fun LikeStamp(progress: Float) {
+ Stamp(
+ label = "LIKE",
+ color = Color(0xFF2ECC71),
+ rotation = -18f,
+ progress = progress,
+ alignment = Alignment.TopStart,
+ )
+ }
+
+ @Composable
+ public fun PassStamp(progress: Float) {
+ Stamp(
+ label = "NOPE",
+ color = Color(0xFFE74C3C),
+ rotation = 18f,
+ progress = progress,
+ alignment = Alignment.TopEnd,
+ )
+ }
+
+ @Composable
+ public fun SuperLikeStamp(progress: Float) {
+ Stamp(
+ label = "SUPER",
+ color = Color(0xFF3498DB),
+ rotation = 0f,
+ progress = progress,
+ alignment = Alignment.TopCenter,
+ )
+ }
+}
+
+@Composable
+private fun Stamp(
+ label: String,
+ color: Color,
+ rotation: Float,
+ progress: Float,
+ alignment: Alignment,
+) {
+ val shape = remember { RoundedCornerShape(6.dp) }
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .clearAndSetSemantics { }
+ .padding(20.dp),
+ contentAlignment = alignment,
+ ) {
+ val alpha = progress.coerceIn(0f, 1f)
+ Text(
+ text = label,
+ color = color.copy(alpha = alpha),
+ fontSize = 28.sp,
+ fontWeight = FontWeight.Black,
+ letterSpacing = 3.sp,
+ modifier = Modifier
+ .rotate(rotation)
+ .background(color.copy(alpha = alpha * 0.12f), shape)
+ .border(2.dp, color.copy(alpha = alpha), shape)
+ .padding(horizontal = 12.dp, vertical = 5.dp),
+ )
+ }
+}
diff --git a/cardstack/src/main/java/com/tinderview/cardstack/CardStackMath.kt b/cardstack/src/main/java/com/tinderview/cardstack/CardStackMath.kt
new file mode 100644
index 0000000..6091b0a
--- /dev/null
+++ b/cardstack/src/main/java/com/tinderview/cardstack/CardStackMath.kt
@@ -0,0 +1,129 @@
+package com.tinderview.cardstack
+
+import kotlin.math.abs
+import kotlin.math.max
+
+/**
+ * Pure geometry for the deck. Kept allocation-free so unit tests and the
+ * gesture path share one implementation.
+ */
+public object CardStackMath {
+
+ public fun rotationZ(
+ translationX: Float,
+ divisor: Float,
+ maxRotation: Float,
+ ): Float {
+ if (divisor == 0f) return 0f
+ return (translationX / divisor).coerceIn(-maxRotation, maxRotation)
+ }
+
+ public fun dominantDirection(offsetX: Float, offsetY: Float): SwipeDirection? {
+ if (offsetX == 0f && offsetY == 0f) return null
+ val ax = abs(offsetX)
+ val ay = abs(offsetY)
+ return when {
+ ay > ax && offsetY < 0f -> SwipeDirection.Up
+ offsetX > 0f -> SwipeDirection.Right
+ offsetX < 0f -> SwipeDirection.Left
+ offsetY < 0f -> SwipeDirection.Up
+ else -> null
+ }
+ }
+
+ public fun axisDistance(
+ offsetX: Float,
+ offsetY: Float,
+ direction: SwipeDirection,
+ ): Float {
+ val raw = when (direction) {
+ SwipeDirection.Left -> -offsetX
+ SwipeDirection.Right -> offsetX
+ SwipeDirection.Up -> -offsetY
+ }
+ return raw.coerceAtLeast(0f)
+ }
+
+ public fun progressTowardCommit(
+ offsetX: Float,
+ offsetY: Float,
+ cardWidth: Float,
+ thresholdFraction: Float,
+ enabled: Set,
+ ): Pair {
+ val direction = dominantDirection(offsetX, offsetY) ?: return null to 0f
+ if (direction !in enabled) return direction to 0f
+ val thresholdPx = cardWidth * thresholdFraction
+ if (thresholdPx <= 0f) return direction to 1f
+ val distance = axisDistance(offsetX, offsetY, direction)
+ return direction to (distance / thresholdPx).coerceIn(0f, 1f)
+ }
+
+ public fun shouldCommit(
+ direction: SwipeDirection?,
+ progress: Float,
+ velocityX: Float,
+ velocityY: Float,
+ flingVelocityPx: Float,
+ enabled: Set,
+ ): SwipeDirection? {
+ if (direction == null || direction !in enabled) return null
+ val flingSpeed = when (direction) {
+ SwipeDirection.Left -> -velocityX
+ SwipeDirection.Right -> velocityX
+ SwipeDirection.Up -> -velocityY
+ }
+ return if (progress >= 1f || flingSpeed >= flingVelocityPx) direction else null
+ }
+
+ public fun nextIndex(current: Int, count: Int): Int {
+ val next = current + 1
+ return if (next <= count) next else current
+ }
+
+ public fun previousIndex(current: Int): Int = if (current > 0) current - 1 else 0
+
+ public fun rubberBand(
+ value: Float,
+ allow: Boolean,
+ coefficient: Float = 0.28f,
+ ): Float {
+ if (allow) return value
+ return value * coefficient / (1f + abs(value) * 0.002f)
+ }
+
+ public fun constrainDrag(
+ offsetX: Float,
+ offsetY: Float,
+ enabled: Set,
+ ): Pair {
+ val xAllowed =
+ (offsetX > 0f && SwipeDirection.Right in enabled) ||
+ (offsetX < 0f && SwipeDirection.Left in enabled)
+ val yAllowed = offsetY < 0f && SwipeDirection.Up in enabled
+ return rubberBand(offsetX, xAllowed) to rubberBand(offsetY, yAllowed)
+ }
+
+ public fun resolvedDimension(value: Float, fallback: Float): Float {
+ return if (value < 8f) fallback else value
+ }
+
+ public fun exitTarget(
+ direction: SwipeDirection,
+ cardWidth: Float,
+ cardHeight: Float,
+ currentX: Float,
+ currentY: Float,
+ ): Pair {
+ val extra = max(cardWidth, cardHeight) * 1.15f
+ return when (direction) {
+ SwipeDirection.Right -> (cardWidth + extra) to (currentY * 1.2f)
+ SwipeDirection.Left -> -(cardWidth + extra) to (currentY * 1.2f)
+ SwipeDirection.Up -> currentX to -(cardHeight + extra)
+ }
+ }
+
+ public fun elevationDp(progress: Float, minDp: Float = 2f, maxDp: Float = 10f): Float {
+ return minDp + (maxDp - minDp) * progress.coerceIn(0f, 1f)
+ }
+}
diff --git a/cardstack/src/main/java/com/tinderview/cardstack/CardStackProperties.kt b/cardstack/src/main/java/com/tinderview/cardstack/CardStackProperties.kt
new file mode 100644
index 0000000..9537b6a
--- /dev/null
+++ b/cardstack/src/main/java/com/tinderview/cardstack/CardStackProperties.kt
@@ -0,0 +1,42 @@
+package com.tinderview.cardstack
+
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.spring
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+/**
+ * Tunables for stack depth, commit threshold, and motion.
+ * Threshold is a fraction of card width — never a raw pixel value.
+ */
+@Immutable
+public data class CardStackProperties(
+ public val visibleCount: Int = 3,
+ public val stackOffset: Dp = 12.dp,
+ public val stackScaleStep: Float = 0.05f,
+ public val maxRotationZ: Float = 14f,
+ public val rotationDivisor: Float = 25f,
+ public val thresholdFraction: Float = 0.35f,
+ public val flingVelocity: Dp = 900.dp,
+ public val enabledDirections: Set = setOf(
+ SwipeDirection.Left,
+ SwipeDirection.Right,
+ SwipeDirection.Up,
+ ),
+ public val enableHaptics: Boolean = true,
+ public val enableColorWash: Boolean = true,
+ public val snapSpring: SpringSpec = spring(
+ dampingRatio = 0.72f,
+ stiffness = 400f,
+ ),
+ public val throwSpring: SpringSpec = spring(
+ dampingRatio = 0.86f,
+ stiffness = 260f,
+ ),
+ public val rewindSpring: SpringSpec = spring(
+ dampingRatio = Spring.DampingRatioLowBouncy,
+ stiffness = 380f,
+ ),
+)
diff --git a/cardstack/src/main/java/com/tinderview/cardstack/CardStackState.kt b/cardstack/src/main/java/com/tinderview/cardstack/CardStackState.kt
new file mode 100644
index 0000000..711bf60
--- /dev/null
+++ b/cardstack/src/main/java/com/tinderview/cardstack/CardStackState.kt
@@ -0,0 +1,196 @@
+package com.tinderview.cardstack
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.spring
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.unit.Density
+
+@Composable
+public fun rememberCardStackState(
+ initialIndex: Int = 0,
+ itemCount: () -> Int,
+): CardStackState {
+ val state = remember {
+ CardStackState(initialIndex = initialIndex, itemCount = itemCount)
+ }
+ state.itemCountProvider = itemCount
+ return state
+}
+
+@Stable
+public class CardStackState internal constructor(
+ initialIndex: Int,
+ itemCount: () -> Int,
+) {
+ internal var itemCountProvider: () -> Int = itemCount
+
+ public var currentIndex: Int by mutableIntStateOf(initialIndex.coerceAtLeast(0))
+ internal set
+
+ internal val offset = Animatable(Offset.Zero, Offset.VectorConverter)
+
+ internal var fingerOffset: Offset by mutableStateOf(Offset.Zero)
+
+ internal var cardSize: Size by mutableStateOf(Size.Zero)
+
+ internal var lastDirection: SwipeDirection? by mutableStateOf(null)
+
+ internal var lastExit: Offset by mutableStateOf(Offset.Zero)
+
+ internal var properties: CardStackProperties = CardStackProperties()
+
+ internal var reduceMotion: Boolean = false
+
+ internal var onSettledSwipe: ((swipedIndex: Int, direction: SwipeDirection) -> Unit)? = null
+
+ internal var animating: Boolean by mutableStateOf(false)
+
+ public val isAnimating: Boolean
+ get() = animating || offset.isRunning
+
+ public val canRewind: Boolean
+ get() = lastDirection != null && currentIndex > 0 && !isAnimating
+
+ public val canSwipe: Boolean
+ get() = currentIndex < itemCountProvider() && !isAnimating
+
+ public val dragOffset: Offset
+ get() = if (offset.isRunning || animating) offset.value else fingerOffset
+
+ public val swipeProgress: Float
+ get() = progressToward(properties.enabledDirections, properties.thresholdFraction).second
+
+ internal fun resolvedCardSize(): Size {
+ val width = CardStackMath.resolvedDimension(cardSize.width, 1080f)
+ val height = CardStackMath.resolvedDimension(cardSize.height, 1920f)
+ return Size(width, height)
+ }
+
+ internal fun progressToward(
+ enabled: Set,
+ thresholdFraction: Float = 0.35f,
+ ): Pair {
+ val current = dragOffset
+ val size = resolvedCardSize()
+ return CardStackMath.progressTowardCommit(
+ offsetX = current.x,
+ offsetY = current.y,
+ cardWidth = size.width,
+ thresholdFraction = thresholdFraction,
+ enabled = enabled,
+ )
+ }
+
+ internal fun updateCardSize(size: Size) {
+ if (size != cardSize && size.width > 0f && size.height > 0f) {
+ cardSize = size
+ }
+ }
+
+ public suspend fun swipe(direction: SwipeDirection) {
+ if (!canSwipe) return
+ throwOff(direction, initialVelocity = Offset.Zero)
+ }
+
+ public suspend fun rewind() {
+ if (!canRewind) return
+ val exit = lastExit
+ // Place the returning card off-screen *before* the index moves or
+ // [animating] flips, otherwise one frame draws it at rest in the center.
+ fingerOffset = exit
+ currentIndex = CardStackMath.previousIndex(currentIndex)
+ offset.snapTo(exit)
+ animating = true
+ try {
+ if (reduceMotion) {
+ offset.snapTo(Offset.Zero)
+ } else {
+ offset.animateTo(Offset.Zero, offsetSpring(properties.rewindSpring))
+ }
+ fingerOffset = Offset.Zero
+ lastDirection = null
+ lastExit = Offset.Zero
+ } finally {
+ animating = false
+ }
+ }
+
+ internal fun dragTo(target: Offset) {
+ fingerOffset = target
+ }
+
+ internal suspend fun snapBack() {
+ animating = true
+ try {
+ offset.snapTo(fingerOffset)
+ if (reduceMotion) {
+ offset.snapTo(Offset.Zero)
+ } else {
+ offset.animateTo(Offset.Zero, offsetSpring(properties.snapSpring))
+ }
+ fingerOffset = Offset.Zero
+ } finally {
+ animating = false
+ }
+ }
+
+ internal suspend fun throwOff(
+ direction: SwipeDirection,
+ initialVelocity: Offset,
+ ) {
+ if (currentIndex >= itemCountProvider()) return
+ val swipedIndex = currentIndex
+ animating = true
+ try {
+ val size = resolvedCardSize()
+ val (tx, ty) = CardStackMath.exitTarget(
+ direction = direction,
+ cardWidth = size.width,
+ cardHeight = size.height,
+ currentX = dragOffset.x,
+ currentY = dragOffset.y,
+ )
+ val target = Offset(tx, ty)
+ offset.snapTo(fingerOffset)
+ if (reduceMotion) {
+ offset.snapTo(target)
+ } else {
+ offset.animateTo(
+ targetValue = target,
+ animationSpec = offsetSpring(properties.throwSpring),
+ initialVelocity = initialVelocity,
+ )
+ }
+ fingerOffset = Offset.Zero
+ lastDirection = direction
+ lastExit = target
+ currentIndex = CardStackMath.nextIndex(currentIndex, itemCountProvider())
+ offset.snapTo(Offset.Zero)
+ onSettledSwipe?.invoke(swipedIndex, direction)
+ } finally {
+ animating = false
+ }
+ }
+
+ internal fun flingVelocityPx(properties: CardStackProperties, density: Density): Float {
+ return with(density) { properties.flingVelocity.toPx() }
+ }
+}
+
+internal fun offsetSpring(spec: SpringSpec): SpringSpec {
+ return spring(
+ dampingRatio = spec.dampingRatio,
+ stiffness = spec.stiffness,
+ visibilityThreshold = Offset(1f, 1f),
+ )
+}
diff --git a/cardstack/src/main/java/com/tinderview/cardstack/SwipeDirection.kt b/cardstack/src/main/java/com/tinderview/cardstack/SwipeDirection.kt
new file mode 100644
index 0000000..e774203
--- /dev/null
+++ b/cardstack/src/main/java/com/tinderview/cardstack/SwipeDirection.kt
@@ -0,0 +1,11 @@
+package com.tinderview.cardstack
+
+/**
+ * Physical swipe axes the deck can commit along.
+ * Apps map these to product actions (Right = like, Left = pass, Up = super like).
+ */
+public enum class SwipeDirection {
+ Left,
+ Right,
+ Up,
+}
diff --git a/cardstack/src/test/java/com/tinderview/cardstack/CardStackMathTest.kt b/cardstack/src/test/java/com/tinderview/cardstack/CardStackMathTest.kt
new file mode 100644
index 0000000..4a93c58
--- /dev/null
+++ b/cardstack/src/test/java/com/tinderview/cardstack/CardStackMathTest.kt
@@ -0,0 +1,146 @@
+package com.tinderview.cardstack
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+public class CardStackMathTest {
+
+ private val allDirections = setOf(
+ SwipeDirection.Left,
+ SwipeDirection.Right,
+ SwipeDirection.Up,
+ )
+
+ @Test
+ public fun rotationIsClamped() {
+ val rotation = CardStackMath.rotationZ(1000f, divisor = 25f, maxRotation = 14f)
+ assertEquals(14f, rotation, 0.01f)
+ }
+
+ @Test
+ public fun dominantDirectionPrefersVerticalWhenUp() {
+ assertEquals(SwipeDirection.Up, CardStackMath.dominantDirection(10f, -80f))
+ assertEquals(SwipeDirection.Right, CardStackMath.dominantDirection(80f, -10f))
+ assertEquals(SwipeDirection.Left, CardStackMath.dominantDirection(-80f, 4f))
+ }
+
+ @Test
+ public fun progressReachesOneAtThreshold() {
+ val (_, progress) = CardStackMath.progressTowardCommit(
+ offsetX = 140f,
+ offsetY = 0f,
+ cardWidth = 400f,
+ thresholdFraction = 0.35f,
+ enabled = allDirections,
+ )
+ assertEquals(1f, progress, 0.01f)
+ }
+
+ @Test
+ public fun progressIsZeroWhenDirectionDisabled() {
+ val (_, progress) = CardStackMath.progressTowardCommit(
+ offsetX = 0f,
+ offsetY = -200f,
+ cardWidth = 400f,
+ thresholdFraction = 0.35f,
+ enabled = setOf(SwipeDirection.Left, SwipeDirection.Right),
+ )
+ assertEquals(0f, progress, 0.01f)
+ }
+
+ @Test
+ public fun flingCommitsEvenBelowDistanceThreshold() {
+ val commit = CardStackMath.shouldCommit(
+ direction = SwipeDirection.Right,
+ progress = 0.2f,
+ velocityX = 1200f,
+ velocityY = 10f,
+ flingVelocityPx = 900f,
+ enabled = allDirections,
+ )
+ assertEquals(SwipeDirection.Right, commit)
+ }
+
+ @Test
+ public fun weakFlingDoesNotCommit() {
+ val commit = CardStackMath.shouldCommit(
+ direction = SwipeDirection.Right,
+ progress = 0.2f,
+ velocityX = 200f,
+ velocityY = 0f,
+ flingVelocityPx = 900f,
+ enabled = allDirections,
+ )
+ assertNull(commit)
+ }
+
+ @Test
+ public fun indexAdvancesAndRewinds() {
+ assertEquals(1, CardStackMath.nextIndex(0, 5))
+ assertEquals(5, CardStackMath.nextIndex(5, 5))
+ assertEquals(0, CardStackMath.previousIndex(0))
+ assertEquals(2, CardStackMath.previousIndex(3))
+ }
+
+ @Test
+ public fun rubberBandReducesDisabledAxis() {
+ val raw = 200f
+ val banded = CardStackMath.rubberBand(raw, allow = false)
+ assertTrue(banded < raw && banded > 0f)
+ }
+
+ @Test
+ public fun distanceThresholdCommitsWithoutFling() {
+ val commit = CardStackMath.shouldCommit(
+ direction = SwipeDirection.Left,
+ progress = 1f,
+ velocityX = 0f,
+ velocityY = 0f,
+ flingVelocityPx = 900f,
+ enabled = allDirections,
+ )
+ assertEquals(SwipeDirection.Left, commit)
+ }
+
+ @Test
+ public fun constrainDragRubberBandsDisabledUp() {
+ val (x, y) = CardStackMath.constrainDrag(
+ offsetX = 80f,
+ offsetY = -200f,
+ enabled = setOf(SwipeDirection.Left, SwipeDirection.Right),
+ )
+ assertEquals(80f, x, 0.01f)
+ assertTrue(kotlin.math.abs(y) < 200f)
+ }
+
+ @Test
+ public fun exitTargetLeavesTheScreen() {
+ val (rightX, _) = CardStackMath.exitTarget(SwipeDirection.Right, 400f, 600f, 20f, 10f)
+ val (leftX, _) = CardStackMath.exitTarget(SwipeDirection.Left, 400f, 600f, -20f, 10f)
+ val (_, upY) = CardStackMath.exitTarget(SwipeDirection.Up, 400f, 600f, 0f, -20f)
+ assertTrue(rightX > 400f)
+ assertTrue(leftX < -400f)
+ assertTrue(upY < -600f)
+ }
+
+ @Test
+ public fun resolvedDimensionFallsBackWhenUnmeasured() {
+ assertEquals(1080f, CardStackMath.resolvedDimension(0f, 1080f), 0.01f)
+ assertEquals(1080f, CardStackMath.resolvedDimension(1f, 1080f), 0.01f)
+ assertEquals(400f, CardStackMath.resolvedDimension(400f, 1080f), 0.01f)
+ }
+
+ @Test
+ public fun progressIsHalfAtMidThreshold() {
+ val (_, progress) = CardStackMath.progressTowardCommit(
+ offsetX = 70f,
+ offsetY = 0f,
+ cardWidth = 400f,
+ thresholdFraction = 0.35f,
+ enabled = allDirections,
+ )
+ assertEquals(0.5f, progress, 0.02f)
+ }
+}
diff --git a/docs/.gitkeep b/docs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/docs/demo.mp4 b/docs/demo.mp4
new file mode 100644
index 0000000..27eef7c
Binary files /dev/null and b/docs/demo.mp4 differ
diff --git a/gradle.properties b/gradle.properties
index 1d3591c..f065224 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,9 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
+org.gradle.parallel=true
+org.gradle.caching=true
+org.gradle.configuration-cache=true
+android.useAndroidX=true
+android.nonTransitiveRClass=true
+kotlin.code.style=official
+kotlin.incremental=true
+android.suppressUnsupportedCompileSdk=36
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..97e442a
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,35 @@
+[versions]
+agp = "8.7.3"
+kotlin = "2.0.21"
+compose-bom = "2024.12.01"
+activity-compose = "1.9.3"
+core-ktx = "1.15.0"
+lifecycle-runtime = "2.8.7"
+junit = "4.13.2"
+androidx-junit = "1.2.1"
+espresso = "3.6.1"
+compose-ui-test = "1.7.6"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
+androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle-runtime" }
+compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
+compose-ui = { group = "androidx.compose.ui", name = "ui" }
+compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+compose-animation = { group = "androidx.compose.animation", name = "animation" }
+compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-junit" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+android-library = { id = "com.android.library", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64..a4b76b9 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a08963a..e2847c8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Wed Sep 30 14:06:07 IST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
diff --git a/gradlew b/gradlew
index 91a7e26..f5feea6 100755
--- a/gradlew
+++ b/gradlew
@@ -1,79 +1,130 @@
-#!/usr/bin/env bash
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
-warn ( ) {
+warn () {
echo "$*"
-}
+} >&2
-die ( ) {
+die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -82,83 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=$((i+1))
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index aec9973..9b42019 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,4 +1,22 @@
-@if "%DEBUG%" == "" @echo off
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -8,26 +26,30 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -35,54 +57,36 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/mobile/.gitignore b/mobile/.gitignore
deleted file mode 100644
index 796b96d..0000000
--- a/mobile/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/mobile/build.gradle b/mobile/build.gradle
deleted file mode 100644
index 061664e..0000000
--- a/mobile/build.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 23
- buildToolsVersion "23.0.1"
-
- defaultConfig {
- applicationId "com.tinderview"
- minSdkVersion 19
- targetSdkVersion 23
- versionCode 1
- versionName "0.1"
- }
- buildTypes {
- release {
-
- minifyEnabled true
- shrinkResources true
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-/*repositories {
- flatDir {
- dirs 'libs'
- }
-}*/
-
-dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- wearApp project(':wear')
- compile 'com.android.support:appcompat-v7:23.0.1'
-
-}
diff --git a/mobile/mobile.iml b/mobile/mobile.iml
deleted file mode 100644
index 2f50646..0000000
--- a/mobile/mobile.iml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- generateDebugAndroidTestSources
- generateDebugSources
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/proguard-rules.pro b/mobile/proguard-rules.pro
deleted file mode 100644
index b89bfd7..0000000
--- a/mobile/proguard-rules.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in /Users/Aradh/Library/Android/sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/mobile/src/androidTest/java/com/tinderview/ApplicationTest.java b/mobile/src/androidTest/java/com/tinderview/ApplicationTest.java
deleted file mode 100644
index e30f53d..0000000
--- a/mobile/src/androidTest/java/com/tinderview/ApplicationTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.tinderview;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * Testing Fundamentals
- */
-public class ApplicationTest extends ApplicationTestCase {
- public ApplicationTest() {
- super(Application.class);
- }
-}
\ No newline at end of file
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
deleted file mode 100644
index 508decc..0000000
--- a/mobile/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/main/java/com/tinderview/activities/MainScreenActivity.java b/mobile/src/main/java/com/tinderview/activities/MainScreenActivity.java
deleted file mode 100755
index 3718e7a..0000000
--- a/mobile/src/main/java/com/tinderview/activities/MainScreenActivity.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package com.tinderview.activities;
-
-
-import android.content.Context;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.tinderview.R;
-import com.tinderview.fragments.Connect;
-import com.tinderview.fragments.Discover;
-import com.tinderview.fragments.Chat;
-
-import com.tinderview.tabbarview.TabBarView;
-import com.tinderview.util.CustomViewPager;
-
-/* created by Aradh Pillai on 05/10/15
-*
-* this class is our home Screen in which all tabs has define
-* Toolbar I have used
-* Customized ViewPager
-* CardStack
-*/
-public class MainScreenActivity extends AppCompatActivity {
-
- //this context using in
- public static Context context;
- public static CustomViewPager mViewPager;
- /*Toolbar use and set the tab dynamically
- setting custom design to Toolbar
- */
- Toolbar toolbar;
- private String TAG = "MainScreenActivity";
- private TabBarView mTabBarView;
- private MainScreenPagerAdapter mMainScreenPagerAdapter;
- //Data
- private int PAGE_COUNT = 3;
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Log.i(TAG, "MainScreenActivity: onCreate()");
- context = this;
- //using sharedPreference to check whether this is using first time on not...
- //even this app is starting first time than from here it will call the OnBoardActivity and updated the value
- //even this app is already started than this screen will work...
-
- //from here it's started
-
- //setting the layout
- setContentView(R.layout.activity_home);
-
- //checking sdk version and giving shadow effect
-
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
- findViewById(R.id.shadow_prelollipop).setVisibility(View.GONE);
- } else {
- findViewById(R.id.shadow_prelollipop).setVisibility(View.VISIBLE);
- }
-
-
- //Setting Custom Toolbar
- setToolBar();
-
- //Setting all 3 Customized Tabs
- setUpCustomTabs();
-
- //Setting the pagerListener
- setPagerListener();
-
- }
-
- //using to set custom design , actions and tabView into toolbar
- private void setToolBar() {
- toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- LayoutInflater mLayoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- //setting TabBarView
- View customTabView = mLayoutInflater.inflate(R.layout.custom_tab_view, null);
- mTabBarView = (TabBarView) customTabView.findViewById(R.id.customTabBar);
- mTabBarView.setStripHeight(7);
-
- mTabBarView.setStripColor(getResources().getColor(R.color.white));
-
- //setting the properties of ActionBar
-
- getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
- getSupportActionBar().setDisplayHomeAsUpEnabled(false);
- getSupportActionBar().setHomeButtonEnabled(false);
-
- //Setting the Customized Toolbar into toolbar object
- toolbar.addView(customTabView);
- }
-
-
- private void setUpCustomTabs() {
-
- //setting of ViewPager
- mMainScreenPagerAdapter = new MainScreenPagerAdapter(getSupportFragmentManager());
- mViewPager = (CustomViewPager) findViewById(R.id.pager);
- mViewPager.setAdapter(mMainScreenPagerAdapter);
-
- //Setting the CustomizedViewPager into Toolbar for tabOption
- mTabBarView.setViewPager(mViewPager);
-
-
- }
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle action bar card_stack_item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
-
- //noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
-
- private void setPagerListener() {
- mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- mTabBarView.onPageScrolled(position, positionOffset, positionOffsetPixels);
- }
-
- @Override
- public void onPageSelected(int position) {
- Log.i(TAG, "Page: " + position);
- mTabBarView.onPageSelected(position);
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mTabBarView.onPageScrollStateChanged(state);
- }
- });
- }
-
-
- // class is implemented with IconTabProvider Interface as well as extends with FragmentStateAdapter for ViewPager
- //with different tabIcons
- public class MainScreenPagerAdapter extends FragmentStatePagerAdapter implements TabBarView.IconTabProvider {
-
-
- //Defining the array for Tab icons..which is going to call dynamically and load it into tabBar of toolbar
- private int[] tab_icons = {
- R.drawable.ic_connect,
- R.drawable.ic_list,
- R.drawable.ic_chat
- };
-
- public MainScreenPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
-
- //this method is returning the ref of our fragments
- @Override
- public Fragment getItem(int pos) {
- switch (pos) {
- case 0:
- return new Connect();
- case 1:
- return new Discover();
- case 2:
- return new Chat();
- default:
- return null;
- }
- }
-
- //returning the number of pages
- @Override
- public int getCount() {
- return PAGE_COUNT;
- }
-
-
- //this is TabBarView.IconTabProvider's method to return the position of icon to load into tabBar of Toolbar
- @Override
- public int getPageIconResId(int position) {
- return tab_icons[position];
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- switch (position) {
- case 0:
- return "Connect";
- case 1:
- return "Discover";
- case 2:
- return "Chat";
- }
- return null;
- }
- }
-}
\ No newline at end of file
diff --git a/mobile/src/main/java/com/tinderview/adapters/CardStackAdapter.java b/mobile/src/main/java/com/tinderview/adapters/CardStackAdapter.java
deleted file mode 100644
index 3126a55..0000000
--- a/mobile/src/main/java/com/tinderview/adapters/CardStackAdapter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.tinderview.adapters;
-
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import com.tinderview.R;
-
-
-/**
- * Created by Aradh Pillaion 07/10/15.
- */
-
-/*
-* cardStackAdapter which is going to hold list of information and setting it into CardStack
-*
-* */
-public class CardStackAdapter extends ArrayAdapter {
-
-
- public CardStackAdapter(Context context, int resource) {
-
- super(context, 0);
- }
-
- @Override
- public View getView(int position, final View contentView, ViewGroup parent) {
- //supply the layout for your card
- TextView v = (TextView) (contentView.findViewById(R.id.helloText));
- v.setText(getItem(position));
- return contentView;
- }
-
-}
diff --git a/mobile/src/main/java/com/tinderview/animation/RippleAnimation.java b/mobile/src/main/java/com/tinderview/animation/RippleAnimation.java
deleted file mode 100755
index 9a44dc2..0000000
--- a/mobile/src/main/java/com/tinderview/animation/RippleAnimation.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package com.tinderview.animation;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.RelativeLayout;
-
-import com.tinderview.R;
-
-import java.util.ArrayList;
-
-/**
- * Created by Aradh Pillai on 14/10/15.
- *
- * This class is creating for Background Ripple Animation
- */
-
-public class RippleAnimation extends RelativeLayout {
-
- private static final int DEFAULT_RIPPLE_COUNT = 6;
- private static final int DEFAULT_DURATION_TIME = 3000;
- private static final float DEFAULT_SCALE = 6.0f;
- private static final int DEFAULT_FILL_TYPE = 0;
-
- private int rippleColor;
- private float rippleStrokeWidth;
- private float rippleRadius;
- private int rippleDurationTime;
- private int rippleAmount;
- private int rippleDelay;
- private float rippleScale;
- private int rippleType;
- private Paint paint;
- private boolean animationRunning = false;
- private AnimatorSet animatorSet;
- private ArrayList animatorList;
- private LayoutParams rippleParams;
- private ArrayList rippleViewList = new ArrayList();
-
-
- //constructor with single parameter
- public RippleAnimation(Context context) {
- super(context);
- }
-
- public RippleAnimation(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
-
- //Parameter Constructor
- public RippleAnimation(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context, attrs);
- }
-
- //initialized and sets all the properties of RippleAnimation
- private void init(final Context context, final AttributeSet attrs) {
- if (isInEditMode())
- return;
-
- if (null == attrs) {
- throw new IllegalArgumentException("Attributes should be provided to this view,");
- }
-
- //created typedArray and setting all properties to typedArray object
- final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleAnimation);
-
- //it will return ripple color even we have specified into xml or else it will get the default ripple Color
- rippleColor = typedArray.getColor(R.styleable.RippleAnimation_rb_color, getResources().getColor(R.color.rippleColor));
-
- //getting rippleStrokeWidth from typedArray object and putting it into rippleStrokeWidth variable
- rippleStrokeWidth = typedArray.getDimension(R.styleable.RippleAnimation_rb_strokeWidth, getResources().getDimension(R.dimen.rippleStrokeWidth));
-
- //rippleRadius contains the default the specified radius even we specified or else it takes default radius...which is 64dp
- rippleRadius = typedArray.getDimension(R.styleable.RippleAnimation_rb_radius, getResources().getDimension(R.dimen.rippleRadius));
-
- //setting rippleAnimation duration is specified or else it takes default Duration Time.
- rippleDurationTime = typedArray.getInt(R.styleable.RippleAnimation_rb_duration, DEFAULT_DURATION_TIME);
-
- //number of ripple if we have specified into design part or else default value it will take.
- rippleAmount = typedArray.getInt(R.styleable.RippleAnimation_rb_rippleAmount, DEFAULT_RIPPLE_COUNT);
-
- //setting scale of ripple
- rippleScale = typedArray.getFloat(R.styleable.RippleAnimation_rb_scale, DEFAULT_SCALE);
-
- //its getting the ripple type and putting it into rippleType variable.
- rippleType = typedArray.getInt(R.styleable.RippleAnimation_rb_type, DEFAULT_FILL_TYPE);
-
- /**
- * Recycles the TypedArray, to be re-used by a later caller. After calling
- * this function you must not ever touch the typed array again.
- *
- * @throws RuntimeException if the TypedArray has already been recycled.
- */
- typedArray.recycle();
-
- rippleDelay = rippleDurationTime / rippleAmount;
-
- paint = new Paint();//creating an object of paint to make ripple based on the ripple type
- paint.setAntiAlias(true);
- if (rippleType == DEFAULT_FILL_TYPE) {
- rippleStrokeWidth = 0;
- paint.setStyle(Paint.Style.FILL);
- } else
- paint.setStyle(Paint.Style.STROKE);
- paint.setColor(rippleColor);
-
- rippleParams = new LayoutParams((int) (2 * (rippleRadius + rippleStrokeWidth)), (int) (2 * (rippleRadius + rippleStrokeWidth)));
- rippleParams.addRule(CENTER_IN_PARENT, TRUE);//setting ripple into center
-
- animatorSet = new AnimatorSet();// creates animationSet object
- animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
- animatorList = new ArrayList();
-
-
- /*based on the number of ripple this for loop create ripple View and put three different animation for this view..
- *
- * */
- for (int i = 0; i < rippleAmount; i++) {
- RippleView rippleView = new RippleView(getContext());//created ripple
- addView(rippleView, rippleParams);//adding view
- rippleViewList.add(rippleView);//putting this view into rippleViewList object
-
-
- //creating objectAnimator object and setting some X Scale properties to object and putting it into animatorList
- final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleX", 1.0f, rippleScale);
- scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE);
- scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);
- scaleXAnimator.setStartDelay(i * rippleDelay);
- scaleXAnimator.setDuration(rippleDurationTime);
- animatorList.add(scaleXAnimator);
-
- //creating objectAnimator object and setting some Y Scale properties to object and putting it into animatorList
- final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleY", 1.0f, rippleScale);
- scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
- scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);
- scaleYAnimator.setStartDelay(i * rippleDelay);
- scaleYAnimator.setDuration(rippleDurationTime);
- animatorList.add(scaleYAnimator);
-
- //setting alpha of the animation and putting it into animatorList
- final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "Alpha", 1.0f, 0f);
- alphaAnimator.setRepeatCount(ObjectAnimator.INFINITE);
- alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);
- alphaAnimator.setStartDelay(i * rippleDelay);
- alphaAnimator.setDuration(rippleDurationTime);
- animatorList.add(alphaAnimator);
- }
-
- //played animator
- animatorSet.playTogether(animatorList);
- }
-
- //used to start ripple Animation
- public void startRippleAnimation() {
- if (!isRippleAnimationRunning()) {
- for (RippleView rippleView : rippleViewList) {
- rippleView.setVisibility(VISIBLE);
- }
- animatorSet.start();//animation started
- animationRunning = true;//setting value as true... this will support to check isRippleAnimation is running or not.
- }
- }
-
- public void stopRippleAnimation() {
- if (isRippleAnimationRunning()) {
- animatorSet.end();
- animationRunning = false;
- }
- }
-
- //stopping ripple Animation using end() method and setting boolean value false to animationRunning
-
- /*call this method to check whether animation is running or not.
- * then basically it return the animationRunning boolean value.
- *
- * even we have stopped animation then it will return false value.
- * or even we have not stopped animation then it will return true value.
- */
- public boolean isRippleAnimationRunning() {
- return animationRunning;
- }
-
- //this class is using to draw the circle
- private class RippleView extends View {
-
- public RippleView(Context context) {
- super(context);
- this.setVisibility(View.INVISIBLE);
- }
-
- //it's drawing ripple based on radius value.
- @Override
- protected void onDraw(Canvas canvas) {
- int radius = (Math.min(getWidth(), getHeight())) / 2;
- canvas.drawCircle(radius, radius, radius - rippleStrokeWidth, paint);
- }
- }
-}
diff --git a/mobile/src/main/java/com/tinderview/cardstatck/animation/RelativeLayoutParamsEvaluator.java b/mobile/src/main/java/com/tinderview/cardstatck/animation/RelativeLayoutParamsEvaluator.java
deleted file mode 100755
index 50c8cf5..0000000
--- a/mobile/src/main/java/com/tinderview/cardstatck/animation/RelativeLayoutParamsEvaluator.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.tinderview.cardstatck.animation;
-
-import android.animation.TypeEvaluator;
-import android.widget.RelativeLayout.LayoutParams;
-
-/*
-* when card discard or reverse then this animation class is calling
-* and this class is overloading the evaluate method of TypeEvaluator class
-* */
-public class RelativeLayoutParamsEvaluator implements TypeEvaluator {
-
-
- @Override
- public LayoutParams evaluate(float fraction, LayoutParams start,
- LayoutParams end) {
-
- LayoutParams result = new LayoutParams(start);
- result.leftMargin += ((end.leftMargin - start.leftMargin) * fraction);
- result.rightMargin += ((end.rightMargin - start.rightMargin) * fraction);
- result.topMargin += ((end.topMargin - start.topMargin) * fraction);
- result.bottomMargin += ((end.bottomMargin - start.bottomMargin) * fraction);
- return result;
- }
-
-}
diff --git a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardAnimator.java b/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardAnimator.java
deleted file mode 100755
index 4c21ef7..0000000
--- a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardAnimator.java
+++ /dev/null
@@ -1,301 +0,0 @@
-package com.tinderview.cardstatck.cardstack;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-import android.widget.RelativeLayout.LayoutParams;
-
-import com.tinderview.R;
-import com.tinderview.activities.MainScreenActivity;
-import com.tinderview.cardstatck.animation.RelativeLayoutParamsEvaluator;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-
-/*
-* CardAnimator class is used to give animation on Card eg: dislike and like animation of card
-* dragging and dropping animation .
-* setting view of Card and arrange it in the form of stack.
-* shadow effect
-* curve effect of card
-* stack view
-* */
-
-public class CardAnimator {
- private static final String DEBUG_TAG = "CardAnimator";
- private static final int REMOTE_DISTANCE = 1000;
- public ArrayList mCardCollection;
- private float mRotation; //dislike rotation value
- private HashMap mLayoutsMap;//used to store the list of card's view
- private RelativeLayout.LayoutParams[] mRemoteLayouts = new RelativeLayout.LayoutParams[4];//creating the four list of stack
- private RelativeLayout.LayoutParams baseLayout;
- private int mStackMargin = 21;//set the margin of cardstack
-
-
- //passing list of cardStack view to mCardCollection. and jumping to setup method
- public CardAnimator(ArrayList viewCollection) {
- mCardCollection = viewCollection;
- setup();
-
- }
-
-
- //calling to setup the each card based on view
- private void setup() {
- mLayoutsMap = new HashMap<>();
-
- for (View v : mCardCollection) {
- //setup basic card layout
- RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
- params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
- params.width = LayoutParams.MATCH_PARENT;
- params.height = LayoutParams.MATCH_PARENT;
-
- v.setLayoutParams(params);
-
- }
-
- baseLayout = (RelativeLayout.LayoutParams) mCardCollection.get(0).getLayoutParams();
- baseLayout = new RelativeLayout.LayoutParams(baseLayout);
-
- initLayout();//calling this method to arrange all the card based on StackView
-
- for (View v : mCardCollection) {
- RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
- RelativeLayout.LayoutParams paramsCopy = new RelativeLayout.LayoutParams(params);
- mLayoutsMap.put(v, paramsCopy);
- }
-
- setupRemotes();
-
- }
-
- /* finding the index of view and creating layoutParams using baseLayout object and those height and width setting to view
- * using method of v.setLayoutParams
- * and setting card scale size to give curve shape to card
- * and moving card toward top of others card, and gives look of stack.
- * */
- public void initLayout() {
- int size = mCardCollection.size();
- for (View v : mCardCollection) {
- int index = mCardCollection.indexOf(v);
- if (index != 0) {
- index -= 1;
- }
- LayoutParams params = new LayoutParams(baseLayout);
- v.setLayoutParams(params);
-
- //calling scale method of CardUtils class to set the margin and than put layout into v
- CardUtils.scale(v, -(size - index - 1) * 8);
-
- /*calling move method of CardUtils class which is going to move the card one by on based on the mStackMargin
- * first index would be 0 so it won't move card and the based on the index it will multiply with the given margin
- * and move card to down.
- */
- CardUtils.move(v, index * mStackMargin, 0);
-
- v.setRotation(0);//setting the rotation of card as 0
-
- //here calling 3 different shadow layout for each Card
- //which will give shadow effect dynamically ...
- if (index == 0)// setting 0 used to add different layout shadow...
- v.setBackground(MainScreenActivity.context.getResources().getDrawable(R.drawable.card_stack_bg_shadow3));
- else if (index == 1)
- v.setBackground(MainScreenActivity.context.getResources().getDrawable(R.drawable.card_stack_bg_shadow2));
- else if (index == 2)
- v.setBackground(MainScreenActivity.context.getResources().getDrawable(R.drawable.card_stack_bg_shadow));
- }
- }
-
- //setting the each card margins and putting view into array of mRemoteLayouts
- private void setupRemotes() {
- View topView = getTopView();
- mRemoteLayouts[0] = CardUtils.getMoveParams(topView, REMOTE_DISTANCE, -REMOTE_DISTANCE);
- mRemoteLayouts[1] = CardUtils.getMoveParams(topView, REMOTE_DISTANCE, REMOTE_DISTANCE);
- mRemoteLayouts[2] = CardUtils.getMoveParams(topView, -REMOTE_DISTANCE, -REMOTE_DISTANCE);
- mRemoteLayouts[3] = CardUtils.getMoveParams(topView, -REMOTE_DISTANCE, REMOTE_DISTANCE);
- }
-
-
- //return the top view of stack.
- private View getTopView() {
- return mCardCollection.get(mCardCollection.size() - 1);
- }
-
-
- private void moveToBack(View child) {
- final ViewGroup parent = (ViewGroup) child.getParent();
- if (null != parent) {
- parent.removeView(child);
- parent.addView(child, 0);
- }
- }
-
- //rearrange the mCardCollection list...
- //
- private void reorder() {
- View temp = getTopView();
- moveToBack(temp);
-
- for (int i = (mCardCollection.size() - 1); i > 0; i--) {
-
- View current = mCardCollection.get(i - 1);
- //current replace next
- mCardCollection.set(i, current);
-
-
- }
- mCardCollection.set(0, temp);
-
- // temp = getTopView();
-
- }
-
- //using to set the discard animation of CardStack into ArrayList
- public void discard(int direction, final AnimatorListener al) {
- AnimatorSet as = new AnimatorSet();
- ArrayList aCollection = new ArrayList<>();
-
-
- final View topView = getTopView();
- RelativeLayout.LayoutParams topParams = (RelativeLayout.LayoutParams) topView.getLayoutParams();
- RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(topParams);
- ValueAnimator discardAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(), layout, mRemoteLayouts[direction]);
-
- discardAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator value) {
- topView.setLayoutParams((LayoutParams) value.getAnimatedValue());
- }
- });
-
- discardAnim.setDuration(100);//setting the discard removed animation time in ms.
- aCollection.add(discardAnim);//putting the discardAnimation to animation ArrayLIst
-
- for (int i = 0; i < mCardCollection.size(); i++) {
- final View v = mCardCollection.get(i);
-
- if (v == topView) continue;
- final View nv = mCardCollection.get(i + 1);
- RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) v.getLayoutParams();
- RelativeLayout.LayoutParams endLayout = new RelativeLayout.LayoutParams(layoutParams);
- ValueAnimator layoutAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(), endLayout, mLayoutsMap.get(nv));
- layoutAnim.setDuration(100);
- layoutAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator value) {
- v.setLayoutParams((LayoutParams) value.getAnimatedValue());
- }
- });
- aCollection.add(layoutAnim);
- }
-
- as.addListener(new AnimatorListenerAdapter() {
-
-
- @Override
- public void onAnimationEnd(Animator animation) {
- reorder();
- if (al != null) {
- al.onAnimationEnd(animation);
- }
- mLayoutsMap = new HashMap();
- for (View v : mCardCollection) {
- RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
- RelativeLayout.LayoutParams paramsCopy = new RelativeLayout.LayoutParams(params);
- mLayoutsMap.put(v, paramsCopy);
- }
-
- }
-
- });
-
-
- as.playTogether(aCollection);
- as.start();
-
-
- }
-
-
- //even card is dragged and not discarded by the user then again this card will come on the top of the stack..
- //this method will give the animation of reverse along with the reverse animation duration
- // time in ms... which is I defined as 250
-
- public void reverse(MotionEvent e1, MotionEvent e2) {
- final View topView = getTopView();
- ValueAnimator rotationAnim = ValueAnimator.ofFloat(mRotation, 0f);
- rotationAnim.setDuration(250);
- rotationAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator v) {
- topView.setRotation(((Float) (v.getAnimatedValue())));
- }
- });
-
- rotationAnim.start();//start the animation
-
- for (final View v : mCardCollection) {
- RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) v.getLayoutParams();
- RelativeLayout.LayoutParams endLayout = new RelativeLayout.LayoutParams(layoutParams);
- ValueAnimator layoutAnim = ValueAnimator.ofObject(new RelativeLayoutParamsEvaluator(), endLayout, mLayoutsMap.get(v));
- layoutAnim.setDuration(250);
- layoutAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator value) {
- v.setLayoutParams((LayoutParams) value.getAnimatedValue());
- }
- });
- layoutAnim.start();
- }
-
- }
-
-
- //dragging method...it's gives the position of object and does the secondary animation of CardStack
- public void drag(MotionEvent e1, MotionEvent e2, float distanceX,
- float distanceY) {
-
- View topView = getTopView();
-
- float rotation_coefficient = 20f;//
-
- RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) topView.getLayoutParams();
- RelativeLayout.LayoutParams topViewLayouts = mLayoutsMap.get(topView);
- int x_diff = (int) ((e2.getRawX() - e1.getRawX()));
- int y_diff = (int) ((e2.getRawY() - e1.getRawY()));
-
- layoutParams.leftMargin = topViewLayouts.leftMargin + x_diff;
- layoutParams.rightMargin = topViewLayouts.rightMargin - x_diff;
- layoutParams.topMargin = topViewLayouts.topMargin + y_diff;
- layoutParams.bottomMargin = topViewLayouts.bottomMargin - y_diff;
-
- mRotation = (x_diff / rotation_coefficient);
- topView.setRotation(mRotation);
- topView.setLayoutParams(layoutParams);
-
- //animate secondary views.
- for (View v : mCardCollection) {
- int index = mCardCollection.indexOf(v);
- if (v != getTopView() && index != 0) {
- LayoutParams l = CardUtils.scaleFrom(v, mLayoutsMap.get(v), (int) (Math.abs(x_diff) * 0.05));
- CardUtils.moveFrom(v, l, 0, (int) (Math.abs(x_diff) * 0.1));
- }
- }
- }
-
- //using to set the margin into cardStack
- public void setStackMargin(int margin) {
- mStackMargin = margin;//passing the value to mStackMargin
- initLayout(); //and then calling this method to set the margin into view.
- }
-
-
-}
diff --git a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardStack.java b/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardStack.java
deleted file mode 100755
index 3dabc03..0000000
--- a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardStack.java
+++ /dev/null
@@ -1,297 +0,0 @@
-package com.tinderview.cardstatck.cardstack;
-
-import java.util.ArrayList;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.FrameLayout;
-import android.widget.RelativeLayout;
-import android.database.DataSetObserver;
-
-
-public class CardStack extends RelativeLayout {
- ArrayList viewCollection = new ArrayList();
- private int mIndex = 0;
- private int mNumVisible = 4;
- private ArrayAdapter> mAdapter;
- private OnTouchListener mOnTouchListener;
- //private Queue mIdleStack = new Queue;
- private CardAnimator mCardAnimator;
- private CardEventListener mEventListener = new DefaultStackEventListener(10);
- private int mContentResource = 0;
-
- //method used to descard the top view based on direction...which is going to use by other widget
- //eg: using button we can call this method and we can pass the direction as parameter eg: 0 or 2 to discard the card...
- private DataSetObserver mOb = new DataSetObserver() {
- @Override
- public void onChanged() {
- reset(false);
- }
- };
-
-
- //return the current index of card
-
- //only necessary when I need the attrs from xml, this will be used when inflating layout
- public CardStack(Context context, AttributeSet attrs) {
- super(context, attrs);
-
-
- //get attrs assign minVisiableNum
- for (int i = 0; i < mNumVisible; i++) {
- addContainerViews();
- }
- setupAnimation();
- }
-
- public CardStack(Context context) {
- super(context);
- }
-
- public void discardTop(final int direction) {
- mCardAnimator.discard(direction, new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator arg0) {
- mCardAnimator.initLayout();
- mIndex++;
- mEventListener.discarded(mIndex, direction);
-
- //mIndex = mIndex%mAdapter.getCount();
- loadLast();
-
- viewCollection.get(0).setOnTouchListener(null);
- viewCollection.get(viewCollection.size() - 1)
- .setOnTouchListener(mOnTouchListener);
- }
- });
- }
-
- public int getCurrIndex() {
- //sync?
- return mIndex;
- }
-
- private void addContainerViews() {
- FrameLayout v = new FrameLayout(getContext());
- viewCollection.add(v);
- addView(v);
- }
-
- public void setStackMargin(int margin) {
- mCardAnimator.setStackMargin(margin);
- mCardAnimator.initLayout();
- }
-
- public void setContentResource(int res) {
- mContentResource = res;
- }
-
- //when need to reset the card into stack
- public void reset(boolean resetIndex) {
- if (resetIndex) mIndex = 0;
- removeAllViews();
- viewCollection.clear();
- for (int i = 0; i < mNumVisible; i++) {
- addContainerViews();
- }
- setupAnimation();
- loadData();
- }
-
- public void setVisibleCardNum(int visiableNum) {
- mNumVisible = visiableNum;
- reset(false);
- }
-
-
- //setting the card animator and putting GestureDetectorListener and overriding the methods of DragListener interface
- //using this method can get to know the dragged card location
- //see the animation effect on that selected card
-
- public void setThreshold(int t) {
- mEventListener = new DefaultStackEventListener(t);
- }
-
- public void setListener(CardEventListener cel) {
- mEventListener = cel;
- }
-
-
- //ArrayList
-
- private void setupAnimation() {
- final View cardView = viewCollection.get(viewCollection.size() - 1);
- mCardAnimator = new CardAnimator(viewCollection); //creating an object of cardAnimator
- mCardAnimator.initLayout(); //initialize the cardAnimator using object
-
- final DragGestureDetector dd = new DragGestureDetector(CardStack.this.getContext(), new DragGestureDetector.DragListener() {
-
-
- //when drag of card will start this method invoke first
- @Override
- public boolean onDragStart(MotionEvent e1, MotionEvent e2,
- float distanceX, float distanceY) {
- mCardAnimator.drag(e1, e2, distanceX, distanceY);
- return true;
- }
-
- //user dragging the card continue then this method will invoke
- @Override
- public boolean onDragContinue(MotionEvent e1, MotionEvent e2,
- float distanceX, float distanceY) {
- float x1 = e1.getRawX();
- float y1 = e1.getRawY();
- float x2 = e2.getRawX();
- float y2 = e2.getRawY();
- //float distance = CardUtils.distance(x1,y1,x2,y2);
- final int direction = CardUtils.direction(x1, y1, x2, y2);
- mCardAnimator.drag(e1, e2, distanceX, distanceY);
- mEventListener.swipeContinue(direction, Math.abs(x2 - x1), Math.abs(y2 - y1));
- return true;
- }
-
- /*when user dum the card then this method will invoke
- * here it will check the distance between the direction and the distance of the card
- *and based on that this method discard or reverse the card
- */
- @Override
- public boolean onDragEnd(MotionEvent e1, MotionEvent e2) {
- //reverse(e1,e2);
- float x1 = e1.getRawX();
- float y1 = e1.getRawY();
- float x2 = e2.getRawX();
- float y2 = e2.getRawY();
- Log.d("onDragEndMethod", "dis:s");
- float distance = CardUtils.distance(x1, y1, x2, y2);
- final int direction = CardUtils.direction(x1, y1, x2, y2);
- Log.d("onDragEndMethod", "dis: " + distance + " direction: " + direction);
- boolean discard = mEventListener.swipeEnd(direction, distance);
- if (discard) {
- Log.d("onDragEndMethod", "deleted");
- mCardAnimator.discard(direction, new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(Animator arg0) {
- mCardAnimator.initLayout();
- mIndex++;
- mEventListener.discarded(mIndex, direction);
-
- //mIndex = mIndex%mAdapter.getCount();
- loadLast();
-
- viewCollection.get(0).setOnTouchListener(null);
- viewCollection.get(viewCollection.size() - 1)
- .setOnTouchListener(mOnTouchListener);
- }
-
- });
- } else {
- Log.d("onDragEndMethod", "reverse");
- mCardAnimator.reverse(e1, e2);
- }
- return true;
- }
-
- @Override
- public boolean onTapUp() {
- mEventListener.topCardTapped();
- return true;
- }
- }
- );
-
- mOnTouchListener = new OnTouchListener() {
- private static final String DEBUG_TAG = "MotionEvents";
-
- @Override
- public boolean onTouch(View arg0, MotionEvent event) {
- dd.onTouchEvent(event);
- return true;
- }
- };
- cardView.setOnTouchListener(mOnTouchListener);
- }
-
- //used to set Adapter
- public void setAdapter(final ArrayAdapter> adapter) {
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(mOb);
- }
- mAdapter = adapter;
- adapter.registerDataSetObserver(mOb);
-
- loadData();
- }
-
- //call to load data based on index
- //this method will put Gone or visible property to view...
- private void loadData() {
- for (int i = mNumVisible - 1; i >= 0; i--) {
- ViewGroup parent = (ViewGroup) viewCollection.get(i);
- int index = (mIndex + mNumVisible - 1) - i;
- if (index > mAdapter.getCount() - 1) {
- parent.setVisibility(View.GONE);
- } else {
- View child = mAdapter.getView(index, getContentView(), this);
- parent.addView(child);
- parent.setVisibility(View.VISIBLE);
- }
- }
- }
-
- //returning a new view even
- private View getContentView() {
- View contentView = null;
- if (mContentResource != 0) {
- LayoutInflater lf = LayoutInflater.from(getContext());
- contentView = lf.inflate(mContentResource, null);
- }
- return contentView;
-
- }
-
- private void loadLast() {
- ViewGroup parent = (ViewGroup) viewCollection.get(0);
-
- int lastIndex = (mNumVisible - 1) + mIndex;
- if (lastIndex > mAdapter.getCount() - 1) {
- parent.setVisibility(View.GONE); //hiding the top view and returning
- return;
- }
-
- View child = mAdapter.getView(lastIndex, getContentView(), parent);
- parent.removeAllViews();// remove all previous view
- parent.addView(child); // and then adding new
- }
-
- //return the size of stack
- public int getStackSize() {
- return mNumVisible;
- }
-
- public interface CardEventListener {
- //section
- // 0 | 1
- //--------
- // 2 | 3
- // swipe distance, most likely be used with height and width of a view ;
-
- boolean swipeEnd(int section, float distance);
-
- boolean swipeStart(int section, float distance);
-
- boolean swipeContinue(int section, float distanceX, float distanceY);
-
- void discarded(int mIndex, int direction);
-
- void topCardTapped();
- }
-}
diff --git a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardUtils.java b/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardUtils.java
deleted file mode 100755
index 1dd5fb1..0000000
--- a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/CardUtils.java
+++ /dev/null
@@ -1,133 +0,0 @@
-package com.tinderview.cardstatck.cardstack;
-
-import android.util.Log;
-import android.view.View;
-import android.widget.RelativeLayout;
-import android.widget.RelativeLayout.LayoutParams;
-
-
-/*
-* CardUtils is using for set the card scale , get the selected layout param, scaleFrom, moveFrom and direction of card.
-* All these method are static which means it is going to access from some others classes and it returns the value based on the
-* passed parameter.
-*
-* */
-public class CardUtils {
- final static int DIRECTION_TOP_LEFT = 0;
- final static int DIRECTION_TOP_RIGHT = 1;
- final static int DIRECTION_BOTTOM_LEFT = 2;
- final static int DIRECTION_BOTTOM_RIGHT = 3;
-
- //it remove some pixels from the each sides of given view and then set that layout to view using setLayoutParams method.
- public static void scale(View v, int pixel) {
- RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
- params.leftMargin -= pixel;
- params.rightMargin -= pixel;
- params.topMargin -= pixel;
- params.bottomMargin -= pixel;
- v.setLayoutParams(params);
- }
-
-
- //setting the layout margin dynamically to make it as stack view and returning the layoutParams object.
- public static LayoutParams getMoveParams(View v, int upDown, int leftRight) {
- RelativeLayout.LayoutParams original = (RelativeLayout.LayoutParams) v.getLayoutParams();
- //RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(original);
- RelativeLayout.LayoutParams params = cloneParams(original);
- params.leftMargin += leftRight;
- params.rightMargin -= leftRight;
- Log.d("moving...", params.leftMargin + "");
-
- params.topMargin -= upDown;
- params.bottomMargin += upDown;
- return params;
- }
-
- //used to move the card based on parameter, from this method again it's calling the getMoveParams(v,upDown,leftRight) methods
- public static void move(View v, int upDown, int leftRight) {
- RelativeLayout.LayoutParams params = getMoveParams(v, upDown, leftRight);
- v.setLayoutParams(params);
- }
-
-
- //setting the scale of card dynamically based on the parameters
- public static LayoutParams scaleFrom(View v, LayoutParams params, int pixel) {
- Log.d("pixel", "onScroll: " + pixel);
- params = cloneParams(params);
- params.leftMargin -= pixel;
- params.rightMargin -= pixel;
- params.topMargin -= pixel;
- params.bottomMargin -= pixel;
- Log.d("pixel", "onScroll: " + pixel);
- v.setLayoutParams(params);
-
- return params;
- }
-
-
- //setting the card margin based on the values using leftRight and Updown variables
- //this method is calling for secondary view
-
- public static LayoutParams moveFrom(View v, LayoutParams params, int leftRight, int upDown) {
- params = cloneParams(params);
- params.leftMargin += leftRight;
- params.rightMargin -= leftRight;
- params.topMargin -= upDown;
- params.bottomMargin += upDown;
- v.setLayoutParams(params);
-
- return params;
- }
-
- //a copy method for RelativeLayout.LayoutParams for backward compatibility
- public static RelativeLayout.LayoutParams cloneParams(RelativeLayout.LayoutParams params) {
- RelativeLayout.LayoutParams copy = new RelativeLayout.LayoutParams(params.width, params.height);
- copy.leftMargin = params.leftMargin;
- copy.topMargin = params.topMargin;
- copy.rightMargin = params.rightMargin;
- copy.bottomMargin = params.bottomMargin;
- int[] rules = params.getRules();
- for (int i = 0; i < rules.length; i++) {
- copy.addRule(i, rules[i]);
- }
- copy.setMarginStart(params.getMarginStart());
- copy.setMarginEnd(params.getMarginEnd());
-
- return copy;
- }
-
-
- //giving the distance in between the cards
- public static float distance(float x1, float y1, float x2, float y2) {
- Log.d("Data: ", "" + Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
- return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
- }
-
-
- // in which direction user dum card, that location this method will return ...
- // we can get to know that whether user has liked or dislike the card...
- //eg:
- // 0 | 1
- //--------
- // 2 | 3
- // dislike value : even this method return the 0 or 2
- // like value: 1 or 3
-
- public static int direction(float x1, float y1, float x2, float y2) {
- if (x2 > x1) {//RIGHT
- if (y2 > y1) {//BOTTOM
- return DIRECTION_BOTTOM_RIGHT;
- } else {//TOP
- return DIRECTION_TOP_RIGHT;
- }
- } else {//LEFT
- if (y2 > y1) {//BOTTOM
- return DIRECTION_BOTTOM_LEFT;
- } else {//TOP
- return DIRECTION_TOP_LEFT;
- }
- }
- }
-
-
-}
diff --git a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/DefaultStackEventListener.java b/mobile/src/main/java/com/tinderview/cardstatck/cardstack/DefaultStackEventListener.java
deleted file mode 100755
index 270472a..0000000
--- a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/DefaultStackEventListener.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.tinderview.cardstatck.cardstack;
-
-
-/*this is using to put the listener event into cardStack
-
- */
-public class DefaultStackEventListener implements CardStack.CardEventListener {
-
- private float mThreshold = 0;
-
- public DefaultStackEventListener(int i) {
- mThreshold = i;
- }
-
- @Override
- public boolean swipeEnd(int section, float distance) {
- return distance > mThreshold;
- }
-
- @Override
- public boolean swipeStart(int section, float distance) {
-
- return true;
- }
-
- @Override
- public boolean swipeContinue(int section, float distanceX, float distanceY) {
- return true;
- }
-
- @Override
- public void discarded(int mIndex, int direction) {
-
- }
-
- @Override
- public void topCardTapped() {
-
- }
-
-
-}
diff --git a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/DragGestureDetector.java b/mobile/src/main/java/com/tinderview/cardstatck/cardstack/DragGestureDetector.java
deleted file mode 100755
index ab23864..0000000
--- a/mobile/src/main/java/com/tinderview/cardstatck/cardstack/DragGestureDetector.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.tinderview.cardstatck.cardstack;
-
-import android.content.Context;
-import android.support.v4.view.GestureDetectorCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-
-//detect both tap and drag based on the gesture
-/*
-* in this class DragListener interface is defined which is going to use to return the boolean value based on the scope of CardStack
-* whether card is start, dragging , dum, etc.
-*
-*
-*
-* */
-
-
-public class DragGestureDetector {
- public static String DEBUG_TAG = "DragGestureDetector";
- GestureDetectorCompat mGestrueDetector;
- DragListener mListener;
- private boolean mStarted = false;
- private boolean mDown = false;
-
- private MotionEvent mOriginalEvent;
-
- public DragGestureDetector(Context context, DragListener myDragListener) {
- mGestrueDetector = new GestureDetectorCompat(context, new MyGestureListener());
- mListener = myDragListener;
- }
-
- public void onTouchEvent(MotionEvent event) {
- mGestrueDetector.onTouchEvent(event);
- int action = MotionEventCompat.getActionMasked(event);
- switch (action) {
- case (MotionEvent.ACTION_UP):
- Log.d(DEBUG_TAG, "Action was UP " + mStarted);
- if (mStarted) {
- mListener.onDragEnd(mOriginalEvent, event);
- mStarted = false;
- }
- break;
-
- case (MotionEvent.ACTION_DOWN):
- Log.d(DEBUG_TAG, "Action was down " + mStarted);
-
-
- if (mDown) {
- mListener.onDragEnd(mOriginalEvent, event);
- mDown = false;
-
- }
- break;
- case (MotionEvent.ACTION_SCROLL):
- Log.d(DEBUG_TAG, "Scroll " + mStarted);
- if (mStarted) {
- mListener.onDragEnd(mOriginalEvent, event);
-
- }
- // break;
-
- //need to set this, quick tap will not generate drap event, so the
- //originalEvent may be null for case action_up
- //which lead to null pointer
- mOriginalEvent = event;
-
- }
- }
-
- public interface DragListener {
- boolean onDragStart(MotionEvent e1, MotionEvent e2, float distanceX,
- float distanceY);
-
- boolean onDragContinue(MotionEvent e1, MotionEvent e2, float distanceX,
- float distanceY);
-
- boolean onDragEnd(MotionEvent e1, MotionEvent e2);
-
- boolean onTapUp();
- }
-
- class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
- float distanceY) {
- if (mListener == null) {
- Log.d(DEBUG_TAG, "NULL");
- return true;
- }
- if (mStarted == false) {
- Log.d(DEBUG_TAG, "onDragStart " + mStarted);
- mListener.onDragStart(e1, e2, distanceX, distanceY);
- mStarted = true;
- } else {
- Log.d(DEBUG_TAG, "onDragContinue " + mStarted);
- mListener.onDragContinue(e1, e2, distanceX, distanceY);
- //mOriginalEvent = e1;
- return true;
- }
- mOriginalEvent = e1;
- return true;
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- Log.d(DEBUG_TAG, "OnSingTapUp" + mStarted);
- return mListener.onTapUp();
- }
- }
-
-
-}
diff --git a/mobile/src/main/java/com/tinderview/fragments/Chat.java b/mobile/src/main/java/com/tinderview/fragments/Chat.java
deleted file mode 100755
index 1a35cbb..0000000
--- a/mobile/src/main/java/com/tinderview/fragments/Chat.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.tinderview.fragments;// Created by Sanat Dutta on 2/17/2015.
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.tinderview.R;
-
-/**
- * Created by Aradh Pillai on 1/10/15.
- */
-/*
-* this fragment is using for chat tab option
-* */
-public class Chat extends Fragment {
-
- private String TAG = "fragment_chat";
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_chat, container, false);
-
- Log.i(TAG, "fragment_chat: onCreateView");
-
- return view;
- }
-}
diff --git a/mobile/src/main/java/com/tinderview/fragments/Connect.java b/mobile/src/main/java/com/tinderview/fragments/Connect.java
deleted file mode 100755
index 390f1fc..0000000
--- a/mobile/src/main/java/com/tinderview/fragments/Connect.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.tinderview.fragments;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.tinderview.R;
-import com.tinderview.adapters.CardStackAdapter;
-import com.tinderview.animation.RippleAnimation;
-import com.tinderview.cardstatck.cardstack.CardStack;
-import com.tinderview.cardstatck.cardstack.DefaultStackEventListener;
-
-/**
- * Created by Aradh Pillai on 2/10/15.
- */
-
-/*
-* this fragment class is using to show the list of connection into stackCard based on users location
-* and user will have option to discard and like the connection.
-*
-*
-* */
-public class Connect extends Fragment {
- RippleAnimation rippleBackground1, rippleBackground2;
-
- CardStack cardStack;
- //this class is using for swipe the AdapterView
- CardStackAdapter mCardAdapter;
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_connect, container, false);
-
- init(view);// to initialize widgets
- doRippleBackground(); //start ripple background work..
- return view;
- }
-
- private void init(View view) {
- //root ripple background initialization
- rippleBackground1 = (RippleAnimation) view.findViewById(R.id.content);
-
- //child ripple background initialization
- // rippleBackground2 = (RippleBackground) view.findViewById(R.id.content2);
-
- //cardStack initialization
- cardStack = (CardStack) view.findViewById(R.id.frame);
-
- //at begin setting rippleBackground visibility as VISIBLE and setting CardStack visibility as GONE
- rippleBackground1.setVisibility(View.VISIBLE);
- cardStack.setVisibility(View.GONE);
-
- //creating adapter
- mCardAdapter = new CardStackAdapter(getActivity().getApplicationContext(), 0);
- }
-
- public void doRippleBackground() {
-
-
- //start ripple background animations
- startAnimation();
-
- //handler created to handle cardStack as well as timer...
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
-
- callCardStack();
- }
- }, 8000);
-
- }
-
- //start the background ripple animation...
- private void startAnimation() {
- //if it's not running
- if (!rippleBackground1.isRippleAnimationRunning()) {
- rippleBackground1.startRippleAnimation();//start root ripple animation
- // rippleBackground2.startRippleAnimation();//start child ripple animation
- }
- }
-
- //this method will stop background ripple animation. if it's running.
- private void stopAnimation() {
- if (rippleBackground1.isRippleAnimationRunning()) {
- rippleBackground1.stopRippleAnimation();
- // rippleBackground2.stopRippleAnimation();
- }
- }
-
- //cardStack view will set it as visible and load the information into stack.
- public void callCardStack() {
-
- cardStack.setVisibility(View.VISIBLE);
- rippleBackground1.setVisibility(View.GONE);
-
- stopAnimation();//start the ripple background animation.
-
- //Setting Resource of CardStack
- cardStack.setContentResource(R.layout.card_stack_item);
-
- //Adding 30 dummy info for CardStack
- for (int i = 0; i <= 30; i++)
- mCardAdapter.add("" + i);
- cardStack.setAdapter(mCardAdapter);
-
- //Setting Listener and passing distance as a parameter ,
- //based on the distance card will discard
- //if dragging card distance would be more than specified distance(100) then card will discard or else card will reverse on same position.
- cardStack.setListener(new DefaultStackEventListener(300));
-
- }
-}
diff --git a/mobile/src/main/java/com/tinderview/fragments/Discover.java b/mobile/src/main/java/com/tinderview/fragments/Discover.java
deleted file mode 100755
index df269b3..0000000
--- a/mobile/src/main/java/com/tinderview/fragments/Discover.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.tinderview.fragments;// Created by Sanat Dutta on 2/17/2015.
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.tinderview.R;
-
-/**
- * Created by Aradh Pillai on 1/10/15.
- */
-public class Discover extends Fragment {
-
- private String TAG = "fragment_discover";
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_discover, container, false);
-
- Log.i(TAG, "fragment_discover: onCreateView");
-
- return view;
- }
-}
diff --git a/mobile/src/main/java/com/tinderview/tabbarview/CheatSheet.java b/mobile/src/main/java/com/tinderview/tabbarview/CheatSheet.java
deleted file mode 100755
index f7dcf04..0000000
--- a/mobile/src/main/java/com/tinderview/tabbarview/CheatSheet.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package com.tinderview.tabbarview;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.Toast;
-
-/**
- * Helper class for showing cheat sheets (tooltips) for icon-only UI elements on long-press. This is
- * already default platform behavior for icon-only {@link android.app.ActionBar} items and tabs.
- * This class provides this behavior for any other such UI element.
- */
-public class CheatSheet {
- /**
- * The estimated height of a toast, in dips (density-independent pixels). This is used to
- * determine whether or not the toast should appear above or below the UI element.
- */
- private static final int ESTIMATED_TOAST_HEIGHT_DIPS = 48;
-
- /**
- * Sets up a cheat sheet (tooltip) for the given view by setting its {@link
- * View.OnLongClickListener}. When the view is long-pressed, a {@link Toast} with
- * the view's {@link View#getContentDescription() content description} will be
- * shown either above (default) or below the view (if there isn't room above it).
- *
- * @param view The view to add a cheat sheet for.
- */
- public static void setup(View view) {
- view.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- return showCheatSheet(view, view.getContentDescription());
- }
- });
- }
-
- /**
- * Sets up a cheat sheet (tooltip) for the given view by setting its {@link
- * View.OnLongClickListener}. When the view is long-pressed, a {@link Toast} with
- * the given text will be shown either above (default) or below the view (if there isn't room
- * above it).
- *
- * @param view The view to add a cheat sheet for.
- * @param textResId The string resource containing the text to show on long-press.
- */
- public static void setup(View view, final int textResId) {
- view.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- return showCheatSheet(view, view.getContext().getString(textResId));
- }
- });
- }
-
- /**
- * Sets up a cheat sheet (tooltip) for the given view by setting its {@link
- * View.OnLongClickListener}. When the view is long-pressed, a {@link Toast} with
- * the given text will be shown either above (default) or below the view (if there isn't room
- * above it).
- *
- * @param view The view to add a cheat sheet for.
- * @param text The text to show on long-press.
- */
- public static void setup(View view, final CharSequence text) {
- view.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View view) {
- return showCheatSheet(view, text);
- }
- });
- }
-
- /**
- * Removes the cheat sheet for the given view by removing the view's {@link
- * View.OnLongClickListener}.
- *
- * @param view The view whose cheat sheet should be removed.
- */
- public static void remove(final View view) {
- view.setOnLongClickListener(null);
- }
-
- /**
- * Internal helper method to show the cheat sheet toast.
- */
- private static boolean showCheatSheet(View view, CharSequence text) {
- if (TextUtils.isEmpty(text)) {
- return false;
- }
-
- final int[] screenPos = new int[2]; // origin is device display
- final Rect displayFrame = new Rect(); // includes decorations (e.g. status bar)
- view.getLocationOnScreen(screenPos);
- view.getWindowVisibleDisplayFrame(displayFrame);
-
- final Context context = view.getContext();
- final int viewWidth = view.getWidth();
- final int viewHeight = view.getHeight();
- final int viewCenterX = screenPos[0] + viewWidth / 2;
- final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
- final int estimatedToastHeight = (int) (ESTIMATED_TOAST_HEIGHT_DIPS
- * context.getResources().getDisplayMetrics().density);
-
- Toast cheatSheet = Toast.makeText(context, text, Toast.LENGTH_SHORT);
- boolean showBelow = screenPos[1] < estimatedToastHeight;
- if (showBelow) {
- // Show below
- // Offsets are after decorations (e.g. status bar) are factored in
- cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
- viewCenterX - screenWidth / 2,
- screenPos[1] - displayFrame.top + viewHeight);
- } else {
- // Show above
- // Offsets are after decorations (e.g. status bar) are factored in
- // NOTE: We can't use Gravity.BOTTOM because when the keyboard is up
- // its height isn't factored in.
- cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
- viewCenterX - screenWidth / 2,
- screenPos[1] - displayFrame.top - estimatedToastHeight);
- }
-
- cheatSheet.show();
- return true;
- }
-}
diff --git a/mobile/src/main/java/com/tinderview/tabbarview/TabBarView.java b/mobile/src/main/java/com/tinderview/tabbarview/TabBarView.java
deleted file mode 100755
index 1f34ddf..0000000
--- a/mobile/src/main/java/com/tinderview/tabbarview/TabBarView.java
+++ /dev/null
@@ -1,297 +0,0 @@
-package com.tinderview.tabbarview;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.ViewPager.OnPageChangeListener;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.widget.LinearLayout;
-
-import com.tinderview.R;
-
-/*
-* created by Aradh Pillai on 1.10.15
-* this class is inheriting features of LinearLayout
-* using this it's creating TabBarView dynamically
-*
-*
-* */
-
-public class TabBarView extends LinearLayout {
- //strip height has defined here
- private static final int STRIP_HEIGHT = 6;
- public static int mSelectedTab = 0;
- public static int tabCount;//hold the number of tabs
- public static int a;
- public final Paint mPaint;
- //this pager will hold the instance of viewPager
- //and rearrange the pages using the object
- public ViewPager pager;
- public OnPageChangeListener delegatePageListener;
- // TabView class objects
- private TabView tabView1, tabView2, tabView3;
- private int mStripHeight;
- private float mOffset = 0f;
- private View child;
-
- private View nextChild;
-
- public TabBarView(Context context) {
- this(context, null);
- }
-
- public TabBarView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.actionBarTabBarStyle);
- }
-
- public TabBarView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT));
-
- setWillNotDraw(false);
-
- mPaint = new Paint();
- mPaint.setColor(Color.WHITE);
-
- mPaint.setAntiAlias(false);
-
- mStripHeight = (int) (STRIP_HEIGHT * getResources().getDisplayMetrics().density + .5f);
- }
-
- //use to set StripColor
- public void setStripColor(int color) {
- if (mPaint.getColor() != color) {
- mPaint.setColor(color);
- invalidate();
- }
- }
-
- //Use to set the height of strip
- public void setStripHeight(int height) {
- if (mStripHeight != height) {
- mStripHeight = height;
- invalidate();
- }
- }
-
- //using to set the tab based on Tabindex which is need to pass as parameter.
- public void setSelectedTab(int tabIndex) {
- if (tabIndex < 0) {
- tabIndex = 0;
- }
- final int childCount = getChildCount();
- if (tabIndex >= childCount) {
- tabIndex = childCount - 1;
- }
- if (mSelectedTab != tabIndex) {
- mSelectedTab = tabIndex;
- invalidate();
- }
- }
-
- public void setOffset(int position, float offset) {
- if (mOffset != offset) {
- mOffset = offset;
- invalidate();
- }
- }
-
- //this method is using to draw the strip
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // Draw the strip manually
- child = getChildAt(mSelectedTab);
- int height = getHeight();
- if (child != null) {
- float left = child.getLeft();
- float right = child.getRight();
- if (mOffset > 0f && mSelectedTab < tabCount - 1) {
- nextChild = getChildAt(mSelectedTab + 1);
- if (nextChild != null) {
- final float nextTabLeft = nextChild.getLeft();
- final float nextTabRight = nextChild.getRight();
- left = (mOffset * nextTabLeft + (1f - mOffset) * left);
- right = (mOffset * nextTabRight + (1f - mOffset) * right);
- }
- }
- canvas.drawRect(left, height - mStripHeight, right, height, mPaint);
- }
- }
-
- //this method is used to set the pager reference to this.pager object and call the notifyDataSetChanged() method
- //
- public void setViewPager(ViewPager pager) {
- this.pager = pager;
-
- if (pager.getAdapter() == null) {
- throw new IllegalStateException("ViewPager does not have adapter instance.");
- }
-
- notifyDataSetChanged();
- }
-
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-
- mSelectedTab = position;
- mOffset = positionOffset;
-
- invalidate();
-
- if (delegatePageListener != null) {
- delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
- }
-
- //based on the position setting tab alpha as 1 if it's selected or others set is as 0.5f
- if (position == 0) {
- tabView1.setAlpha(1.0f);
- tabView2.setAlpha(0.5f);
- tabView3.setAlpha(0.5f);
-
- } else if (position == 1) {
- tabView1.setAlpha(0.5f);
- tabView2.setAlpha(1.0f);
- tabView3.setAlpha(0.5f);
-
- } else if (position == 2) {
- tabView1.setAlpha(0.5f);
- tabView2.setAlpha(0.5f);
- tabView3.setAlpha(1.0f);
-
- }
- }
-
- public void onPageScrollStateChanged(int state) {
- if (state == ViewPager.SCROLL_STATE_IDLE) {
-
- }
-
- if (delegatePageListener != null) {
- delegatePageListener.onPageScrollStateChanged(state);
- }
- }
-
- public void onPageSelected(int position) {
- if (delegatePageListener != null) {
- delegatePageListener.onPageSelected(position);
- }
- }
-
- // it will remove all previous views and creating new view for tab
- public void notifyDataSetChanged() {
-
- int resId;
-
- this.removeAllViews();
-
- tabCount = pager.getAdapter().getCount();
-
- for (int i = 0; i < tabCount; i++) {
-
- resId = ((IconTabProvider) pager.getAdapter()).getPageIconResId(i);
-
- if (getResources().getConfiguration().orientation == 1)
- addTabViewP(i, pager.getAdapter().getPageTitle(i).toString(), resId);
- else
- addTabViewL(i, pager.getAdapter().getPageTitle(i).toString(), resId);
- }
-
- //removing swiping animation effect from viewPager
-
-
- getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-
- @SuppressLint("NewApi")
- @Override
- public void onGlobalLayout() {
-
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
-
- mSelectedTab = pager.getCurrentItem();
-
- }
- });
-
- }
-
- //used to set the properties for landscape screen like icon,text and click-event into tab and then add this tab into View using addView(tab) method
- private void addTabViewL(final int i, String string, int pageIconResId) {
- // TODO Auto-generated method stub
- TabView tab = new TabView(getContext());
-// tab.setIcon(pageIconResId);
- tab.setText(string, pageIconResId);
- tab.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- pager.setCurrentItem(i, false);
- }
- });
-
- this.addView(tab);
- }
-
- private void addTabViewP(final int i, final String string, int pageIconResId) {
- // TODO Auto-generated method stub
-
- if (i == 0) {
- tabView1 = new TabView(getContext());
-
- tabView1.setIcon(pageIconResId);
- tabView1.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- pager.setCurrentItem(i);
-
-
- }
- });
- CheatSheet.setup(tabView1, string);
- this.addView(tabView1);
- } else if (i == 1) {
- tabView2 = new TabView(getContext());
- tabView2.setIcon(pageIconResId);
- tabView2.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- pager.setCurrentItem(i);
-
- }
- });
- CheatSheet.setup(tabView2, string);
- this.addView(tabView2);
- } else if (i == 2) {
- tabView3 = new TabView(getContext());
- tabView3.setIcon(pageIconResId);
- tabView3.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- pager.setCurrentItem(i);
-
- }
- });
- CheatSheet.setup(tabView3, string);
- this.addView(tabView3);
- }
-
-
- }
-
- //used to set the properties for portrait screen like icon and click-event into tabView objects
- // and then add this tab object into View using addView(tabView1) method
- //here we setup the tabs properties
-
- public void setOnPageChangeListener(OnPageChangeListener listener) {
- this.delegatePageListener = listener;
- }
-
- public interface IconTabProvider {
- int getPageIconResId(int position);
- }
-}
\ No newline at end of file
diff --git a/mobile/src/main/java/com/tinderview/tabbarview/TabView.java b/mobile/src/main/java/com/tinderview/tabbarview/TabView.java
deleted file mode 100755
index 557a802..0000000
--- a/mobile/src/main/java/com/tinderview/tabbarview/TabView.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.tinderview.tabbarview;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.tinderview.R;
-
-/*
-* created by Aradh Pillai on 1.10.15
-* This TabView class using to create tab and setting ImageView and Text into TabView
-* and adding ripple animation into View.
-*
-*
-* */
-
-
-public class TabView extends LinearLayout {
-
- private ImageView mImageView;
- private TextView mTextView;
-
- public TabView(Context context) {
- this(context, null);
- }
-
- public TabView(Context context, AttributeSet attrs) {
- this(context, attrs, android.R.attr.actionBarTabStyle);
- }
-
- public TabView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.actionBarTabTextStyle, outValue, true);
-
- int txtstyle = outValue.data;
-
- int pad = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources()
- .getDisplayMetrics());
-
-
- mImageView = new ImageView(context);//dynamically create an object of imageView and setting param into it...
- mImageView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- mImageView.setScaleType(ScaleType.CENTER_INSIDE);
-
- mTextView = new TextView(context); //dynamically create a text view and setting these properties into it...
- mTextView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- mTextView.setGravity(Gravity.CENTER);
- mTextView.setCompoundDrawablePadding(pad);
- mTextView.setTextAppearance(context, txtstyle);
-
-
- this.addView(mImageView);// adding the imageview object into view
- this.addView(mTextView);// adding textview object into view
- this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));//setting param into view
-
- this.setBackgroundResource(R.drawable.list_selector_white);//setting ripple animation into this View.
-
-
- }
-
- public void setIcon(int resId) {
- setIcon(getContext().getResources().getDrawable(resId));
- }
-
- public void setIcon(Drawable icon) {
- if (icon != null) {
- mImageView.setVisibility(View.VISIBLE);
- mImageView.setImageDrawable(icon);
- } else {
- mImageView.setImageResource(View.GONE);
- }
- }
-
- public void setText(int resId, int ico) {
- setText(getContext().getString(resId), ico);
- }
-
- public void setText(CharSequence text, int ico) {
- mTextView.setText(text);
- mTextView.setCompoundDrawablesWithIntrinsicBounds(ico, 0, 0, 0);
- }
-
-}
\ No newline at end of file
diff --git a/mobile/src/main/java/com/tinderview/util/CircularImageView.java b/mobile/src/main/java/com/tinderview/util/CircularImageView.java
deleted file mode 100644
index ea6ebef..0000000
--- a/mobile/src/main/java/com/tinderview/util/CircularImageView.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.tinderview.util;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-
-/**
- * Created by Aradh Pillaion 07/10/15.
- *
- * setting the context and image resource from drawable and converting it into bitmap
- */
-public class CircularImageView {
-
- public static Drawable makeCircleFromResources(Context context, int resource) {
- Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resource);
-
- RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(
- context.getResources(), bitmapToSquare(bitmap));
- roundedBitmapDrawable.setCornerRadius(bitmap.getWidth() / 2);
- roundedBitmapDrawable.setAntiAlias(true);
- return roundedBitmapDrawable;
- }
-
-
- //
- private static Bitmap bitmapToSquare(Bitmap bitmap) {
- if (bitmap.getWidth() >= bitmap.getHeight()) {
- bitmap = Bitmap.createBitmap(bitmap,
- bitmap.getWidth() / 2 - bitmap.getHeight() / 2, 0,
- bitmap.getHeight(), bitmap.getHeight());
- } else {
- bitmap = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() / 2
- - bitmap.getWidth() / 2, bitmap.getWidth(),
- bitmap.getWidth());
- }
-
- return bitmap;
- }
-
-}
diff --git a/mobile/src/main/java/com/tinderview/util/CustomViewPager.java b/mobile/src/main/java/com/tinderview/util/CustomViewPager.java
deleted file mode 100644
index 754fd82..0000000
--- a/mobile/src/main/java/com/tinderview/util/CustomViewPager.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.tinderview.util;
-
-import android.content.Context;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-/**
- * Created by Aradh Pillai on 09/10/15.
- *
- *
- * this class is created to avoid the swipe gesture from ViewPager
- * this is customized ViewPager class to override the specific methods of ViewPager to avoid swipe feature.
- */
-
-
-public class CustomViewPager extends ViewPager {
-
- public CustomViewPager(Context context) {
- super(context);
- }
-
-
- public CustomViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
-
- /* when user will touch viewPager in intercept motion then this method will
- * invoke and it will return "false" it means it won't work so using this method we can avoid this feather from ViewPager
- *
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- // Never allow swiping to switch between pages
- return false;
- }
-
- /* even user will touch viewPager scope then this method will invoke and it will return false value ...
- * so using this method we can avoid touch event of ViewPager.
- */
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // Never allow swiping to switch between pages
- return false;
- }
-}
\ No newline at end of file
diff --git a/mobile/src/main/res/drawable-hdpi/ic_chat.png b/mobile/src/main/res/drawable-hdpi/ic_chat.png
deleted file mode 100755
index cc71515..0000000
Binary files a/mobile/src/main/res/drawable-hdpi/ic_chat.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-hdpi/ic_connect.png b/mobile/src/main/res/drawable-hdpi/ic_connect.png
deleted file mode 100755
index 4452783..0000000
Binary files a/mobile/src/main/res/drawable-hdpi/ic_connect.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-hdpi/ic_list.png b/mobile/src/main/res/drawable-hdpi/ic_list.png
deleted file mode 100755
index cbe6016..0000000
Binary files a/mobile/src/main/res/drawable-hdpi/ic_list.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-mdpi/ic_chat.png b/mobile/src/main/res/drawable-mdpi/ic_chat.png
deleted file mode 100755
index 735280c..0000000
Binary files a/mobile/src/main/res/drawable-mdpi/ic_chat.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-mdpi/ic_connect.png b/mobile/src/main/res/drawable-mdpi/ic_connect.png
deleted file mode 100755
index 0afed6e..0000000
Binary files a/mobile/src/main/res/drawable-mdpi/ic_connect.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-mdpi/ic_list.png b/mobile/src/main/res/drawable-mdpi/ic_list.png
deleted file mode 100755
index 970105c..0000000
Binary files a/mobile/src/main/res/drawable-mdpi/ic_list.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-v21/list_selector_white.xml b/mobile/src/main/res/drawable-v21/list_selector_white.xml
deleted file mode 100755
index b7cc05a..0000000
--- a/mobile/src/main/res/drawable-v21/list_selector_white.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/drawable-xhdpi/ic_chat.png b/mobile/src/main/res/drawable-xhdpi/ic_chat.png
deleted file mode 100755
index ae28ab2..0000000
Binary files a/mobile/src/main/res/drawable-xhdpi/ic_chat.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xhdpi/ic_connect.png b/mobile/src/main/res/drawable-xhdpi/ic_connect.png
deleted file mode 100755
index b357171..0000000
Binary files a/mobile/src/main/res/drawable-xhdpi/ic_connect.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xhdpi/ic_list.png b/mobile/src/main/res/drawable-xhdpi/ic_list.png
deleted file mode 100755
index a3d744f..0000000
Binary files a/mobile/src/main/res/drawable-xhdpi/ic_list.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xhdpi/phone2.png b/mobile/src/main/res/drawable-xhdpi/phone2.png
deleted file mode 100755
index c7efb42..0000000
Binary files a/mobile/src/main/res/drawable-xhdpi/phone2.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_chat.png b/mobile/src/main/res/drawable-xxhdpi/ic_chat.png
deleted file mode 100755
index d5c8997..0000000
Binary files a/mobile/src/main/res/drawable-xxhdpi/ic_chat.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_connect.png b/mobile/src/main/res/drawable-xxhdpi/ic_connect.png
deleted file mode 100755
index 59db6b2..0000000
Binary files a/mobile/src/main/res/drawable-xxhdpi/ic_connect.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_list.png b/mobile/src/main/res/drawable-xxhdpi/ic_list.png
deleted file mode 100755
index abb4a06..0000000
Binary files a/mobile/src/main/res/drawable-xxhdpi/ic_list.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxxhdpi/ic_chat.png b/mobile/src/main/res/drawable-xxxhdpi/ic_chat.png
deleted file mode 100755
index 502a86d..0000000
Binary files a/mobile/src/main/res/drawable-xxxhdpi/ic_chat.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxxhdpi/ic_connect.png b/mobile/src/main/res/drawable-xxxhdpi/ic_connect.png
deleted file mode 100755
index 17d8cbf..0000000
Binary files a/mobile/src/main/res/drawable-xxxhdpi/ic_connect.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxxhdpi/ic_list.png b/mobile/src/main/res/drawable-xxxhdpi/ic_list.png
deleted file mode 100755
index 0fcccfd..0000000
Binary files a/mobile/src/main/res/drawable-xxxhdpi/ic_list.png and /dev/null differ
diff --git a/mobile/src/main/res/drawable-xxxhdpi/ripple_selector_white.xml b/mobile/src/main/res/drawable-xxxhdpi/ripple_selector_white.xml
deleted file mode 100755
index 167d9b6..0000000
--- a/mobile/src/main/res/drawable-xxxhdpi/ripple_selector_white.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/drawable/card_stack_bg_shadow.xml b/mobile/src/main/res/drawable/card_stack_bg_shadow.xml
deleted file mode 100644
index 1546f9f..0000000
--- a/mobile/src/main/res/drawable/card_stack_bg_shadow.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/main/res/drawable/card_stack_bg_shadow2.xml b/mobile/src/main/res/drawable/card_stack_bg_shadow2.xml
deleted file mode 100644
index a95666b..0000000
--- a/mobile/src/main/res/drawable/card_stack_bg_shadow2.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/drawable/card_stack_bg_shadow3.xml b/mobile/src/main/res/drawable/card_stack_bg_shadow3.xml
deleted file mode 100644
index c1aa5cc..0000000
--- a/mobile/src/main/res/drawable/card_stack_bg_shadow3.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/drawable/list_selector_white.xml b/mobile/src/main/res/drawable/list_selector_white.xml
deleted file mode 100755
index ef7c9e5..0000000
--- a/mobile/src/main/res/drawable/list_selector_white.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/drawable/toolbar_shadow_pre_lolipop.xml b/mobile/src/main/res/drawable/toolbar_shadow_pre_lolipop.xml
deleted file mode 100644
index 21eef6c..0000000
--- a/mobile/src/main/res/drawable/toolbar_shadow_pre_lolipop.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/layout/activity_home.xml b/mobile/src/main/res/layout/activity_home.xml
deleted file mode 100755
index a3302a9..0000000
--- a/mobile/src/main/res/layout/activity_home.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/main/res/layout/background_ripple_layout.xml b/mobile/src/main/res/layout/background_ripple_layout.xml
deleted file mode 100644
index 98d6d2b..0000000
--- a/mobile/src/main/res/layout/background_ripple_layout.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/mobile/src/main/res/layout/card_stack_item.xml b/mobile/src/main/res/layout/card_stack_item.xml
deleted file mode 100755
index b231e6d..0000000
--- a/mobile/src/main/res/layout/card_stack_item.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/layout/custom_tab_view.xml b/mobile/src/main/res/layout/custom_tab_view.xml
deleted file mode 100755
index 054d069..0000000
--- a/mobile/src/main/res/layout/custom_tab_view.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
diff --git a/mobile/src/main/res/layout/fragment_chat.xml b/mobile/src/main/res/layout/fragment_chat.xml
deleted file mode 100755
index 0e47c23..0000000
--- a/mobile/src/main/res/layout/fragment_chat.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
diff --git a/mobile/src/main/res/layout/fragment_connect.xml b/mobile/src/main/res/layout/fragment_connect.xml
deleted file mode 100755
index 5d8c1c5..0000000
--- a/mobile/src/main/res/layout/fragment_connect.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
diff --git a/mobile/src/main/res/layout/fragment_discover.xml b/mobile/src/main/res/layout/fragment_discover.xml
deleted file mode 100755
index a18fbe6..0000000
--- a/mobile/src/main/res/layout/fragment_discover.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/mobile/src/main/res/menu/menu_main.xml b/mobile/src/main/res/menu/menu_main.xml
deleted file mode 100755
index 57c37ed..0000000
--- a/mobile/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/mobile/src/main/res/values-v21/styles.xml b/mobile/src/main/res/values-v21/styles.xml
deleted file mode 100644
index b39a4fa..0000000
--- a/mobile/src/main/res/values-v21/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
diff --git a/mobile/src/main/res/values/attrs.xml b/mobile/src/main/res/values/attrs.xml
deleted file mode 100755
index 2daedb4..0000000
--- a/mobile/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/values/colors.xml b/mobile/src/main/res/values/colors.xml
deleted file mode 100755
index 9d4717d..0000000
--- a/mobile/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
- //ripple color for Tabs
- #77ffffff
-
- //color for background
- #e6e6e6
-
- //theme properties mentioned here
- #3498db
- #2980b9
- #FFFFFF
-
- #000000
- #ffffff
-
- #b6b6b6
-
- //for ripple background animation
- #993498db
-
- //stroke color for CardStack
- #B1B1B1
-
- //these three different shadow colors for CardStack
- #fbfcfc
- #f8f8f8
- #f2f2f2
-
-
- //for pre-lollipop gradient color
- #40000000
-
-
\ No newline at end of file
diff --git a/mobile/src/main/res/values/dimensions.xml b/mobile/src/main/res/values/dimensions.xml
deleted file mode 100755
index c6d8397..0000000
--- a/mobile/src/main/res/values/dimensions.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- //sets some default properties for rippleBackgroundAnimation
- 2dp
- 64dp
-
\ No newline at end of file
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
deleted file mode 100755
index 0507672..0000000
--- a/mobile/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
- tinderview
-
- Hello world!
- Settings
-
- Discover
- The fastest way to connect with startups in a single tap
-
- Connect
- Simply swipe to connect with a startup. Chat freely & take informed decisions
-
- Invest
- Add friends to your group and invest together, We will help you keep track of everything
-
- Connect
- Discover
- Chat
-
-
diff --git a/mobile/src/main/res/values/styles.xml b/mobile/src/main/res/values/styles.xml
deleted file mode 100755
index 506ec38..0000000
--- a/mobile/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index 6a4e79f..0000000
--- a/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include ':mobile', ':wear'
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..a66ea92
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "TinderView"
+include(":cardstack")
+include(":app")
diff --git a/wear/.gitignore b/wear/.gitignore
deleted file mode 100644
index 796b96d..0000000
--- a/wear/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/wear/build.gradle b/wear/build.gradle
deleted file mode 100644
index 6507949..0000000
--- a/wear/build.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-apply plugin: 'com.android.application'
-
-
-android {
- compileSdkVersion 22
- buildToolsVersion "22.0.1"
-
- defaultConfig {
- applicationId "com.tinderview"
- minSdkVersion 20
- targetSdkVersion 22
- versionCode 1
- versionName "1.0"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.google.android.support:wearable:1.2.0'
- compile 'com.google.android.gms:play-services-wearable:7.8.0'
-}
diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro
deleted file mode 100644
index b89bfd7..0000000
--- a/wear/proguard-rules.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in /Users/Aradh/Library/Android/sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/wear/src/main/java/com/letsventure/MainActivity.java b/wear/src/main/java/com/letsventure/MainActivity.java
deleted file mode 100644
index 03baabe..0000000
--- a/wear/src/main/java/com/letsventure/MainActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.tinderview;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.wearable.view.WatchViewStub;
-import android.widget.TextView;
-
-public class MainActivity extends Activity {
-
- private TextView mTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
- stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
- @Override
- public void onLayoutInflated(WatchViewStub stub) {
- mTextView = (TextView) stub.findViewById(R.id.text);
- }
- });
- }
-}
diff --git a/wear/src/main/res/layout/activity_main.xml b/wear/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 7b5851a..0000000
--- a/wear/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
diff --git a/wear/src/main/res/layout/rect_activity_main.xml b/wear/src/main/res/layout/rect_activity_main.xml
deleted file mode 100644
index 203071b..0000000
--- a/wear/src/main/res/layout/rect_activity_main.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
diff --git a/wear/src/main/res/layout/round_activity_main.xml b/wear/src/main/res/layout/round_activity_main.xml
deleted file mode 100644
index c91ab44..0000000
--- a/wear/src/main/res/layout/round_activity_main.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher.png b/wear/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index cde69bc..0000000
Binary files a/wear/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/wear/src/main/res/mipmap-mdpi/ic_launcher.png b/wear/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index c133a0c..0000000
Binary files a/wear/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/wear/src/main/res/mipmap-xhdpi/ic_launcher.png b/wear/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index bfa42f0..0000000
Binary files a/wear/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 324e72c..0000000
Binary files a/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml
deleted file mode 100644
index cf92265..0000000
--- a/wear/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- tinderview
- Hello Round World!
- Hello Square World!
-
diff --git a/wear/wear.iml b/wear/wear.iml
deleted file mode 100644
index c0ac484..0000000
--- a/wear/wear.iml
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- generateDebugAndroidTestSources
- generateDebugSources
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file