-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
58 changed files
with
2,544 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,28 @@ | ||
# OpenC25k | ||
|
||
Open source couch to 5k run tracker app | ||
|
||
Inspired by [this project.](https://github.com/roelb/Simple-C25K) | ||
|
||
## Features | ||
|
||
- C25k run list of which the user selects one individual run to track on each running session | ||
|
||
- Tap on a run to track it | ||
|
||
- Hold on a run to check/uncheck the completed checkbox | ||
|
||
- The run is broken into intervals, e.g. Walk 90 seconds, run 60 seconds which are tracked | ||
|
||
- Beeps and vibrates: | ||
- Once for walk | ||
- Twice for jog | ||
- 4 times for completed run | ||
|
||
- Sound and vibration enable and disable settings | ||
|
||
- Tracks which runs have been completed and displayed on checkboxes | ||
|
||
- Progress and settings store persistently between app runs. Can be reset by deleting app storage in android settings. | ||
|
||
- Material you colors which change depending on which theme the user has set for their device |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
plugins { | ||
id("com.android.application") | ||
id("org.jetbrains.kotlin.android") | ||
id("kotlin-parcelize") | ||
} | ||
|
||
android { | ||
namespace = "se.wmuth.openc25k" | ||
compileSdk = 34 | ||
|
||
defaultConfig { | ||
applicationId = "se.wmuth.openc25k" | ||
minSdk = 31 | ||
targetSdk = 33 | ||
versionCode = 1 | ||
versionName = "1.0" | ||
|
||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||
} | ||
|
||
buildFeatures { | ||
viewBinding = true | ||
} | ||
|
||
buildTypes { | ||
release { | ||
isMinifyEnabled = false | ||
proguardFiles( | ||
getDefaultProguardFile("proguard-android-optimize.txt"), | ||
"proguard-rules.pro" | ||
) | ||
} | ||
} | ||
compileOptions { | ||
sourceCompatibility = JavaVersion.VERSION_1_8 | ||
targetCompatibility = JavaVersion.VERSION_1_8 | ||
} | ||
kotlinOptions { | ||
jvmTarget = "1.8" | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") | ||
implementation("androidx.core:core-ktx:1.10.1") | ||
implementation("androidx.appcompat:appcompat:1.6.1") | ||
implementation("com.google.android.material:material:1.9.0") | ||
implementation("androidx.constraintlayout:constraintlayout:2.1.4") | ||
implementation("androidx.datastore:datastore-preferences:1.0.0") | ||
implementation("androidx.cardview:cardview:1.0.0") | ||
testImplementation("junit:junit:4.13.2") | ||
androidTestImplementation("androidx.test.ext:junit:1.1.5") | ||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"version": 3, | ||
"artifactType": { | ||
"type": "APK", | ||
"kind": "Directory" | ||
}, | ||
"applicationId": "se.wmuth.openc25k", | ||
"variantName": "debug", | ||
"elements": [ | ||
{ | ||
"type": "SINGLE", | ||
"filters": [], | ||
"attributes": [], | ||
"versionCode": 1, | ||
"versionName": "1.0", | ||
"outputFile": "app-debug.apk" | ||
} | ||
], | ||
"elementType": "File" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"version": 3, | ||
"artifactType": { | ||
"type": "APK", | ||
"kind": "Directory" | ||
}, | ||
"applicationId": "se.wmuth.openc25k", | ||
"variantName": "release", | ||
"elements": [ | ||
{ | ||
"type": "SINGLE", | ||
"filters": [], | ||
"attributes": [], | ||
"versionCode": 1, | ||
"versionName": "1.0", | ||
"outputFile": "app-release.apk" | ||
} | ||
], | ||
"elementType": "File" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
||
<uses-permission android:name="android.permission.VIBRATE" /> | ||
<application | ||
android:allowBackup="true" | ||
android:dataExtractionRules="@xml/data_extraction_rules" | ||
android:fullBackupContent="@xml/backup_rules" | ||
android:icon="@mipmap/ic_green" | ||
android:label="@string/app_name" | ||
android:roundIcon="@mipmap/ic_green_round" | ||
android:supportsRtl="true" | ||
android:theme="@style/Base.Theme.OpenC25k"> | ||
<activity | ||
android:name=".TrackActivity" | ||
android:exported="false" /> | ||
<activity | ||
android:name=".MainActivity" | ||
android:exported="true"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
</application> | ||
</manifest> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package se.wmuth.openc25k | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import androidx.activity.result.ActivityResultLauncher | ||
import androidx.activity.result.contract.ActivityResultContracts | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.datastore.core.DataStore | ||
import androidx.datastore.preferences.core.Preferences | ||
import androidx.datastore.preferences.preferencesDataStore | ||
import androidx.recyclerview.widget.LinearLayoutManager | ||
import se.wmuth.openc25k.both.Beeper | ||
import se.wmuth.openc25k.data.Run | ||
import se.wmuth.openc25k.databinding.ActivityMainBinding | ||
import se.wmuth.openc25k.main.DataHandler | ||
import se.wmuth.openc25k.main.RunAdapter | ||
import se.wmuth.openc25k.main.SettingsMenu | ||
import se.wmuth.openc25k.main.VolumeDialog | ||
|
||
// Get the datastore for the app | ||
val Context.datastore: DataStore<Preferences> by preferencesDataStore(name = "settings") | ||
|
||
/** | ||
* The main activity, ties together everything on the apps 'home' page | ||
*/ | ||
class MainActivity : AppCompatActivity(), RunAdapter.RunAdapterClickListener, | ||
SettingsMenu.SettingsMenuListener, VolumeDialog.VolumeDialogListener { | ||
private lateinit var menu: SettingsMenu | ||
private lateinit var runs: Array<Run> | ||
private lateinit var volDialog: VolumeDialog | ||
private lateinit var handler: DataHandler | ||
private lateinit var adapter: RunAdapter | ||
private lateinit var launcher: ActivityResultLauncher<Intent> | ||
private var sound: Boolean = true | ||
private var vibrate: Boolean = true | ||
private var volume: Float = 0.5f | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
handler = DataHandler(this, datastore) | ||
sound = handler.getSound() | ||
vibrate = handler.getVibrate() | ||
volume = handler.getVolume() | ||
runs = handler.getRuns() | ||
|
||
super.onCreate(savedInstanceState) | ||
val binding = ActivityMainBinding.inflate(layoutInflater) | ||
setContentView(binding.root) | ||
|
||
volDialog = VolumeDialog(this, this, layoutInflater) | ||
menu = SettingsMenu(this, binding.materialToolbar.menu) | ||
|
||
val runRV = binding.recyclerView | ||
adapter = RunAdapter(this, runs, this) | ||
runRV.adapter = adapter | ||
runRV.layoutManager = LinearLayoutManager(this) | ||
|
||
binding.materialToolbar.setOnMenuItemClickListener(menu) | ||
binding.materialToolbar.menu.findItem(R.id.vibrate).isChecked = vibrate | ||
binding.materialToolbar.menu.findItem(R.id.sound).isChecked = sound | ||
|
||
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { | ||
handleActivityResult(it.resultCode, it.data) | ||
} | ||
} | ||
|
||
override fun onRunItemClick(position: Int) { | ||
// Run was clicked, launch TrackActivity with extras | ||
val intent = Intent(this, TrackActivity::class.java) | ||
intent.putExtra("run", runs[position]) | ||
intent.putExtra("id", position) | ||
intent.putExtra("sound", sound) | ||
intent.putExtra("vibrate", vibrate) | ||
intent.putExtra("volume", volume) | ||
launcher.launch(intent) | ||
} | ||
|
||
override fun onRunItemLongClick(position: Int) { | ||
// Run held, toggle isComplete | ||
runs[position].isComplete = !runs[position].isComplete | ||
adapter.notifyItemChanged(position) | ||
handler.setRuns(runs) | ||
} | ||
|
||
/** | ||
* Handle the result of the TrackActivity | ||
* | ||
* @param resultCode if RESULT_OK, run was finished | ||
* @param data the data sent back from the activity, which run was completed | ||
*/ | ||
private fun handleActivityResult(resultCode: Int, data: Intent?) { | ||
if (resultCode == RESULT_OK && data != null) { | ||
runs[data.getIntExtra("id", 0)].isComplete = true | ||
adapter.notifyItemChanged(data.getIntExtra("id", 0)) | ||
handler.setRuns(runs) | ||
} | ||
} | ||
|
||
override fun createVolumeDialog() { | ||
volDialog.createAlertDialog(volume) | ||
} | ||
|
||
override fun shouldMakeSound(): Boolean { | ||
return sound | ||
} | ||
|
||
override fun shouldVibrate(): Boolean { | ||
return vibrate | ||
} | ||
|
||
override fun testVolume() { | ||
val beeper = Beeper(applicationContext, volume) | ||
if (sound) { | ||
beeper.beep() | ||
} | ||
} | ||
|
||
override fun toggleSound() { | ||
sound = !sound | ||
handler.setSound(sound) | ||
} | ||
|
||
override fun toggleVibration() { | ||
vibrate = !vibrate | ||
handler.setVibrate(vibrate) | ||
} | ||
|
||
override fun setVolume(nV: Float) { | ||
volume = nV | ||
handler.setVolume(volume) | ||
} | ||
} |
Oops, something went wrong.