├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── laboontech │ │ └── internetspeedtestui │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── laboontech │ │ │ └── internetspeedtestui │ │ │ ├── MainActivity.kt │ │ │ ├── presentation │ │ │ ├── feature_speedtest │ │ │ │ └── SpeedTestScreen.kt │ │ │ ├── ui │ │ │ │ └── theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Shape.kt │ │ │ │ │ ├── Theme.kt │ │ │ │ │ └── Type.kt │ │ │ └── uistate │ │ │ │ └── UiState.kt │ │ │ └── utils │ │ │ ├── BottomMenuContent.kt │ │ │ └── Constants.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── baseline_person_pin_circle_24.xml │ │ ├── baseline_signal_wifi_statusbar_connected_no_internet_4_24.xml │ │ ├── baseline_speed_24.xml │ │ ├── ic_launcher_background.xml │ │ ├── icon_download.png │ │ ├── icon_upload.png │ │ └── settings.xml │ │ ├── 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-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── laboontech │ └── internetspeedtestui │ └── 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: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/.idea/.gitignore -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | InternetSpeedTester -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InternetSpeedTestUI 2 | Internet Speed Test Ui app is made using Jetpack Compose. 3 | Canvas is used to create speed indicator design and Animatable apis for animations. 4 | 5 | 6 | https://user-images.githubusercontent.com/56515866/226181036-bd26b235-b32b-43a2-aedf-5ac2169ae303.mp4 7 | 8 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-android' 5 | id 'kotlin-kapt' 6 | } 7 | 8 | android { 9 | namespace 'com.laboontech.internetspeedtestui' 10 | compileSdk 33 11 | 12 | defaultConfig { 13 | applicationId "com.laboontech.internetspeedtestui" 14 | minSdk 24 15 | targetSdk 33 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | vectorDrawables { 21 | useSupportLibrary true 22 | } 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | buildFeatures { 39 | compose true 40 | } 41 | composeOptions { 42 | kotlinCompilerExtensionVersion '1.3.0' 43 | } 44 | packagingOptions { 45 | resources { 46 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 47 | } 48 | } 49 | 50 | } 51 | 52 | dependencies { 53 | 54 | implementation 'androidx.core:core-ktx:1.8.0' 55 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' 56 | implementation 'androidx.activity:activity-compose:1.5.1' 57 | implementation "androidx.compose.ui:ui:$compose_ui_version" 58 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" 59 | implementation 'androidx.compose.material:material:1.2.1' 60 | testImplementation 'junit:junit:4.13.2' 61 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 62 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 63 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" 64 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" 65 | debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" 66 | 67 | //Internet speed checker 68 | implementation 'com.github.oatrice:internet-speed-testing:1.0.1' 69 | 70 | 71 | 72 | } -------------------------------------------------------------------------------- /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/laboontech/internetspeedtestui/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui 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.laboontech.internetspeedtester", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.material.MaterialTheme 8 | import androidx.compose.material.Surface 9 | import androidx.compose.ui.Modifier 10 | import com.laboontech.internetspeedtestui.presentation.feature_speedtest.SpeedTestScreen 11 | import com.laboontech.internetspeedtestui.presentation.ui.theme.InternetSpeedTesterTheme 12 | 13 | 14 | 15 | class MainActivity : ComponentActivity() { 16 | 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | 21 | setContent { 22 | InternetSpeedTesterTheme { 23 | // A surface container using the 'background' color from the theme 24 | Surface( 25 | modifier = Modifier.fillMaxSize(), 26 | color = MaterialTheme.colors.background 27 | ) { 28 | SpeedTestScreen() 29 | } 30 | } 31 | } 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/presentation/feature_speedtest/SpeedTestScreen.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.presentation.feature_speedtest 2 | 3 | 4 | import androidx.compose.animation.core.Animatable 5 | import androidx.compose.animation.core.AnimationVector1D 6 | import androidx.compose.animation.core.CubicBezierEasing 7 | import androidx.compose.animation.core.LinearOutSlowInEasing 8 | import androidx.compose.animation.core.Spring 9 | import androidx.compose.animation.core.animateDpAsState 10 | import androidx.compose.animation.core.keyframes 11 | import androidx.compose.animation.core.spring 12 | import androidx.compose.foundation.BorderStroke 13 | import androidx.compose.foundation.Canvas 14 | import androidx.compose.foundation.Image 15 | import androidx.compose.foundation.background 16 | import androidx.compose.foundation.clickable 17 | import androidx.compose.foundation.layout.Arrangement 18 | import androidx.compose.foundation.layout.Box 19 | import androidx.compose.foundation.layout.Column 20 | import androidx.compose.foundation.layout.IntrinsicSize 21 | import androidx.compose.foundation.layout.Row 22 | import androidx.compose.foundation.layout.RowScope 23 | import androidx.compose.foundation.layout.Spacer 24 | import androidx.compose.foundation.layout.aspectRatio 25 | import androidx.compose.foundation.layout.fillMaxHeight 26 | import androidx.compose.foundation.layout.fillMaxSize 27 | import androidx.compose.foundation.layout.fillMaxWidth 28 | import androidx.compose.foundation.layout.height 29 | import androidx.compose.foundation.layout.padding 30 | import androidx.compose.foundation.layout.size 31 | import androidx.compose.foundation.layout.width 32 | import androidx.compose.foundation.shape.RoundedCornerShape 33 | import androidx.compose.material.BottomNavigation 34 | import androidx.compose.material.ButtonDefaults 35 | import androidx.compose.material.Icon 36 | import androidx.compose.material.MaterialTheme 37 | import androidx.compose.material.OutlinedButton 38 | import androidx.compose.material.Text 39 | import androidx.compose.runtime.Composable 40 | import androidx.compose.runtime.getValue 41 | import androidx.compose.runtime.mutableStateOf 42 | import androidx.compose.runtime.remember 43 | import androidx.compose.runtime.rememberCoroutineScope 44 | import androidx.compose.runtime.setValue 45 | import androidx.compose.ui.Alignment 46 | import androidx.compose.ui.Modifier 47 | import androidx.compose.ui.draw.clip 48 | import androidx.compose.ui.geometry.Offset 49 | import androidx.compose.ui.geometry.Size 50 | import androidx.compose.ui.graphics.Color 51 | import androidx.compose.ui.graphics.StrokeCap 52 | import androidx.compose.ui.graphics.drawscope.DrawScope 53 | import androidx.compose.ui.graphics.drawscope.Stroke 54 | import androidx.compose.ui.graphics.drawscope.rotate 55 | import androidx.compose.ui.res.painterResource 56 | import androidx.compose.ui.res.stringResource 57 | import androidx.compose.ui.text.font.FontFamily 58 | import androidx.compose.ui.text.font.FontWeight 59 | import androidx.compose.ui.tooling.preview.Devices 60 | import androidx.compose.ui.tooling.preview.Preview 61 | import androidx.compose.ui.unit.dp 62 | import androidx.compose.ui.unit.sp 63 | import com.laboontech.internetspeedtestui.R 64 | import com.laboontech.internetspeedtestui.presentation.ui.theme.ArcColorPrimary 65 | import com.laboontech.internetspeedtestui.presentation.ui.theme.DarkGradient 66 | import com.laboontech.internetspeedtestui.presentation.ui.theme.LightColor 67 | import com.laboontech.internetspeedtestui.presentation.ui.theme.ArcGradient 68 | import com.laboontech.internetspeedtestui.presentation.ui.theme.DarkColor 69 | import com.laboontech.internetspeedtestui.presentation.ui.theme.DarkColor2 70 | import com.laboontech.internetspeedtestui.presentation.ui.theme.LightColor2 71 | import com.laboontech.internetspeedtestui.presentation.ui.theme.TitleColor 72 | import com.laboontech.internetspeedtestui.presentation.uistate.UiState 73 | import com.laboontech.internetspeedtestui.utils.BottomMenuContent 74 | import com.laboontech.internetspeedtestui.utils.Constants 75 | import kotlinx.coroutines.launch 76 | import kotlin.math.floor 77 | import kotlin.math.roundToInt 78 | 79 | 80 | /** 81 | * SpeedTest Main Screen 82 | * */ 83 | @Composable 84 | fun SpeedTestScreen() { 85 | 86 | 87 | val coroutineScope = rememberCoroutineScope() 88 | 89 | val animation = remember { 90 | Animatable(0f) 91 | } 92 | 93 | val maxSpeed = remember { mutableStateOf(0f) } 94 | maxSpeed.value = java.lang.Float.max(maxSpeed.value, animation.value * 100f) 95 | 96 | 97 | 98 | Box(modifier = Modifier.fillMaxSize()) { 99 | Column( 100 | modifier = Modifier 101 | .fillMaxSize() 102 | .background(DarkGradient) 103 | ) { 104 | Header() 105 | Spacer(modifier = Modifier.height(30.dp)) 106 | SpeedIndicatorContent(state = animation.toUiState(maxSpeed.value)) { 107 | coroutineScope.launch { 108 | maxSpeed.value = 0f 109 | startAnimation(animation) 110 | } 111 | } 112 | Spacer(modifier = Modifier.height(40.dp)) 113 | SpeedInformation(state = animation.toUiState(maxSpeed.value)) 114 | } 115 | 116 | BottomMenu( 117 | items = listOf( 118 | BottomMenuContent( 119 | stringResource(id = R.string.network), 120 | R.drawable.baseline_signal_wifi_statusbar_connected_no_internet_4_24 121 | ), 122 | BottomMenuContent( 123 | stringResource(id = R.string.speed), 124 | R.drawable.baseline_speed_24 125 | ), 126 | BottomMenuContent( 127 | stringResource(id = R.string.location), 128 | R.drawable.baseline_person_pin_circle_24 129 | ) 130 | ), 131 | modifier = Modifier.align(Alignment.BottomCenter) 132 | ) 133 | } 134 | 135 | 136 | } 137 | 138 | 139 | suspend fun startAnimation(animation: Animatable) { 140 | animation.animateTo(0.65f, keyframes { 141 | durationMillis = 9000 142 | 0f at 0 with CubicBezierEasing(0f, 1.5f, 0.8f, 1f) 143 | 0.12f at 1000 with CubicBezierEasing(0.2f, -1.5f, 0f, 1f) 144 | 0.20f at 2000 with CubicBezierEasing(0.2f, -2f, 0f, 1f) 145 | 0.35f at 3000 with CubicBezierEasing(0.2f, -1.5f, 0f, 1f) 146 | 0.62f at 4000 with CubicBezierEasing(0.2f, -2f, 0f, 1f) 147 | 0.75f at 5000 with CubicBezierEasing(0.2f, -2f, 0f, 1f) 148 | 0.89f at 6000 with CubicBezierEasing(0.2f, -1.2f, 0f, 1f) 149 | 0.82f at 7500 with LinearOutSlowInEasing 150 | }) 151 | } 152 | 153 | 154 | fun Animatable.toUiState(maxSpeed: Float) = UiState( 155 | arcValue = value, 156 | speed = "%.1f".format(value * 100), 157 | uploadSpeed = "%.1f".format(value * 50), 158 | ping = if (value > 0.2f) "${(value * 15).roundToInt()} ms" else "0.0 ms", 159 | maxSpeed = if (maxSpeed > 0f) "%.1f mbps".format(maxSpeed) else "0.0 mbps", 160 | inProgress = isRunning 161 | ) 162 | 163 | /** 164 | * Header View - Header Title and Setting Icon 165 | * */ 166 | @Composable 167 | fun Header() { 168 | Row( 169 | modifier = Modifier 170 | .fillMaxWidth() 171 | .padding(20.dp), 172 | horizontalArrangement = Arrangement.SpaceBetween, 173 | verticalAlignment = Alignment.CenterVertically 174 | ) { 175 | 176 | Text( 177 | text = stringResource(id = R.string.app_name), 178 | color = Color.White, 179 | fontSize = 24.sp, 180 | fontFamily = FontFamily.Serif 181 | ) 182 | Image( 183 | painter = painterResource(id = R.drawable.settings), 184 | contentDescription = stringResource(id = R.string.settings) 185 | ) 186 | 187 | } 188 | } 189 | 190 | 191 | /** 192 | * Download Speed, Upload Speed, Max Speed and Ping Information 193 | * */ 194 | @Composable 195 | fun SpeedInformation( 196 | state: UiState 197 | ) { 198 | 199 | @Composable 200 | fun RowScope.InfoColumn(title: String, value: String) { 201 | Column( 202 | horizontalAlignment = Alignment.CenterHorizontally, 203 | modifier = Modifier.weight(1f) 204 | ) { 205 | Text( 206 | text = title, 207 | color = TitleColor, 208 | fontFamily = FontFamily.Monospace, 209 | fontSize = 12.sp 210 | ) 211 | Text( 212 | text = value, 213 | style = MaterialTheme.typography.body2, 214 | color = Color.White, 215 | modifier = Modifier.padding(vertical = 8.dp), 216 | fontSize = 16.sp, 217 | fontWeight = FontWeight.Bold 218 | ) 219 | } 220 | } 221 | 222 | // Download Speed 223 | // Upload Speed 224 | Row( 225 | Modifier 226 | .fillMaxWidth() 227 | .height(IntrinsicSize.Min) 228 | ) { 229 | InfoColumn( 230 | title = stringResource(id = R.string.download_speed), 231 | value = "${state.speed} mbps" 232 | ) 233 | VerticalDivider() 234 | InfoColumn( 235 | title = stringResource(id = R.string.upload_speed), 236 | value = "${state.uploadSpeed} mbps" 237 | ) 238 | } 239 | 240 | Spacer(modifier = Modifier.height(40.dp)) 241 | 242 | // Ping 243 | // Max Speed 244 | Row( 245 | Modifier 246 | .fillMaxWidth() 247 | .height(IntrinsicSize.Min) 248 | ) { 249 | InfoColumn(title = stringResource(id = R.string.ping), value = state.ping) 250 | VerticalDivider() 251 | InfoColumn( 252 | title = stringResource(id = R.string.max_speed), 253 | value = state.maxSpeed 254 | ) 255 | } 256 | } 257 | 258 | 259 | /** 260 | * Vertical Divider 261 | * */ 262 | @Composable 263 | fun VerticalDivider() { 264 | Box( 265 | modifier = Modifier 266 | .fillMaxHeight() 267 | .background(Color(0xFF414D66)) 268 | .width(1.dp) 269 | ) 270 | } 271 | 272 | /** 273 | * This function contains - Speed Indicator, Speed Value text, Start/Stop button 274 | * */ 275 | @Composable 276 | fun SpeedIndicatorContent( 277 | state: UiState, 278 | onclick: () -> Unit 279 | ) { 280 | 281 | val buttonHorizontalPadding by animateDpAsState( 282 | targetValue = if (state.inProgress) 40.dp else 20.dp, 283 | animationSpec = spring( 284 | dampingRatio = Spring.DampingRatioMediumBouncy, 285 | stiffness = Spring.StiffnessLow 286 | ) 287 | ) 288 | 289 | Box( 290 | contentAlignment = Alignment.BottomCenter, 291 | modifier = Modifier 292 | .fillMaxWidth() 293 | .aspectRatio(1f) 294 | ) { 295 | 296 | 297 | /* Circular speed indicator */ 298 | Canvas( 299 | modifier = Modifier 300 | .fillMaxSize() 301 | .padding(40.dp) 302 | ) { 303 | 304 | // Draw Scale lines 305 | drawLines(state.arcValue, Constants.MAX_VALUE_FOR_LINES) 306 | 307 | // Draw Arc 308 | drawArcs(state.arcValue, Constants.MAX_VALUE_FOR_LINES) 309 | } 310 | 311 | 312 | /* Speed value Texts */ 313 | Column( 314 | modifier = Modifier 315 | .fillMaxSize(), 316 | verticalArrangement = Arrangement.Center, 317 | horizontalAlignment = Alignment.CenterHorizontally 318 | ) { 319 | 320 | // Download Text 321 | Text( 322 | text = stringResource(id = R.string.download), 323 | style = MaterialTheme.typography.caption, 324 | color = Color.White 325 | ) 326 | 327 | 328 | // Speed Value 329 | Text( 330 | text = state.speed, 331 | fontSize = 45.sp, 332 | color = Color.White, 333 | fontWeight = FontWeight.Bold, 334 | fontFamily = FontFamily.SansSerif 335 | ) 336 | 337 | // mbps text 338 | Text( 339 | text = stringResource(id = R.string.mbps), 340 | style = MaterialTheme.typography.caption, 341 | color = Color.White 342 | ) 343 | 344 | 345 | } 346 | 347 | 348 | 349 | 350 | 351 | /* Start check speed value Button */ 352 | OutlinedButton( 353 | onClick = { 354 | if (!state.inProgress) onclick() 355 | }, 356 | modifier = Modifier.padding(bottom = 24.dp), 357 | colors = ButtonDefaults.buttonColors( 358 | backgroundColor = Color.Transparent, 359 | contentColor = Color.White 360 | ), 361 | shape = RoundedCornerShape(24.dp), 362 | border = BorderStroke(width = 1.dp, color = LightColor2), 363 | ) { 364 | 365 | // Start button text 366 | Text( 367 | modifier = Modifier 368 | .padding(horizontal = buttonHorizontalPadding, vertical = 4.dp), 369 | text = if (!state.inProgress) { 370 | stringResource(id = R.string.start) 371 | } else { 372 | stringResource(id = R.string.checking_internet_speed) 373 | }, 374 | color = if (state.inProgress) { 375 | Color.White.copy(alpha = 0.5f) 376 | } else { 377 | Color.White 378 | } 379 | 380 | ) 381 | 382 | } 383 | } 384 | 385 | 386 | } 387 | 388 | 389 | /** 390 | * Draw arcs extension function to draw an speed indicator arc above the lines 391 | * */ 392 | fun DrawScope.drawArcs(progress: Float, maxValue: Float) { 393 | val startAngle = 270 - maxValue / 2 394 | val sweepAngle = maxValue * progress 395 | 396 | val topLeft = Offset(50f, 50f) 397 | val size = Size(size.width - 100f, size.height - 100f) 398 | 399 | fun drawBlur() { 400 | for (i in 0..20) { 401 | drawArc( 402 | color = ArcColorPrimary.copy(alpha = i / 900f), 403 | startAngle = startAngle, 404 | sweepAngle = sweepAngle, 405 | useCenter = false, 406 | topLeft = topLeft, 407 | size = size, 408 | style = Stroke(width = 80f + (20 - i) * 20, cap = StrokeCap.Round) 409 | ) 410 | } 411 | } 412 | 413 | fun drawStroke() { 414 | drawArc( 415 | color = ArcColorPrimary, 416 | startAngle = startAngle, 417 | sweepAngle = sweepAngle, 418 | useCenter = false, 419 | topLeft = topLeft, 420 | size = size, 421 | style = Stroke(width = 86f, cap = StrokeCap.Round) 422 | ) 423 | } 424 | 425 | fun drawGradient() { 426 | drawArc( 427 | brush = ArcGradient, 428 | startAngle = startAngle, 429 | sweepAngle = sweepAngle, 430 | useCenter = false, 431 | topLeft = topLeft, 432 | size = size, 433 | style = Stroke(width = 80f, cap = StrokeCap.Round) 434 | ) 435 | } 436 | 437 | drawBlur() 438 | drawStroke() 439 | drawGradient() 440 | } 441 | 442 | /** 443 | * Draw lines extension function to draw lines which look like a round scale 444 | * */ 445 | fun DrawScope.drawLines( 446 | progress: Float, 447 | maxValue: Float, 448 | numberOfLines: Int = Constants.DEFAULT_NUMBER_OF_LINES 449 | ) { 450 | 451 | // To calculate rotation size 452 | val oneRotation = maxValue / numberOfLines 453 | // Start value of indicator, it can start from 0 or from current progress 454 | val startValue = if (progress == 0f) 0 else floor(x = progress * numberOfLines).toInt() + 1 455 | 456 | 457 | // Loop starts from start value to the number of lines 458 | for (i in startValue..numberOfLines) { 459 | val rotationDegree = i * oneRotation + (180 - maxValue) / 2 460 | rotate(degrees = rotationDegree) { 461 | drawLine( 462 | color = LightColor, 463 | start = Offset(if (i % 5 == 0) 80f else 30f, size.height / 2), 464 | end = Offset(0f, size.height / 2), 465 | strokeWidth = 8f, 466 | cap = StrokeCap.Round 467 | ) 468 | } 469 | } 470 | 471 | 472 | } 473 | 474 | 475 | @Composable 476 | fun BottomMenu( 477 | items: List, 478 | modifier: Modifier = Modifier, 479 | activeHighlightColor: Color = LightColor, 480 | activeTextColor: Color = Color.White, 481 | inactiveTextColor: Color = LightColor2, 482 | initialSelectedItemIndex: Int = 1 483 | ) { 484 | var selectedItemIndex by remember { 485 | mutableStateOf(initialSelectedItemIndex) 486 | } 487 | Row( 488 | horizontalArrangement = Arrangement.SpaceAround, 489 | verticalAlignment = Alignment.CenterVertically, 490 | modifier = modifier 491 | .fillMaxWidth() 492 | .background(DarkColor) 493 | .padding(15.dp) 494 | ) { 495 | items.forEachIndexed { index, item -> 496 | BottomMenuItem( 497 | item = item, 498 | isSelected = index == selectedItemIndex, 499 | activeHighlightColor = activeHighlightColor, 500 | activeTextColor = activeTextColor, 501 | inactiveTextColor = inactiveTextColor 502 | ) { 503 | selectedItemIndex = index 504 | } 505 | } 506 | } 507 | } 508 | 509 | @Composable 510 | fun BottomMenuItem( 511 | item: BottomMenuContent, 512 | isSelected: Boolean = false, 513 | activeHighlightColor: Color = LightColor, 514 | activeTextColor: Color = Color.White, 515 | inactiveTextColor: Color = LightColor2, 516 | onItemClick: () -> Unit 517 | ) { 518 | Column( 519 | horizontalAlignment = Alignment.CenterHorizontally, 520 | verticalArrangement = Arrangement.Center, 521 | modifier = Modifier 522 | .clickable { 523 | onItemClick() 524 | } 525 | ) { 526 | Box( 527 | contentAlignment = Alignment.Center, 528 | modifier = Modifier 529 | .clip(RoundedCornerShape(10.dp)) 530 | .background(if (isSelected) activeHighlightColor else Color.Transparent) 531 | .padding(10.dp) 532 | ) { 533 | Icon( 534 | painter = painterResource(id = item.iconId), 535 | contentDescription = item.title, 536 | tint = if (isSelected) activeTextColor else inactiveTextColor, 537 | modifier = Modifier.size(20.dp) 538 | ) 539 | } 540 | Text( 541 | text = item.title, 542 | color = if (isSelected) activeTextColor else inactiveTextColor 543 | ) 544 | } 545 | 546 | } 547 | 548 | @Preview(device = Devices.PIXEL) 549 | @Composable 550 | fun SpeedTestScreenPreview() { 551 | SpeedTestScreen() 552 | } -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/presentation/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.presentation.ui.theme 2 | 3 | import androidx.compose.ui.geometry.Offset 4 | import androidx.compose.ui.graphics.Brush 5 | import androidx.compose.ui.graphics.Color 6 | 7 | val Purple200 = Color(0xFFBB86FC) 8 | val Purple500 = Color(0xFF7553B1) 9 | val Purple700 = Color(0xFF673AB7) 10 | val Teal200 = Color(0xFF03DAC5) 11 | 12 | val DarkColor = Color(0xFF020B29) 13 | val DarkColor2 = Color(0xFF112055) 14 | val LightColor = Color(0xff505cf3) 15 | val LightColor2 = Color(0xFF7A82E9) 16 | 17 | val ArcColorPrimary = Color(0xFFFFC107) 18 | val ArcColorSecondary = Color(0xFFFFE500) 19 | 20 | val TitleColor = Color(0xFFBCC5BC) 21 | 22 | 23 | val ArcGradient = Brush.linearGradient( 24 | colors = listOf(ArcColorSecondary, ArcColorPrimary), 25 | start = Offset(0f, 0f), 26 | end = Offset(Float.POSITIVE_INFINITY, 0f) 27 | ) 28 | 29 | val DarkGradient = Brush.verticalGradient( 30 | colors = listOf(DarkColor2, DarkColor) 31 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/presentation/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.presentation.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/presentation/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.presentation.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun InternetSpeedTesterTheme( 32 | darkTheme: Boolean = isSystemInDarkTheme(), 33 | content: @Composable () -> Unit 34 | ) { 35 | val colors = if (darkTheme) { 36 | DarkColorPalette 37 | } else { 38 | LightColorPalette 39 | } 40 | 41 | MaterialTheme( 42 | colors = colors, 43 | typography = Typography, 44 | shapes = Shapes, 45 | content = content 46 | ) 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/presentation/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.presentation.ui.theme 2 | 3 | import androidx.compose.material.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 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/presentation/uistate/UiState.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.presentation.uistate 2 | 3 | class UiState( 4 | val speed: String = "", 5 | val uploadSpeed: String = "", 6 | val ping: String = "-", 7 | val maxSpeed: String = "-", 8 | val arcValue: Float = 0f, 9 | val inProgress: Boolean = false 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/utils/BottomMenuContent.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.utils 2 | 3 | import androidx.annotation.DrawableRes 4 | 5 | data class BottomMenuContent( 6 | val title: String, 7 | @DrawableRes val iconId: Int 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/laboontech/internetspeedtestui/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui.utils 2 | 3 | object Constants { 4 | 5 | 6 | const val DEFAULT_NUMBER_OF_LINES = 40 7 | const val MAX_VALUE_FOR_LINES = 240f 8 | const val TAG_TEST = "TestMyAndroidApp" 9 | 10 | 11 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_person_pin_circle_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_signal_wifi_statusbar_connected_no_internet_4_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_speed_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/drawable/icon_download.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/drawable/icon_upload.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /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/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #FF112055 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Internet Speed Test 3 | Settings 4 | 5 | Download 6 | Upload 7 | mbps 8 | PING 9 | MAX SPEED 10 | UPLOAD SPEED 11 | DOWNLOAD SPEED 12 | Checking Internet Speed 13 | 14 | START 15 | STOP 16 | 17 | Network 18 | Location 19 | Speed 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/laboontech/internetspeedtestui/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.laboontech.internetspeedtestui 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | compose_ui_version = '1.2.1' 4 | } 5 | }// Top-level build file where you can add configuration options common to all sub-projects/modules. 6 | plugins { 7 | id 'com.android.application' version '8.1.0-alpha08' apply false 8 | id 'com.android.library' version '8.1.0-alpha08' apply false 9 | id 'org.jetbrains.kotlin.android' version '1.7.10' apply false 10 | } 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | android.enableJetifier=true 19 | # Kotlin code style for this project: "official" or "obsolete": 20 | kotlin.code.style=official 21 | # Enables namespacing of each library's R class so that its R class includes only the 22 | # resources declared in the library itself and none from the library's dependencies, 23 | # thereby reducing the size of the R class for that library 24 | android.nonTransitiveRClass=true 25 | android.defaults.buildfeatures.buildconfig=true 26 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagarkhawse/InternetSpeedTestUI/450ceef9a02e76f5a2c9e7b3cabc05fb80102e7f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Stop when "xargs" is not available. 209 | if ! command -v xargs >/dev/null 2>&1 210 | then 211 | die "xargs is not available" 212 | fi 213 | 214 | # Use "xargs" to parse quoted args. 215 | # 216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 217 | # 218 | # In Bash we could simply go: 219 | # 220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 221 | # set -- "${ARGS[@]}" "$@" 222 | # 223 | # but POSIX shell has neither arrays nor command substitution, so instead we 224 | # post-process each arg (as a line of input to sed) to backslash-escape any 225 | # character that might be a shell metacharacter, then use eval to reverse 226 | # that process (while maintaining the separation between arguments), and wrap 227 | # the whole thing up as a single "set" statement. 228 | # 229 | # This will of course break if any of these variables contains a newline or 230 | # an unmatched quote. 231 | # 232 | 233 | eval "set -- $( 234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 235 | xargs -n1 | 236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 237 | tr '\n' ' ' 238 | )" '"$@"' 239 | 240 | exec "$JAVACMD" "$@" 241 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url "https://jitpack.io" } 14 | } 15 | 16 | } 17 | rootProject.name = "InternetSpeedTester" 18 | include ':app' 19 | --------------------------------------------------------------------------------