diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58f7f18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +app/build/ diff --git a/README.md b/README.md index a0181e4..d66ec91 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,40 @@ -# carputerandroid -androd capupter dashboard +# CarPuter Android + +Android car computer dashboard application. + +## Features + +- **Speed Display** – Large, colour-coded GPS speedometer (green / yellow / red) +- **Unit Toggle** – Tap "km/h" or "mph" label to switch units +- **Max Speed Tracker** – Tracks trip maximum speed; long-press to reset +- **Real-Time Clock** – Shows current time and date +- **GPS Status** – Shows GPS accuracy in metres +- **Always-On Screen** – Keeps the display on while driving +- **Immersive Fullscreen** – Hides navigation and status bars +- **Landscape Orientation** – Optimised for dashboard mounting + +## Requirements + +- Android 8.0 (API 26) or higher +- GPS / Location permission + +## Building + +1. Open in Android Studio, or build from the command line: + +```bash +./gradlew assembleDebug +``` + +2. Install on device: + +```bash +adb install app/build/outputs/apk/debug/app-debug.apk +``` + +## Permissions + +| Permission | Purpose | +|---|---| +| `ACCESS_FINE_LOCATION` | GPS speed reading | +| `ACCESS_COARSE_LOCATION` | Fallback location | diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f78ae56 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,48 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.kingfrezz.carputerandroid' + compileSdk 34 + + defaultConfig { + applicationId "com.kingfrezz.carputerandroid" + minSdk 26 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = '17' + } + buildFeatures { + viewBinding true + } +} + +dependencies { + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.11.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..2913620 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,3 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f58b7e4 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/kingfrezz/carputerandroid/MainActivity.kt b/app/src/main/java/com/kingfrezz/carputerandroid/MainActivity.kt new file mode 100644 index 0000000..e077a2c --- /dev/null +++ b/app/src/main/java/com/kingfrezz/carputerandroid/MainActivity.kt @@ -0,0 +1,225 @@ +package com.kingfrezz.carputerandroid + +import android.Manifest +import android.content.pm.PackageManager +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View +import android.view.WindowManager +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import com.kingfrezz.carputerandroid.databinding.ActivityMainBinding +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class MainActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMainBinding + private lateinit var locationManager: LocationManager + private lateinit var locationListener: LocationListener + + private val clockHandler = Handler(Looper.getMainLooper()) + private val clockRunnable = object : Runnable { + override fun run() { + updateClock() + clockHandler.postDelayed(this, 1000) + } + } + + companion object { + private const val LOCATION_PERMISSION_REQUEST = 1001 + private const val MS_TO_KMH = 3.6f + private const val MS_TO_MPH = 2.23694f + } + + private var useKmh = true + private var maxSpeed = 0f + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Keep screen on while driving + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + + // Full screen immersive mode + @Suppress("DEPRECATION") + window.decorView.systemUiVisibility = ( + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + or View.SYSTEM_UI_FLAG_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + ) + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + locationManager = getSystemService(LOCATION_SERVICE) as LocationManager + + setupLocationListener() + setupClickListeners() + requestLocationPermissions() + + binding.tvSpeed.text = "0" + binding.tvUnit.text = if (useKmh) "km/h" else "mph" + binding.tvMaxSpeed.text = getString(R.string.max_speed_format, 0, if (useKmh) "km/h" else "mph") + } + + override fun onResume() { + super.onResume() + clockHandler.post(clockRunnable) + if (ContextCompat.checkSelfPermission( + this, Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { + startLocationUpdates() + } + } + + override fun onPause() { + super.onPause() + clockHandler.removeCallbacks(clockRunnable) + stopLocationUpdates() + } + + private fun setupClickListeners() { + // Toggle speed unit on tap; preserve max speed by converting to new unit + binding.tvUnit.setOnClickListener { + if (useKmh) { + // km/h → mph + maxSpeed = maxSpeed / MS_TO_KMH * MS_TO_MPH + useKmh = false + } else { + // mph → km/h + maxSpeed = maxSpeed / MS_TO_MPH * MS_TO_KMH + useKmh = true + } + val unitLabel = if (useKmh) "km/h" else "mph" + binding.tvUnit.text = unitLabel + binding.tvMaxSpeed.text = getString(R.string.max_speed_format, maxSpeed.toInt(), unitLabel) + } + + // Reset max speed on long press + binding.tvMaxSpeed.setOnLongClickListener { + maxSpeed = 0f + binding.tvMaxSpeed.text = getString(R.string.max_speed_format, 0, if (useKmh) "km/h" else "mph") + true + } + } + + private fun setupLocationListener() { + locationListener = LocationListener { location -> + updateSpeed(location) + updateGpsAccuracy(location) + } + } + + private fun updateSpeed(location: Location) { + val speedMs = if (location.hasSpeed()) location.speed else 0f + val speed = if (useKmh) speedMs * MS_TO_KMH else speedMs * MS_TO_MPH + val speedInt = speed.toInt() + + if (speed > maxSpeed) { + maxSpeed = speed + binding.tvMaxSpeed.text = getString( + R.string.max_speed_format, + maxSpeed.toInt(), + if (useKmh) "km/h" else "mph" + ) + } + + binding.tvSpeed.text = speedInt.toString() + + // Color code speed using unit-appropriate thresholds: + // green < 60 km/h (37 mph) + // yellow < 100 km/h (62 mph) + // red >= 100 km/h (62 mph) + val lowThreshold = if (useKmh) 60 else 37 + val highThreshold = if (useKmh) 100 else 62 + val color = when { + speedInt < lowThreshold -> ContextCompat.getColor(this, R.color.speed_low) + speedInt < highThreshold -> ContextCompat.getColor(this, R.color.speed_mid) + else -> ContextCompat.getColor(this, R.color.speed_high) + } + binding.tvSpeed.setTextColor(color) + } + + private fun updateGpsAccuracy(location: Location) { + val accuracyText = if (location.hasAccuracy()) { + getString(R.string.gps_accuracy_format, location.accuracy.toInt()) + } else { + getString(R.string.gps_active) + } + binding.tvGpsStatus.text = accuracyText + } + + private fun updateClock() { + val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) + val dateFormat = SimpleDateFormat("EEE, MMM d", Locale.getDefault()) + val now = Date() + binding.tvTime.text = timeFormat.format(now) + binding.tvDate.text = dateFormat.format(now) + } + + private fun requestLocationPermissions() { + val fineLocation = Manifest.permission.ACCESS_FINE_LOCATION + val coarseLocation = Manifest.permission.ACCESS_COARSE_LOCATION + + if (ContextCompat.checkSelfPermission(this, fineLocation) == PackageManager.PERMISSION_GRANTED) { + startLocationUpdates() + } else { + ActivityCompat.requestPermissions( + this, + arrayOf(fineLocation, coarseLocation), + LOCATION_PERMISSION_REQUEST + ) + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == LOCATION_PERMISSION_REQUEST) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startLocationUpdates() + } else { + binding.tvGpsStatus.text = getString(R.string.gps_denied) + } + } + } + + private fun startLocationUpdates() { + if (ActivityCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) != PackageManager.PERMISSION_GRANTED + ) return + + val hasGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + if (hasGps) { + locationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, + 500L, + 0f, + locationListener + ) + binding.tvGpsStatus.text = getString(R.string.gps_active) + } else { + binding.tvGpsStatus.text = getString(R.string.gps_unavailable) + } + } + + private fun stopLocationUpdates() { + locationManager.removeUpdates(locationListener) + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..e069e24 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/chip_background.xml b/app/src/main/res/drawable/chip_background.xml new file mode 100644 index 0000000..b67c2bd --- /dev/null +++ b/app/src/main/res/drawable/chip_background.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a3c2772 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..e5689fc --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..e5689fc --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..7a5ad59 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..7a5ad59 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..8bae1b4 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..8bae1b4 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..4b618e1 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..4b618e1 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b7dbede Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b7dbede Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..a362dde Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..a362dde Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..1ab403d --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,17 @@ + + + + #0A0A0A + + + #FFFFFF + #8A8A8A + + + #4CAF50 + #FFC107 + #F44336 + + + #1E1E1E + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..579a73f --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,9 @@ + + + CarPuter + GPS Active + GPS Denied + GPS Off + GPS ±%dm + Max: %d %s + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..2e0945f --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5435907 --- /dev/null +++ b/build.gradle @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.3.0' apply false + id 'org.jetbrains.kotlin.android' version '1.9.22' apply false +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f0a2e55 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +kotlin.code.style=official +android.nonTransitiveRClass=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 0000000..b1b8ef5 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a80b22c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..a388f9d --- /dev/null +++ b/gradlew @@ -0,0 +1,215 @@ +#!/bin/sh +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by "Gradle" +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other sh-compliant shell, such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patch: +# +# (2) This script targets any POSIX shell, so it avoids most Bashisms. To +# maintain that target, use "bash --posix" to run the script during +# development and testing. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( absolute + *) app_path=$APP_HOME$link ;; #( relative + esac +done + +# This is reliable for all POSIX shells +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + ;; + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX path + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # temporary variables we have allocated above; given the number of + # temporary variables we (the while loop), we need to keep a running + # total of them and add 1 for the final 'shift'. + set -- "$@" "$arg" + shift # remove old arg + done +fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$DEFAULT_JVM_OPTS" ) +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# temporary newlines generated from the "xargs" parsing; and then we effectively +# undo that backslash-escaping in the for loop below, where we are free to use +# temporary newlines. + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + ) $@" + +exec "$JAVACMD" "$@" diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5a27a38 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "CarPuter" +include ':app'