├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── nameisjayant │ │ └── threadsapp │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── nameisjayant │ │ │ └── threadsapp │ │ │ ├── MainActivity.kt │ │ │ ├── bottom_bar │ │ │ ├── BottomBarItems.kt │ │ │ └── bottom_bar.kt │ │ │ ├── components │ │ │ ├── searchview_component.kt │ │ │ └── spacer_component.kt │ │ │ ├── features │ │ │ └── screens │ │ │ │ ├── home_screen.kt │ │ │ │ ├── notification_screen.kt │ │ │ │ ├── post_screen.kt │ │ │ │ ├── profile_screen.kt │ │ │ │ └── search_screen.kt │ │ │ ├── navigation │ │ │ └── app_navigation.kt │ │ │ ├── ui │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── utils │ │ │ └── extensions.kt │ └── res │ │ ├── drawable │ │ ├── add.png │ │ ├── home.png │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── logo.png │ │ ├── love.png │ │ ├── person.png │ │ ├── post.xml │ │ └── search.png │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── nameisjayant │ └── threadsapp │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Threads App -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.nameisjayant.threadsapp' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.nameisjayant.threadsapp" 12 | minSdk 24 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | buildFeatures { 37 | compose true 38 | } 39 | composeOptions { 40 | kotlinCompilerExtensionVersion '1.4.8' 41 | } 42 | packagingOptions { 43 | resources { 44 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 45 | } 46 | } 47 | } 48 | 49 | dependencies { 50 | 51 | implementation 'androidx.core:core-ktx:1.10.1' 52 | implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') 53 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' 54 | implementation 'androidx.activity:activity-compose:1.7.2' 55 | implementation platform('androidx.compose:compose-bom:2023.05.01') 56 | implementation 'androidx.compose.ui:ui' 57 | implementation 'androidx.compose.ui:ui-graphics' 58 | implementation 'androidx.compose.ui:ui-tooling-preview' 59 | implementation 'androidx.compose.material3:material3' 60 | testImplementation 'junit:junit:4.13.2' 61 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 62 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 63 | androidTestImplementation platform('androidx.compose:compose-bom:2023.05.01') 64 | androidTestImplementation 'androidx.compose.ui:ui-test-junit4' 65 | debugImplementation 'androidx.compose.ui:ui-tooling' 66 | debugImplementation 'androidx.compose.ui:ui-test-manifest' 67 | implementation "androidx.navigation:navigation-compose:2.6.0" 68 | implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1" 69 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/nameisjayant/threadsapp/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.nameisjayant.threadsapp", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import androidx.activity.ComponentActivity 6 | import androidx.activity.compose.setContent 7 | import androidx.compose.foundation.layout.PaddingValues 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.height 10 | import androidx.compose.material3.BottomAppBar 11 | import androidx.compose.material3.MaterialTheme 12 | import androidx.compose.material3.Scaffold 13 | import androidx.compose.material3.Surface 14 | import androidx.compose.material3.Text 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.tooling.preview.Preview 19 | import androidx.compose.ui.unit.dp 20 | import androidx.navigation.compose.rememberNavController 21 | import com.nameisjayant.threadsapp.bottom_bar.BottomBar 22 | import com.nameisjayant.threadsapp.navigation.AppNavigation 23 | import com.nameisjayant.threadsapp.ui.theme.ThreadsAppTheme 24 | 25 | class MainActivity : ComponentActivity() { 26 | @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContent { 30 | val navHostController = rememberNavController() 31 | ThreadsAppTheme { 32 | Scaffold( 33 | bottomBar = { 34 | BottomAppBar( 35 | modifier = Modifier.height(60.dp), 36 | containerColor = Color.White, 37 | contentPadding = PaddingValues(horizontal = 20.dp), 38 | ) { 39 | BottomBar(navHostController = navHostController) 40 | } 41 | } 42 | ) { 43 | AppNavigation(navHostController) 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/bottom_bar/BottomBarItems.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.bottom_bar 2 | 3 | import com.nameisjayant.threadsapp.R 4 | 5 | 6 | sealed class BottomBarItems( 7 | val route: String, 8 | val title: String, 9 | val icon: Int 10 | ) { 11 | 12 | object Home : BottomBarItems( 13 | "home", 14 | "Home", 15 | R.drawable.home 16 | ) 17 | 18 | object Search : BottomBarItems( 19 | "search", 20 | "Search", 21 | R.drawable.search 22 | ) 23 | 24 | object Post : BottomBarItems( 25 | "post", 26 | "Post", 27 | R.drawable.post 28 | ) 29 | 30 | object Notification : BottomBarItems( 31 | "notification", 32 | "Notification", 33 | R.drawable.love 34 | ) 35 | 36 | object Profile : BottomBarItems( 37 | "profile", 38 | "Profile", 39 | R.drawable.person 40 | ) 41 | 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/bottom_bar/bottom_bar.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.bottom_bar 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.size 8 | import androidx.compose.material3.Icon 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.res.painterResource 15 | import androidx.compose.ui.unit.dp 16 | import androidx.navigation.NavDestination 17 | import androidx.navigation.NavDestination.Companion.hierarchy 18 | import androidx.navigation.NavGraph.Companion.findStartDestination 19 | import androidx.navigation.NavHostController 20 | import androidx.navigation.compose.currentBackStackEntryAsState 21 | import com.nameisjayant.threadsapp.utils.noRippleEffect 22 | 23 | 24 | @Composable 25 | fun BottomBar( 26 | navHostController: NavHostController 27 | ) { 28 | 29 | val items = listOf( 30 | BottomBarItems.Home, 31 | BottomBarItems.Search, 32 | BottomBarItems.Post, 33 | BottomBarItems.Notification, 34 | BottomBarItems.Profile 35 | ) 36 | 37 | val navStackBackEntry by navHostController.currentBackStackEntryAsState() 38 | val currentDestination = navStackBackEntry?.destination 39 | 40 | Row( 41 | modifier = Modifier 42 | .fillMaxWidth(), 43 | horizontalArrangement = Arrangement.SpaceBetween, 44 | verticalAlignment = Alignment.CenterVertically 45 | ) { 46 | items.forEach { screen -> 47 | AddItem( 48 | screen = screen, 49 | currentDestination = currentDestination, 50 | navController = navHostController 51 | ) 52 | } 53 | } 54 | 55 | } 56 | 57 | @Composable 58 | fun AddItem( 59 | screen: BottomBarItems, 60 | currentDestination: NavDestination?, 61 | navController: NavHostController 62 | ) { 63 | val selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true 64 | 65 | val contentColor = 66 | if (selected) Color.Black else Color.Gray 67 | 68 | Box( 69 | modifier = Modifier 70 | .noRippleEffect { 71 | navController.navigate(screen.route) { 72 | popUpTo(navController.graph.findStartDestination().id) 73 | launchSingleTop = true 74 | } 75 | }) { 76 | Row( 77 | verticalAlignment = Alignment.CenterVertically, 78 | ) { 79 | Icon( 80 | painter = painterResource(id = screen.icon), 81 | contentDescription = "icon", 82 | tint = contentColor, 83 | modifier = Modifier.size(32.dp) 84 | ) 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/components/searchview_component.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.size 10 | import androidx.compose.foundation.shape.RoundedCornerShape 11 | import androidx.compose.foundation.text.BasicTextField 12 | import androidx.compose.foundation.text.KeyboardOptions 13 | import androidx.compose.material3.Icon 14 | import androidx.compose.material3.Text 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Alignment.Companion.CenterStart 17 | import androidx.compose.ui.Alignment.Companion.CenterVertically 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.res.painterResource 21 | import androidx.compose.ui.res.stringResource 22 | import androidx.compose.ui.text.input.ImeAction 23 | import androidx.compose.ui.unit.dp 24 | import com.nameisjayant.threadsapp.R 25 | 26 | 27 | @Composable 28 | fun SearchViewComponent( 29 | search: String, 30 | modifier: Modifier = Modifier, 31 | onValueChange: (String) -> Unit 32 | ) { 33 | 34 | BasicTextField( 35 | value = search, 36 | onValueChange = onValueChange, 37 | modifier = modifier.height(40.dp), 38 | maxLines = 1, 39 | singleLine = true, 40 | keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), 41 | decorationBox = { 42 | Box( 43 | modifier = Modifier 44 | .fillMaxWidth() 45 | .background(Color(0XFFF1F1F1), RoundedCornerShape(8.dp)) 46 | ) { 47 | Row( 48 | modifier = Modifier 49 | .padding(horizontal = 8.dp) 50 | .align(CenterStart) 51 | ) { 52 | Icon( 53 | painter = painterResource(id = R.drawable.search), contentDescription = "", 54 | modifier = Modifier 55 | .size(20.dp), 56 | tint = Color.Gray 57 | ) 58 | Box(modifier = Modifier.padding(horizontal = 8.dp)) { 59 | if (search.isEmpty()) { 60 | Text( 61 | text = stringResource(R.string.search), 62 | ) 63 | } 64 | it.invoke() 65 | } 66 | } 67 | } 68 | 69 | 70 | } 71 | ) 72 | 73 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/components/spacer_component.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.components 2 | 3 | import androidx.compose.foundation.layout.Spacer 4 | import androidx.compose.foundation.layout.height 5 | import androidx.compose.foundation.layout.width 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.unit.Dp 9 | import androidx.compose.ui.unit.dp 10 | 11 | 12 | @Composable 13 | fun SpacerHeight( 14 | height: Dp = 10.dp 15 | ) { 16 | Spacer(modifier = Modifier.height(height)) 17 | } 18 | 19 | @Composable 20 | fun SpacerWidth( 21 | width: Dp = 10.dp 22 | ) { 23 | Spacer(modifier = Modifier.width(width)) 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/features/screens/home_screen.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.features.screens 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.offset 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.size 10 | import androidx.compose.foundation.shape.CircleShape 11 | import androidx.compose.material.icons.Icons 12 | import androidx.compose.material.icons.filled.AddCircle 13 | import androidx.compose.material.icons.filled.MoreVert 14 | import androidx.compose.material3.Icon 15 | import androidx.compose.material3.Text 16 | import androidx.compose.runtime.Composable 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.layout.layoutId 21 | import androidx.compose.ui.res.painterResource 22 | import androidx.compose.ui.unit.dp 23 | import androidx.constraintlayout.compose.ChainStyle 24 | import androidx.constraintlayout.compose.ConstraintLayout 25 | import androidx.constraintlayout.compose.ConstraintSet 26 | import com.nameisjayant.threadsapp.R 27 | import com.nameisjayant.threadsapp.ui.theme.Purple40 28 | import com.nameisjayant.threadsapp.ui.theme.Purple80 29 | import com.nameisjayant.threadsapp.utils.noRippleEffect 30 | 31 | @Composable 32 | fun HomeScreen() { 33 | 34 | ConstraintLayout( 35 | decoupledConstraints(), 36 | modifier = Modifier.fillMaxSize() 37 | ) { 38 | Icon( 39 | painter = painterResource(id = R.drawable.logo), contentDescription = "", 40 | modifier = Modifier 41 | .layoutId(Home.LOGO) 42 | .size(50.dp), 43 | tint = Color.Unspecified, 44 | ) 45 | UserPostEach(modifier = Modifier.layoutId(Home.PARENT_ROW)) 46 | 47 | } 48 | 49 | 50 | } 51 | 52 | @Composable 53 | fun UserPostEach( 54 | modifier: Modifier = Modifier 55 | ) { 56 | ConstraintLayout( 57 | decoupledConstraints(), 58 | modifier = modifier 59 | .fillMaxWidth() 60 | .padding(vertical = 10.dp, horizontal = 10.dp), 61 | ) { 62 | ProfileIcon(modifier = Modifier.layoutId(Home.ICON)) {} 63 | ConstraintLayout( 64 | decoupledConstraints(), 65 | modifier = Modifier 66 | .layoutId(Home.POST_LAYOUT) 67 | .fillMaxWidth() 68 | 69 | ) { 70 | Text(text = "nameisjayant", modifier = Modifier.layoutId(Home.USERNAME)) 71 | 72 | ConstraintLayout( 73 | decoupledConstraints(), 74 | modifier = Modifier.layoutId(Home.TIME_LAYOUT) 75 | ) { 76 | Text(text = "2 h", modifier = Modifier.layoutId(Home.TIME)) 77 | Icon( 78 | Icons.Default.MoreVert, 79 | contentDescription = "", 80 | tint = Color.Black, 81 | modifier = Modifier.layoutId(Home.THREE_DOTS) 82 | ) 83 | } 84 | } 85 | } 86 | } 87 | 88 | @Composable 89 | fun ProfileIcon( 90 | modifier: Modifier = Modifier, 91 | onClick: () -> Unit 92 | ) { 93 | Box( 94 | modifier = modifier 95 | .background(Purple80, CircleShape) 96 | .size(45.dp) 97 | .noRippleEffect { onClick() }, 98 | contentAlignment = Alignment.BottomEnd 99 | ) { 100 | Icon( 101 | imageVector = Icons.Default.AddCircle, 102 | contentDescription = "", 103 | modifier = Modifier.offset(x = 5.dp, y = 5.dp) 104 | ) 105 | } 106 | } 107 | 108 | private fun decoupledConstraints(): ConstraintSet { 109 | return ConstraintSet { 110 | val icon = createRefFor(Home.ICON) 111 | val logo = createRefFor(Home.LOGO) 112 | val parentRow = createRefFor(Home.PARENT_ROW) 113 | val postLayout = createRefFor(Home.POST_LAYOUT) 114 | val username = createRefFor(Home.USERNAME) 115 | val timeLayout = createRefFor(Home.TIME_LAYOUT) 116 | val time = createRefFor(Home.TIME) 117 | val threeDot = createRefFor(Home.THREE_DOTS) 118 | 119 | constrain(icon) { 120 | start.linkTo(parent.start) 121 | top.linkTo(parent.top) 122 | } 123 | constrain(logo) { 124 | start.linkTo(parent.start) 125 | end.linkTo(parent.end) 126 | top.linkTo(parent.top, 20.dp) 127 | } 128 | constrain(parentRow) { 129 | start.linkTo(parent.start) 130 | end.linkTo(parent.end) 131 | top.linkTo(logo.bottom) 132 | } 133 | constrain(postLayout) { 134 | start.linkTo(icon.end, 10.dp) 135 | top.linkTo(parent.top) 136 | } 137 | constrain(username) { 138 | start.linkTo(parent.start) 139 | top.linkTo(parent.top) 140 | } 141 | constrain(timeLayout) { 142 | start.linkTo(username.end) 143 | end.linkTo(parent.end) 144 | top.linkTo(parent.top) 145 | } 146 | constrain(time) { 147 | start.linkTo(parent.start) 148 | top.linkTo(parent.top) 149 | } 150 | constrain(threeDot) { 151 | start.linkTo(time.end) 152 | } 153 | 154 | 155 | } 156 | } 157 | 158 | private enum class Home { 159 | ICON, 160 | PARENT_ROW, 161 | USERNAME, 162 | TIME, 163 | THREE_DOTS, 164 | TEXT, 165 | LIKE, 166 | COMMENT, 167 | SHARE, 168 | REPOST, 169 | REPLIES, 170 | LIKES, 171 | ICON_ONE, 172 | ICON_TWO, 173 | ICON_THREE, 174 | H_LINE, 175 | V_LINE, 176 | LOGO, 177 | POST_LAYOUT, 178 | TIME_LAYOUT, 179 | 180 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/features/screens/notification_screen.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.features.screens 2 | 3 | import androidx.compose.foundation.BorderStroke 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.horizontalScroll 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.BoxWithConstraints 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.PaddingValues 10 | import androidx.compose.foundation.layout.Row 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.foundation.layout.fillMaxWidth 13 | import androidx.compose.foundation.layout.padding 14 | import androidx.compose.foundation.layout.size 15 | import androidx.compose.foundation.layout.width 16 | import androidx.compose.foundation.lazy.LazyColumn 17 | import androidx.compose.foundation.rememberScrollState 18 | import androidx.compose.foundation.shape.CircleShape 19 | import androidx.compose.foundation.shape.RoundedCornerShape 20 | import androidx.compose.material3.Button 21 | import androidx.compose.material3.ButtonDefaults 22 | import androidx.compose.material3.Divider 23 | import androidx.compose.material3.Text 24 | import androidx.compose.runtime.Composable 25 | import androidx.compose.runtime.getValue 26 | import androidx.compose.runtime.mutableStateOf 27 | import androidx.compose.runtime.remember 28 | import androidx.compose.runtime.setValue 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.graphics.Color 31 | import androidx.compose.ui.layout.layout 32 | import androidx.compose.ui.layout.layoutId 33 | import androidx.compose.ui.res.stringResource 34 | import androidx.compose.ui.text.SpanStyle 35 | import androidx.compose.ui.text.TextStyle 36 | import androidx.compose.ui.text.buildAnnotatedString 37 | import androidx.compose.ui.text.font.FontWeight 38 | import androidx.compose.ui.text.withStyle 39 | import androidx.compose.ui.unit.Dp 40 | import androidx.compose.ui.unit.dp 41 | import androidx.compose.ui.unit.sp 42 | import androidx.constraintlayout.compose.ConstraintLayout 43 | import androidx.constraintlayout.compose.ConstraintSet 44 | import com.nameisjayant.threadsapp.R 45 | import com.nameisjayant.threadsapp.ui.theme.Purple40 46 | import com.nameisjayant.threadsapp.ui.theme.Purple80 47 | import com.nameisjayant.threadsapp.ui.theme.PurpleGrey80 48 | import com.nameisjayant.threadsapp.ui.theme.gray_color 49 | 50 | private fun decoupledConstraints(): ConstraintSet { 51 | return ConstraintSet { 52 | val activityText = createRefFor(Notification.ACTIVITY_TEXT) 53 | val chipSelection = createRefFor(Notification.CHIP_SELECTION) 54 | val recyclerview = createRefFor(Notification.RECYCLER_VIEW) 55 | 56 | 57 | constrain(activityText) { 58 | start.linkTo(parent.start) 59 | top.linkTo(parent.top) 60 | } 61 | constrain(chipSelection) { 62 | top.linkTo(activityText.bottom) 63 | start.linkTo(parent.start) 64 | end.linkTo(parent.end) 65 | } 66 | constrain(recyclerview) { 67 | start.linkTo(parent.start) 68 | top.linkTo(chipSelection.bottom, 20.dp) 69 | } 70 | 71 | 72 | } 73 | } 74 | 75 | private fun decoupledAllSection(): ConstraintSet { 76 | return ConstraintSet { 77 | 78 | val iconOne = createRefFor(All.ICON_ONE) 79 | val iconTwo = createRefFor(All.ICON_TWO) 80 | val iconThree = createRefFor(All.ICON_THREE) 81 | val iconFour = createRefFor(All.ICON_FOUR) 82 | val parentRow = createRefFor(All.PARENT_ROW) 83 | val title = createRefFor(All.TITLE) 84 | val time = createRefFor(All.TIME) 85 | val followedBy = createRefFor(All.FOLLOWED) 86 | val line = createRefFor(All.LINE) 87 | 88 | constrain(iconOne) { 89 | start.linkTo(parent.start) 90 | top.linkTo(parent.top) 91 | } 92 | constrain(iconTwo) { 93 | start.linkTo(iconOne.end, 5.dp) 94 | top.linkTo(parent.top) 95 | } 96 | constrain(iconThree) { 97 | start.linkTo(parent.start) 98 | top.linkTo(iconOne.bottom, 10.dp) 99 | } 100 | constrain(iconFour) { 101 | start.linkTo(iconThree.end, 5.dp) 102 | top.linkTo(iconTwo.bottom, 5.dp) 103 | } 104 | constrain(parentRow) { 105 | start.linkTo(iconTwo.end, 10.dp) 106 | top.linkTo(parent.top) 107 | bottom.linkTo(parent.bottom) 108 | } 109 | constrain(title) { 110 | start.linkTo(parent.start) 111 | top.linkTo(parent.top) 112 | } 113 | constrain(time) { 114 | start.linkTo(title.end, 10.dp) 115 | top.linkTo(parent.top) 116 | 117 | } 118 | constrain(followedBy) { 119 | top.linkTo(title.bottom) 120 | start.linkTo(parent.start) 121 | } 122 | constrain(line) { 123 | start.linkTo(parent.start) 124 | end.linkTo(parent.end) 125 | top.linkTo(followedBy.bottom, 15.dp) 126 | } 127 | 128 | } 129 | } 130 | 131 | private fun decoupledRepliesSection(): ConstraintSet { 132 | return ConstraintSet { 133 | val icon = createRefFor(Replies.ICON) 134 | val username = createRefFor(Replies.USER_NAME) 135 | val tag = createRefFor(Replies.TAG) 136 | val replies = createRefFor(Replies.REPLIES) 137 | val time = createRefFor(Replies.TIME) 138 | val parentRow = createRefFor(Replies.PARENT_ROW) 139 | val line = createRefFor(Replies.LINE) 140 | 141 | constrain(icon) { 142 | start.linkTo(parent.start) 143 | top.linkTo(parent.top) 144 | //end.linkTo(parentRow.start) 145 | } 146 | constrain(parentRow) { 147 | start.linkTo(icon.end, 10.dp) 148 | top.linkTo(parent.top) 149 | // end.linkTo(parent.end) 150 | } 151 | 152 | constrain(username) { 153 | start.linkTo(parent.start) 154 | top.linkTo(parent.top) 155 | } 156 | constrain(time) { 157 | start.linkTo(username.end, 5.dp) 158 | top.linkTo(parent.top) 159 | } 160 | constrain(tag) { 161 | start.linkTo(parent.start) 162 | top.linkTo(username.bottom) 163 | } 164 | constrain(replies) { 165 | start.linkTo(parent.start) 166 | top.linkTo(tag.bottom) 167 | } 168 | constrain(line) { 169 | start.linkTo(parent.start) 170 | end.linkTo(parent.end) 171 | top.linkTo(replies.bottom, 10.dp) 172 | } 173 | } 174 | 175 | } 176 | 177 | private enum class Notification { 178 | ACTIVITY_TEXT, CHIP_SELECTION, RECYCLER_VIEW 179 | } 180 | 181 | private enum class All { 182 | ICON_ONE, 183 | ICON_TWO, 184 | ICON_THREE, 185 | ICON_FOUR, 186 | PARENT_ROW, 187 | TITLE, 188 | TIME, 189 | FOLLOWED, 190 | LINE 191 | } 192 | 193 | private enum class Replies { 194 | ICON, 195 | USER_NAME, 196 | TAG, 197 | REPLIES, 198 | TIME, 199 | PARENT_ROW, 200 | LINE 201 | } 202 | 203 | @Composable 204 | fun NotificationScreen() { 205 | 206 | val chips = listOf("All", "Replies", "Mentions", "Verified") 207 | var selected by remember { mutableStateOf(0) } 208 | val scrollState = rememberScrollState() 209 | 210 | BoxWithConstraints( 211 | modifier = Modifier 212 | .fillMaxSize() 213 | .background(Color.White) 214 | ) { 215 | ConstraintLayout(decoupledConstraints(), modifier = Modifier.padding(16.dp)) { 216 | Text( 217 | text = stringResource(R.string.activity), 218 | modifier = Modifier.layoutId(Notification.ACTIVITY_TEXT), 219 | style = TextStyle( 220 | color = Color.Black, fontSize = 35.sp, fontWeight = FontWeight.Bold 221 | ) 222 | ) 223 | Row( 224 | modifier = Modifier 225 | .padding(top = 15.dp) 226 | .horizontalScroll(scrollState) 227 | .fillMaxWidth() 228 | .layoutId(Notification.CHIP_SELECTION) 229 | ) { 230 | chips.forEachIndexed { index, title -> 231 | NotificationFilterChip(title = title, 232 | selected = index == selected, 233 | index = index, 234 | onValueChange = { 235 | selected = it 236 | }) 237 | } 238 | } 239 | 240 | LazyColumn( 241 | modifier = Modifier 242 | .fillMaxSize() 243 | .layoutId(Notification.RECYCLER_VIEW) 244 | ) { 245 | if (selected == 0) 246 | item { 247 | AllSection() 248 | RepliesSection() 249 | AllSection() 250 | RepliesSection(text = "Mentioned you") 251 | } 252 | if (selected == 1) 253 | items(5) { 254 | RepliesSection() 255 | } 256 | if(selected == 2) 257 | items(5){ 258 | RepliesSection( 259 | text = "Mentioned you" 260 | ) 261 | } 262 | if(selected == 3) 263 | item(5){ 264 | SearchProfileLayout() 265 | } 266 | 267 | 268 | } 269 | } 270 | 271 | } 272 | } 273 | 274 | @Composable 275 | fun RepliesSection( 276 | modifier: Modifier = Modifier, 277 | text:String = "@threadsapp is built by using jetpack comp...." 278 | ) { 279 | 280 | ConstraintLayout( 281 | decoupledRepliesSection(), modifier = modifier.padding(vertical = 10.dp) 282 | ) { 283 | NotificationProfileIcon(size = 30.dp, modifier = Modifier.layoutId(Replies.ICON)) 284 | ConstraintLayout( 285 | decoupledRepliesSection(), 286 | modifier = Modifier.layoutId(Replies.PARENT_ROW) 287 | ) { 288 | Text( 289 | text = "nameisjayant", 290 | modifier = Modifier.layoutId(Replies.USER_NAME), 291 | style = TextStyle( 292 | color = Color.Black, 293 | fontSize = 18.sp, 294 | fontWeight = FontWeight.W600 295 | ) 296 | ) 297 | Text(text = "3 d", modifier = Modifier.layoutId(Replies.TIME)) 298 | Text( 299 | text = text, 300 | modifier = Modifier.layoutId(Replies.TAG), 301 | style = TextStyle( 302 | color = Color.Gray, 303 | fontSize = 18.sp, 304 | fontWeight = FontWeight.W400 305 | ) 306 | ) 307 | Text( 308 | text = "I thought it's React Native", 309 | modifier = Modifier.layoutId(Replies.REPLIES), 310 | style = TextStyle( 311 | color = Color.Black, 312 | fontSize = 19.sp, 313 | fontWeight = FontWeight.W400 314 | ) 315 | ) 316 | Divider( 317 | modifier = Modifier 318 | .layoutId(Replies.LINE) 319 | .fillMaxWidth(), 320 | thickness = 0.5.dp, 321 | color = Color.LightGray 322 | ) 323 | } 324 | } 325 | 326 | } 327 | 328 | @Composable 329 | fun AllSection( 330 | modifier: Modifier = Modifier 331 | ) { 332 | 333 | val annotatedString = buildAnnotatedString { 334 | withStyle( 335 | style = SpanStyle( 336 | color = Color.Black, 337 | fontSize = 18.sp, 338 | fontWeight = FontWeight.W600 339 | ) 340 | ) { 341 | append("nameisajayant ") 342 | } 343 | 344 | 345 | withStyle( 346 | style = SpanStyle( 347 | color = Color.Black, 348 | fontSize = 18.sp, 349 | fontWeight = FontWeight.W400 350 | ) 351 | ) { 352 | append("and 406 others") 353 | } 354 | } 355 | 356 | ConstraintLayout(decoupledAllSection(), modifier = modifier.padding(vertical = 10.dp)) { 357 | NotificationProfileIcon( 358 | modifier = Modifier.layoutId(All.ICON_ONE), 359 | size = 15.dp 360 | ) 361 | NotificationProfileIcon( 362 | modifier = Modifier.layoutId(All.ICON_TWO), 363 | size = 20.dp, 364 | color = Purple80 365 | ) 366 | NotificationProfileIcon( 367 | modifier = Modifier.layoutId(All.ICON_THREE), 368 | size = 15.dp, 369 | color = PurpleGrey80 370 | ) 371 | NotificationProfileIcon( 372 | modifier = Modifier.layoutId(All.ICON_FOUR), 373 | size = 20.dp 374 | ) 375 | ConstraintLayout( 376 | decoupledAllSection(), 377 | modifier = Modifier 378 | .padding(5.dp) 379 | .layoutId(All.PARENT_ROW) 380 | ) { 381 | Text(text = annotatedString, modifier = Modifier.layoutId(All.TITLE)) 382 | Text( 383 | text = "Followed You", style = TextStyle( 384 | fontSize = 18.sp, 385 | color = Color.Gray 386 | ), modifier = Modifier.layoutId(All.FOLLOWED) 387 | ) 388 | Text( 389 | text = "2 h", style = TextStyle( 390 | fontSize = 18.sp, 391 | color = Color.Gray 392 | ), modifier = Modifier.layoutId(All.TIME) 393 | ) 394 | Divider( 395 | modifier = Modifier 396 | .fillMaxWidth() 397 | .layoutId(All.LINE), 398 | thickness = 0.5.dp, 399 | color = Color.LightGray 400 | ) 401 | } 402 | } 403 | } 404 | 405 | 406 | @Composable 407 | fun NotificationProfileIcon( 408 | modifier: Modifier = Modifier, size: Dp = 20.dp, color: Color = Purple40 409 | ) { 410 | Box( 411 | modifier = modifier 412 | .background(color, CircleShape) 413 | .size(size) 414 | ) 415 | } 416 | 417 | 418 | @Composable 419 | fun NotificationFilterChip( 420 | title: String, 421 | selected: Boolean, 422 | index: Int, 423 | modifier: Modifier = Modifier, 424 | onValueChange: (Int) -> Unit 425 | ) { 426 | 427 | Button( 428 | onClick = { onValueChange(index) }, 429 | shape = RoundedCornerShape(10.dp), 430 | border = if (!selected) BorderStroke(1.dp, Color.LightGray) else null, 431 | colors = ButtonDefaults.buttonColors( 432 | containerColor = if (selected) Color.Black else Color.White, 433 | contentColor = if (selected) Color.White else Color.Black 434 | ), 435 | elevation = ButtonDefaults.buttonElevation(0.dp), 436 | modifier = modifier 437 | .padding(end = 10.dp) 438 | .width(120.dp), 439 | contentPadding = PaddingValues(vertical = 0.dp) 440 | ) { 441 | Text( 442 | text = title, style = TextStyle( 443 | fontSize = 18.sp, fontWeight = FontWeight.W600 444 | ) 445 | ) 446 | } 447 | 448 | } 449 | -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/features/screens/post_screen.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.features.screens 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | 6 | @Composable 7 | fun PostScreen() { 8 | 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/features/screens/profile_screen.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.features.screens 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | 6 | @Composable 7 | fun ProfileScreen() { 8 | 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/features/screens/search_screen.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.features.screens 2 | 3 | import android.annotation.SuppressLint 4 | import androidx.compose.foundation.BorderStroke 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.BoxWithConstraints 8 | import androidx.compose.foundation.layout.PaddingValues 9 | import androidx.compose.foundation.layout.Row 10 | import androidx.compose.foundation.layout.fillMaxSize 11 | import androidx.compose.foundation.layout.fillMaxWidth 12 | import androidx.compose.foundation.layout.height 13 | import androidx.compose.foundation.layout.imePadding 14 | import androidx.compose.foundation.layout.padding 15 | import androidx.compose.foundation.layout.size 16 | import androidx.compose.foundation.layout.width 17 | import androidx.compose.foundation.lazy.LazyColumn 18 | import androidx.compose.foundation.lazy.rememberLazyListState 19 | import androidx.compose.foundation.shape.CircleShape 20 | import androidx.compose.foundation.shape.RoundedCornerShape 21 | import androidx.compose.material3.ButtonDefaults 22 | import androidx.compose.material3.Divider 23 | import androidx.compose.material3.Text 24 | import androidx.compose.material3.TextButton 25 | import androidx.compose.runtime.Composable 26 | import androidx.compose.runtime.getValue 27 | import androidx.compose.runtime.mutableStateOf 28 | import androidx.compose.runtime.remember 29 | import androidx.compose.runtime.setValue 30 | import androidx.compose.ui.Modifier 31 | import androidx.compose.ui.graphics.Color 32 | import androidx.compose.ui.res.stringResource 33 | import androidx.compose.ui.text.TextStyle 34 | import androidx.compose.ui.text.font.FontWeight 35 | import androidx.compose.ui.unit.dp 36 | import androidx.compose.ui.unit.sp 37 | import androidx.constraintlayout.compose.ConstraintLayout 38 | import com.nameisjayant.threadsapp.R 39 | import com.nameisjayant.threadsapp.components.SearchViewComponent 40 | import com.nameisjayant.threadsapp.ui.theme.Purple40 41 | import com.nameisjayant.threadsapp.ui.theme.gray_color 42 | 43 | 44 | @Composable 45 | fun SearchScreen() { 46 | 47 | var search by remember { mutableStateOf("") } 48 | val state = rememberLazyListState() 49 | 50 | BoxWithConstraints( 51 | modifier = Modifier 52 | .fillMaxSize() 53 | .background(Color.White) 54 | ) { 55 | ConstraintLayout( 56 | modifier = Modifier 57 | .padding(20.dp) 58 | .fillMaxSize() 59 | ) { 60 | val (searchText, searchView, profile) = createRefs() 61 | Text( 62 | text = stringResource(id = R.string.search), style = TextStyle( 63 | color = Color.Black, 64 | fontSize = 35.sp, 65 | fontWeight = FontWeight.Bold 66 | ), 67 | modifier = Modifier.constrainAs(searchText) { 68 | start.linkTo(parent.start) 69 | top.linkTo(parent.top) 70 | bottom.linkTo(searchView.top) 71 | }) 72 | SearchViewComponent( 73 | search = search, 74 | modifier = Modifier 75 | .constrainAs(searchView) { 76 | start.linkTo(parent.start) 77 | top.linkTo(searchText.bottom, 12.dp) 78 | end.linkTo(parent.end) 79 | }) { 80 | search = it 81 | } 82 | LazyColumn( 83 | modifier = Modifier.constrainAs(profile) { 84 | start.linkTo(parent.start) 85 | end.linkTo(parent.end) 86 | top.linkTo(searchView.bottom) 87 | }, 88 | state = state 89 | ) { 90 | items(10) { 91 | SearchProfileLayout() 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | @Composable 99 | fun SearchProfileLayout( 100 | modifier: Modifier = Modifier 101 | ) { 102 | var isFollow by remember { mutableStateOf(false) } 103 | ConstraintLayout( 104 | modifier = modifier 105 | .padding(vertical = 10.dp) 106 | .fillMaxWidth() 107 | ) { 108 | val (icon, username, name, followers, followButton, line, userComponent) = createRefs() 109 | 110 | Box( 111 | modifier = Modifier 112 | .background(Purple40, CircleShape) 113 | .size(40.dp) 114 | .constrainAs(icon) { 115 | start.linkTo(parent.start) 116 | top.linkTo(parent.top) 117 | }) 118 | 119 | ConstraintLayout( 120 | modifier = Modifier.constrainAs(userComponent) { 121 | start.linkTo(icon.end, 15.dp) 122 | top.linkTo(parent.top) 123 | bottom.linkTo(parent.bottom) 124 | } 125 | ) { 126 | Text(text = "nameisjayant", style = TextStyle( 127 | color = Color.Black, 128 | fontSize = 17.sp, 129 | fontWeight = FontWeight.W600 130 | ), modifier = Modifier.constrainAs(username) { 131 | start.linkTo(parent.start) 132 | top.linkTo(parent.top) 133 | bottom.linkTo(name.top) 134 | }) 135 | Text(text = "Jayant", style = TextStyle( 136 | color = Color.Gray, 137 | fontSize = 19.sp, 138 | fontWeight = FontWeight.W400 139 | ), modifier = Modifier.constrainAs(name) { 140 | start.linkTo(parent.start) 141 | top.linkTo(username.bottom) 142 | bottom.linkTo(followers.top) 143 | }) 144 | Text( 145 | text = "512 Followers", 146 | color = Color.Black, 147 | modifier = Modifier.constrainAs(followers) { 148 | start.linkTo(parent.start) 149 | top.linkTo(name.bottom, 10.dp) 150 | bottom.linkTo(parent.top) 151 | }) 152 | Divider( 153 | modifier = Modifier 154 | .fillMaxWidth() 155 | .constrainAs(line) { 156 | start.linkTo(parent.start) 157 | end.linkTo(parent.end) 158 | top.linkTo(followers.bottom, 16.dp) 159 | }, thickness = 0.5.dp 160 | ) 161 | } 162 | 163 | FollowButton( 164 | modifier = Modifier.constrainAs(followButton) { 165 | end.linkTo(parent.end) 166 | top.linkTo(parent.top) 167 | }, 168 | text = if (isFollow) stringResource(R.string.following) else stringResource(id = R.string.follow) 169 | ) { 170 | isFollow = !isFollow 171 | } 172 | } 173 | 174 | } 175 | 176 | @SuppressLint("ModifierParameter") 177 | @Composable 178 | fun FollowButton( 179 | text: String = stringResource(id = R.string.follow), 180 | modifier: Modifier = Modifier, 181 | onClick: () -> Unit = {} 182 | ) { 183 | 184 | TextButton( 185 | onClick = onClick, 186 | border = BorderStroke(1.dp, Color.LightGray), 187 | modifier = modifier 188 | .width(120.dp), 189 | colors = ButtonDefaults.buttonColors( 190 | containerColor = Color.White, 191 | contentColor = Color.Black 192 | ), 193 | shape = RoundedCornerShape(10.dp), 194 | contentPadding = PaddingValues(vertical = 0.dp) 195 | ) { 196 | Text( 197 | text = text, style = TextStyle( 198 | fontSize = 18.sp, 199 | fontWeight = FontWeight.W500 200 | 201 | ) 202 | ) 203 | } 204 | 205 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/navigation/app_navigation.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.navigation 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.navigation.NavHostController 5 | import androidx.navigation.compose.NavHost 6 | import androidx.navigation.compose.composable 7 | import com.nameisjayant.threadsapp.bottom_bar.BottomBarItems 8 | import com.nameisjayant.threadsapp.features.screens.HomeScreen 9 | import com.nameisjayant.threadsapp.features.screens.NotificationScreen 10 | import com.nameisjayant.threadsapp.features.screens.PostScreen 11 | import com.nameisjayant.threadsapp.features.screens.ProfileScreen 12 | import com.nameisjayant.threadsapp.features.screens.SearchScreen 13 | 14 | 15 | @Composable 16 | fun AppNavigation( 17 | navHostController: NavHostController 18 | ) { 19 | 20 | NavHost(navController = navHostController, startDestination = BottomBarItems.Home.route) { 21 | composable(BottomBarItems.Home.route) { 22 | HomeScreen() 23 | } 24 | composable(BottomBarItems.Search.route) { 25 | SearchScreen() 26 | } 27 | composable(BottomBarItems.Post.route) { 28 | PostScreen() 29 | } 30 | composable(BottomBarItems.Notification.route) { 31 | NotificationScreen() 32 | } 33 | composable(BottomBarItems.Profile.route) { 34 | ProfileScreen() 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) 12 | 13 | val gray_color = Color(0XFFF1F1F1) -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.graphics.toArgb 15 | import androidx.compose.ui.platform.LocalContext 16 | import androidx.compose.ui.platform.LocalView 17 | import androidx.core.view.WindowCompat 18 | 19 | private val DarkColorScheme = darkColorScheme( 20 | primary = Purple80, 21 | secondary = PurpleGrey80, 22 | tertiary = Pink80 23 | ) 24 | 25 | private val LightColorScheme = lightColorScheme( 26 | primary = Purple40, 27 | secondary = PurpleGrey40, 28 | tertiary = Pink40 29 | 30 | /* Other default colors to override 31 | background = Color(0xFFFFFBFE), 32 | surface = Color(0xFFFFFBFE), 33 | onPrimary = Color.White, 34 | onSecondary = Color.White, 35 | onTertiary = Color.White, 36 | onBackground = Color(0xFF1C1B1F), 37 | onSurface = Color(0xFF1C1B1F), 38 | */ 39 | ) 40 | 41 | @Composable 42 | fun ThreadsAppTheme( 43 | darkTheme: Boolean = isSystemInDarkTheme(), 44 | // Dynamic color is available on Android 12+ 45 | dynamicColor: Boolean = true, 46 | content: @Composable () -> Unit 47 | ) { 48 | val colorScheme = when { 49 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 50 | val context = LocalContext.current 51 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 52 | } 53 | 54 | darkTheme -> DarkColorScheme 55 | else -> LightColorScheme 56 | } 57 | val view = LocalView.current 58 | if (!view.isInEditMode) { 59 | SideEffect { 60 | val window = (view.context as Activity).window 61 | window.statusBarColor = Color.LightGray.toArgb() 62 | window.navigationBarColor = Color.LightGray.toArgb() 63 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 64 | } 65 | } 66 | 67 | MaterialTheme( 68 | colorScheme = colorScheme, 69 | typography = Typography, 70 | content = content 71 | ) 72 | } -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/nameisjayant/threadsapp/utils/extensions.kt: -------------------------------------------------------------------------------- 1 | package com.nameisjayant.threadsapp.utils 2 | 3 | import android.annotation.SuppressLint 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.interaction.MutableInteractionSource 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.composed 8 | import androidx.constraintlayout.compose.ConstrainScope 9 | 10 | 11 | @SuppressLint("UnnecessaryComposedModifier") 12 | inline fun Modifier.noRippleEffect(crossinline onClick: () -> Unit) = composed { 13 | clickable( 14 | interactionSource = MutableInteractionSource(), 15 | indication = null 16 | ) { 17 | onClick() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/drawable/add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/drawable/home.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/love.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/drawable/love.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/drawable/person.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/post.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/drawable/search.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nameisjayant/threads-app-compose/810c8a6cca1bf462c3b4044a1c2d9f12d29d50c1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Threads App 3 | Search 4 | Activity 5 | Follow 6 | Following 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |