├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
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 |
--------------------------------------------------------------------------------