diff --git a/android/app/build.gradle b/android/app/build.gradle
index 21bd8ea..fe078ff 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -104,5 +104,6 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.paging:paging-runtime-ktx:$paging_version"
+ implementation 'com.pavelsikun:material-seekbar-preference:2.3.0'
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 75442f5..00b11fd 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,52 +1,67 @@
+
-
-
+
-
-
+
+
+
+
+ android:theme="@style/AppTheme">
+
+
+
+
+ android:exported="false" />
-
+ android:exported="false" />
+
+
-
+
\ No newline at end of file
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/MainActivity.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/MainActivity.kt
index d4bd042..402ec1c 100644
--- a/android/app/src/main/java/ai/kun/socialdistancealarm/MainActivity.kt
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/MainActivity.kt
@@ -1,6 +1,11 @@
package ai.kun.socialdistancealarm
import ai.kun.socialdistancealarm.alarm.BLETrace
+import ai.kun.socialdistancealarm.ui.home.HomeFragment
+import ai.kun.socialdistancealarm.ui.settings.SettingsActivity
+import ai.kun.socialdistancealarm.ui.timer.TimerFragment
+import ai.kun.socialdistancealarm.ui.timer.TimerState
+import ai.kun.socialdistancealarm.util.PrefUtil
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -10,6 +15,7 @@ import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
@@ -18,7 +24,7 @@ import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
-class MainActivity : AppCompatActivity() {
+class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
@@ -39,7 +45,7 @@ class MainActivity : AppCompatActivity() {
R.id.onBoardFragment_2,
R.id.onBoardFragment_3,
R.id.onBoardFragment_4,
- R.id.launchFragment-> {
+ R.id.launchFragment -> {
navView.visibility = View.GONE
toolbar.visibility = View.GONE
}
@@ -52,7 +58,8 @@ class MainActivity : AppCompatActivity() {
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
- val appBarConfiguration = AppBarConfiguration(setOf(R.id.navigation_history, R.id.navigation_home))
+ val appBarConfiguration =
+ AppBarConfiguration(setOf(R.id.navigation_history, R.id.navigation_home))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
@@ -65,15 +72,15 @@ class MainActivity : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
- menu?.let {
- // Change the state of the toolbar depending on the state of BLETrace
- BLETrace.isStarted.observeForever(Observer { isStarted ->
- setPausePlayOption(it, isStarted)
- })
-
- // Initialize to the current state
- setPausePlayOption(it, BLETrace.isStarted.value)
- }
+ menu?.let {
+ // Change the state of the toolbar depending on the state of BLETrace
+ BLETrace.isStarted.observeForever(Observer { isStarted ->
+ setPausePlayOption(it, isStarted)
+ })
+
+ // Initialize to the current state
+ setPausePlayOption(it, BLETrace.isStarted.value)
+ }
return true
}
@@ -111,9 +118,15 @@ class MainActivity : AppCompatActivity() {
}
R.id.app_bar_pause -> {
BLETrace.isPaused = true
+ PrefUtil.setTimerState(TimerState.Paused, this)
}
R.id.app_bar_play -> {
BLETrace.isPaused = false
+ PrefUtil.setTimerState(TimerState.Running, this)
+ }
+ R.id.action_settings -> {
+ val intent = Intent(this, SettingsActivity::class.java)
+ startActivity(intent)
}
}
return super.onOptionsItemSelected(item)
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/base/BaseFragment.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/base/BaseFragment.kt
new file mode 100644
index 0000000..306ec22
--- /dev/null
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/base/BaseFragment.kt
@@ -0,0 +1,22 @@
+package ai.kun.socialdistancealarm.ui.base
+
+import androidx.annotation.IdRes
+import androidx.fragment.app.Fragment
+
+abstract class BaseFragment: Fragment() {
+
+ protected fun addFragment(@IdRes containerViewId: Int, fragment: Fragment, fragmentTag: String) {
+ childFragmentManager
+ .beginTransaction()
+ .add(containerViewId, fragment, fragmentTag)
+ .commit()
+ }
+
+ protected fun replaceFragment(@IdRes containerViewId: Int, fragment: Fragment, fragmentTag: String, backStackStateName: String?) {
+ childFragmentManager
+ .beginTransaction()
+ .replace(containerViewId, fragment, fragmentTag)
+ .addToBackStack(backStackStateName)
+ .commit()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/home/HomeFragment.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/home/HomeFragment.kt
index 3a880b3..ae0a5b1 100644
--- a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/home/HomeFragment.kt
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/home/HomeFragment.kt
@@ -2,6 +2,8 @@ package ai.kun.socialdistancealarm.ui.home
import ai.kun.socialdistancealarm.R
import ai.kun.socialdistancealarm.alarm.BLETrace
+import ai.kun.socialdistancealarm.ui.base.BaseFragment
+import ai.kun.socialdistancealarm.ui.timer.TimerFragment
import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
@@ -16,14 +18,13 @@ import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-class HomeFragment : Fragment() {
+class HomeFragment : BaseFragment() {
private val TAG = "HomeFragment"
private val REQUEST_FINE_LOCATION = 2
@@ -43,6 +44,9 @@ class HomeFragment : Fragment() {
): View? {
val homeViewModel: HomeViewModel by viewModels()
val root = inflater.inflate(R.layout.fragment_home, container, false)
+
+ addFragment(R.id.child_fragment_container, TimerFragment.newInstance(), "TIMER_FRAGMENT_TAG")
+
val recyclerView = root.findViewById(R.id.recyclerView_devices)
context?.let { fragmentContext ->
val deviceListAdapter = DeviceListAdapter(fragmentContext)
@@ -60,7 +64,7 @@ class HomeFragment : Fragment() {
homeViewModel.isStarted.observe(viewLifecycleOwner, Observer { isStarted ->
isStarted?.let {
- setVisibility(root, it)
+// setVisibility(root, it)
}
})
}
@@ -94,7 +98,7 @@ class HomeFragment : Fragment() {
// Initialize the visibility
BLETrace.isStarted.value?.let {
- setVisibility(view, it)
+// setVisibility(view, it)
}
// Initialize the resume when tapping on the blue text...
@@ -241,5 +245,4 @@ class HomeFragment : Fragment() {
}
}
}
-
}
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/settings/SettingsActivity.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/settings/SettingsActivity.kt
new file mode 100644
index 0000000..8b485a9
--- /dev/null
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/settings/SettingsActivity.kt
@@ -0,0 +1,19 @@
+package ai.kun.socialdistancealarm.ui.settings
+
+import ai.kun.socialdistancealarm.R
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import kotlinx.android.synthetic.main.activity_main.*
+
+class SettingsActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_settings)
+ setSupportActionBar(toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ supportActionBar?.title = "Settings"
+ }
+}
+
+
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/settings/SettingsActivityFragment.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/settings/SettingsActivityFragment.kt
new file mode 100644
index 0000000..5dc69e4
--- /dev/null
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/settings/SettingsActivityFragment.kt
@@ -0,0 +1,11 @@
+package ai.kun.socialdistancealarm.ui.settings
+
+import ai.kun.socialdistancealarm.R
+import android.os.Bundle
+import androidx.preference.PreferenceFragmentCompat
+
+class SettingsActivityFragment : PreferenceFragmentCompat() {
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ addPreferencesFromResource(R.xml.preferences)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/timer/TimerFragment.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/timer/TimerFragment.kt
new file mode 100644
index 0000000..9a168b1
--- /dev/null
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/timer/TimerFragment.kt
@@ -0,0 +1,191 @@
+package ai.kun.socialdistancealarm.ui.timer
+
+import ai.kun.socialdistancealarm.R
+import ai.kun.socialdistancealarm.alarm.BLETrace
+import ai.kun.socialdistancealarm.ui.home.HomeViewModel
+import ai.kun.socialdistancealarm.util.PrefUtil
+import ai.kun.socialdistancealarm.util.PrefUtil.TIMER_STATE_ID
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import android.os.Bundle
+import android.os.CountDownTimer
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
+import androidx.preference.PreferenceManager
+import kotlinx.android.synthetic.main.fragment_timer.*
+import java.util.*
+import kotlin.concurrent.schedule
+
+class TimerFragment : Fragment(), OnSharedPreferenceChangeListener {
+
+ companion object {
+ fun newInstance(): TimerFragment {
+ return TimerFragment()
+ }
+ }
+
+ private lateinit var timer: CountDownTimer
+ private var timerLengthSeconds: Long = 0
+ private var timerState = TimerState.Stopped
+ private var secondsRemaining: Long = 0
+ private lateinit var sharedPreferenceChangeListener : OnSharedPreferenceChangeListener
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_timer, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val homeViewModel: HomeViewModel by viewModels()
+ context?.let { context ->
+ homeViewModel.isStarted.observe(viewLifecycleOwner, Observer { isStarted ->
+ isStarted?.let {
+ // If BLE is started then
+ if (timerState != TimerState.Running) {
+ Timer("BLE Two Ticks", false).schedule(2000) {
+ activity?.runOnUiThread {
+ startTimer(context)
+ timerState = TimerState.Running
+ }
+ }
+ }
+ }
+ })
+
+ sharedPreferenceChangeListener = this
+ val prefs: SharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(activity)
+
+ prefs.registerOnSharedPreferenceChangeListener(
+ sharedPreferenceChangeListener
+ )
+ }
+
+
+ }
+
+ private fun startTimer(context: Context) {
+ BLETrace.stop()
+ timerState = TimerState.Running
+ val twoTicks = timerLengthSeconds
+
+ timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
+ override fun onFinish() = onTimerFinished(context)
+
+ override fun onTick(millisUntilFinished: Long) {
+ secondsRemaining = millisUntilFinished / 1000
+
+ updateCountdownUI()
+ }
+ }.start()
+ }
+
+ private fun updateCountdownUI() {
+ val minutesUntilFinished = secondsRemaining / 60
+ val secondsInMinuteUntilFinished = secondsRemaining - minutesUntilFinished * 60
+ val secondsStr = secondsInMinuteUntilFinished.toString()
+ textView_countdown?.text =
+ "$minutesUntilFinished:${if (secondsStr.length == 2) secondsStr else "0" + secondsStr}"
+ }
+
+
+ private fun onTimerFinished(context: Context) {
+ timerState = TimerState.Stopped
+ BLETrace.start(true)
+
+ //set the length of the timer to be the one set in SettingsActivity
+ //if the length was changed when the timer was running
+ setNewTimerLength(context)
+
+ activity?.let { PrefUtil.setSecondsRemaining(timerLengthSeconds, it) }
+ secondsRemaining = timerLengthSeconds
+ updateCountdownUI()
+ }
+
+ private fun setNewTimerLength(context: Context) {
+ timerLengthSeconds = PrefUtil.getTimerLength(context).toLong()
+ }
+
+ private fun setPreviousTimerLength(context: Context) {
+ timerLengthSeconds = PrefUtil.getPreviousTimerLengthSeconds(context)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ context?.let { context ->
+ initTimer(context)
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+
+ context?.let { context ->
+
+ if (timerState == TimerState.Running) {
+ timer.cancel()
+ }
+
+ PrefUtil.setPreviousTimerLengthSeconds(timerLengthSeconds, context)
+ PrefUtil.setSecondsRemaining(secondsRemaining, context)
+ PrefUtil.setTimerState(timerState, context)
+ }
+ }
+
+ private fun initTimer(context: Context) {
+
+ timerState = PrefUtil.getTimerState(context)
+
+ //we don't want to change the length of the timer which is already running
+ //if the length was changed in settings while it was backgrounded
+ if (timerState == TimerState.Stopped)
+ setNewTimerLength(context)
+ else
+ setPreviousTimerLength(context)
+
+ secondsRemaining = (if (timerState == TimerState.Running || timerState == TimerState.Paused)
+ PrefUtil.getSecondsRemaining(context)
+ else
+ timerLengthSeconds)
+
+ if (secondsRemaining <= 0)
+ onTimerFinished(context)
+ else if (timerState == TimerState.Running)
+ startTimer(context)
+ updateCountdownUI()
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
+
+ context?.let { context ->
+
+ if (key == TIMER_STATE_ID) {
+ val timerState = PrefUtil.getTimerState(context)
+ Log.d("Timer State", timerState.toString())
+ when (timerState) {
+ TimerState.Stopped -> {
+ timer.cancel()
+ onTimerFinished(context)
+ }
+ TimerState.Paused -> {
+ timer.cancel()
+ this.timerState = TimerState.Paused
+ }else -> return
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/ui/timer/TimerState.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/timer/TimerState.kt
new file mode 100644
index 0000000..2c75039
--- /dev/null
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/ui/timer/TimerState.kt
@@ -0,0 +1,5 @@
+package ai.kun.socialdistancealarm.ui.timer
+
+enum class TimerState {
+ Stopped, Paused, Running
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/ai/kun/socialdistancealarm/util/PrefUtil.kt b/android/app/src/main/java/ai/kun/socialdistancealarm/util/PrefUtil.kt
new file mode 100644
index 0000000..6dd3ef3
--- /dev/null
+++ b/android/app/src/main/java/ai/kun/socialdistancealarm/util/PrefUtil.kt
@@ -0,0 +1,57 @@
+package ai.kun.socialdistancealarm.util
+
+import ai.kun.socialdistancealarm.ui.timer.TimerState
+import android.content.Context
+import androidx.preference.PreferenceManager
+
+
+object PrefUtil {
+ private const val TIMER_LENGTH_ID = "timer_length"
+
+ fun getTimerLength(context: Context): Int {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ return preferences.getInt(TIMER_LENGTH_ID, 10)
+ }
+
+ private const val PREVIOUS_TIMER_LENGTH_SECONDS_ID =
+ "previous_timer_length_seconds"
+
+ fun getPreviousTimerLengthSeconds(context: Context): Long {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ return preferences.getLong(PREVIOUS_TIMER_LENGTH_SECONDS_ID, 0)
+ }
+
+ fun setPreviousTimerLengthSeconds(seconds: Long, context: Context) {
+ val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
+ editor.putLong(PREVIOUS_TIMER_LENGTH_SECONDS_ID, seconds)
+ editor.apply()
+ }
+
+ const val TIMER_STATE_ID = "timer_state"
+
+ fun getTimerState(context: Context): TimerState {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ val ordinal = preferences.getInt(TIMER_STATE_ID, 0)
+ return TimerState.values()[ordinal]
+ }
+
+ fun setTimerState(state: TimerState, context: Context) {
+ val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
+ val ordinal = state.ordinal
+ editor.putInt(TIMER_STATE_ID, ordinal)
+ editor.apply()
+ }
+
+ private const val SECONDS_REMAINING_ID = "seconds_remaining"
+
+ fun getSecondsRemaining(context: Context): Long {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ return preferences.getLong(SECONDS_REMAINING_ID, 0)
+ }
+
+ fun setSecondsRemaining(seconds: Long, context: Context) {
+ val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
+ editor.putLong(SECONDS_REMAINING_ID, seconds)
+ editor.apply()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ic_settings.xml b/android/app/src/main/res/drawable/ic_settings.xml
new file mode 100644
index 0000000..a219c09
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_settings.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/layout/activity_settings.xml b/android/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..377a302
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/fragment_home.xml b/android/app/src/main/res/layout/fragment_home.xml
index 5c75bb7..4e9be29 100644
--- a/android/app/src/main/res/layout/fragment_home.xml
+++ b/android/app/src/main/res/layout/fragment_home.xml
@@ -7,18 +7,29 @@
android:background="@color/background"
tools:context=".ui.home.HomeFragment">
+
+
diff --git a/android/app/src/main/res/layout/fragment_timer.xml b/android/app/src/main/res/layout/fragment_timer.xml
new file mode 100644
index 0000000..3e0f706
--- /dev/null
+++ b/android/app/src/main/res/layout/fragment_timer.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/menu/menu_main.xml b/android/app/src/main/res/menu/menu_main.xml
index 37e4cf0..bb1f4ce 100644
--- a/android/app/src/main/res/menu/menu_main.xml
+++ b/android/app/src/main/res/menu/menu_main.xml
@@ -21,4 +21,12 @@
android:orderInCategory="100"
android:title="@string/action_notification_settings"
app:showAsAction="never" />
+
+
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index c9b0926..faf8232 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -35,5 +35,6 @@
Alarm
Device detection
Detection will run unless paused. While paused, the app will not detect other devices or be able to be detected.
-
+ Settings
+ This lists updates every 10 seconds.
diff --git a/android/app/src/main/res/xml/preferences.xml b/android/app/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..7f97c8a
--- /dev/null
+++ b/android/app/src/main/res/xml/preferences.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
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..12b43c4
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun May 10 10:55:43 IST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$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=""
+
+# 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
+ ;;
+ 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega