diff --git a/.DS_Store b/.DS_Store
index ff3856e..d09b377 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/android/.DS_Store b/android/.DS_Store
new file mode 100644
index 0000000..c832e3b
Binary files /dev/null and b/android/.DS_Store differ
diff --git a/android/.gradle/8.5/checksums/checksums.lock b/android/.gradle/8.5/checksums/checksums.lock
index e776488..456b3a5 100644
Binary files a/android/.gradle/8.5/checksums/checksums.lock and b/android/.gradle/8.5/checksums/checksums.lock differ
diff --git a/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock b/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock
index 1737bfd..9e81dd8 100644
Binary files a/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock and b/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock differ
diff --git a/android/.gradle/8.5/executionHistory/executionHistory.bin b/android/.gradle/8.5/executionHistory/executionHistory.bin
index bc72ced..508368f 100644
Binary files a/android/.gradle/8.5/executionHistory/executionHistory.bin and b/android/.gradle/8.5/executionHistory/executionHistory.bin differ
diff --git a/android/.gradle/8.5/executionHistory/executionHistory.lock b/android/.gradle/8.5/executionHistory/executionHistory.lock
index 5c8f309..0f6277c 100644
Binary files a/android/.gradle/8.5/executionHistory/executionHistory.lock and b/android/.gradle/8.5/executionHistory/executionHistory.lock differ
diff --git a/android/.gradle/8.5/fileHashes/fileHashes.bin b/android/.gradle/8.5/fileHashes/fileHashes.bin
index 62b32e1..b5390e3 100644
Binary files a/android/.gradle/8.5/fileHashes/fileHashes.bin and b/android/.gradle/8.5/fileHashes/fileHashes.bin differ
diff --git a/android/.gradle/8.5/fileHashes/fileHashes.lock b/android/.gradle/8.5/fileHashes/fileHashes.lock
index 50a19aa..35955aa 100644
Binary files a/android/.gradle/8.5/fileHashes/fileHashes.lock and b/android/.gradle/8.5/fileHashes/fileHashes.lock differ
diff --git a/android/.gradle/8.5/fileHashes/resourceHashesCache.bin b/android/.gradle/8.5/fileHashes/resourceHashesCache.bin
index 3bf848f..520d066 100644
Binary files a/android/.gradle/8.5/fileHashes/resourceHashesCache.bin and b/android/.gradle/8.5/fileHashes/resourceHashesCache.bin differ
diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
index d301889..aad4f8a 100644
Binary files a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/android/.gradle/buildOutputCleanup/cache.properties b/android/.gradle/buildOutputCleanup/cache.properties
index f3476bd..83f09d4 100644
--- a/android/.gradle/buildOutputCleanup/cache.properties
+++ b/android/.gradle/buildOutputCleanup/cache.properties
@@ -1,2 +1,2 @@
-#Mon Apr 21 08:14:16 CST 2025
+#Fri Apr 25 19:53:19 CST 2025
gradle.version=8.5
diff --git a/android/.gradle/buildOutputCleanup/outputFiles.bin b/android/.gradle/buildOutputCleanup/outputFiles.bin
index dabf4cc..7aa3d7a 100644
Binary files a/android/.gradle/buildOutputCleanup/outputFiles.bin and b/android/.gradle/buildOutputCleanup/outputFiles.bin differ
diff --git a/android/.gradle/config.properties b/android/.gradle/config.properties
index 9f9d1d6..6861a6c 100644
--- a/android/.gradle/config.properties
+++ b/android/.gradle/config.properties
@@ -1,2 +1,2 @@
-#Mon Apr 21 08:12:48 CST 2025
+#Fri Apr 25 19:53:31 CST 2025
java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
diff --git a/android/.gradle/file-system.probe b/android/.gradle/file-system.probe
index d7ed2a9..3821b15 100644
Binary files a/android/.gradle/file-system.probe and b/android/.gradle/file-system.probe differ
diff --git a/android/.idea/android.iml b/android/.idea/android.iml
new file mode 100644
index 0000000..12bbf74
--- /dev/null
+++ b/android/.idea/android.iml
@@ -0,0 +1 @@
+
diff --git a/android/.idea/assetWizardSettings.xml b/android/.idea/assetWizardSettings.xml
new file mode 100644
index 0000000..2a9c5e0
--- /dev/null
+++ b/android/.idea/assetWizardSettings.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/caches/deviceStreaming.xml b/android/.idea/caches/deviceStreaming.xml
deleted file mode 100644
index 9e9ba09..0000000
--- a/android/.idea/caches/deviceStreaming.xml
+++ /dev/null
@@ -1,607 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml
index 74dd639..d15a481 100644
--- a/android/.idea/misc.xml
+++ b/android/.idea/misc.xml
@@ -2,9 +2,6 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/android/.idea/render.experimental.xml b/android/.idea/render.experimental.xml
new file mode 100644
index 0000000..8ec256a
--- /dev/null
+++ b/android/.idea/render.experimental.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/.DS_Store b/android/app/.DS_Store
new file mode 100644
index 0000000..e302732
Binary files /dev/null and b/android/app/.DS_Store differ
diff --git a/android/app/src/.DS_Store b/android/app/src/.DS_Store
new file mode 100644
index 0000000..beeb7cb
Binary files /dev/null and b/android/app/src/.DS_Store differ
diff --git a/android/app/src/main/.DS_Store b/android/app/src/main/.DS_Store
new file mode 100644
index 0000000..8ab4390
Binary files /dev/null and b/android/app/src/main/.DS_Store differ
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 910942c..f802450 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -13,12 +13,23 @@
android:roundIcon="@drawable/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.PoseEstimation">
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/java/.DS_Store b/android/app/src/main/java/.DS_Store
new file mode 100644
index 0000000..7c45a62
Binary files /dev/null and b/android/app/src/main/java/.DS_Store differ
diff --git a/android/app/src/main/java/org/.DS_Store b/android/app/src/main/java/org/.DS_Store
new file mode 100644
index 0000000..c2ccece
Binary files /dev/null and b/android/app/src/main/java/org/.DS_Store differ
diff --git a/android/app/src/main/java/org/tensorflow/.DS_Store b/android/app/src/main/java/org/tensorflow/.DS_Store
new file mode 100644
index 0000000..dadc68c
Binary files /dev/null and b/android/app/src/main/java/org/tensorflow/.DS_Store differ
diff --git a/android/app/src/main/java/org/tensorflow/lite/.DS_Store b/android/app/src/main/java/org/tensorflow/lite/.DS_Store
new file mode 100644
index 0000000..bd8e1d2
Binary files /dev/null and b/android/app/src/main/java/org/tensorflow/lite/.DS_Store differ
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/.DS_Store b/android/app/src/main/java/org/tensorflow/lite/examples/.DS_Store
new file mode 100644
index 0000000..ed82e15
Binary files /dev/null and b/android/app/src/main/java/org/tensorflow/lite/examples/.DS_Store differ
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/AgeSelectionActivity.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/AgeSelectionActivity.kt
new file mode 100644
index 0000000..6dda173
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/AgeSelectionActivity.kt
@@ -0,0 +1,120 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.MotionEvent
+import android.widget.ImageButton
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.button.MaterialButton
+import kotlin.math.abs
+
+class AgeSelectionActivity : AppCompatActivity() {
+ private lateinit var selectedAgeText: TextView
+ private lateinit var age1Above: TextView
+ private lateinit var age2Above: TextView
+ private lateinit var age1Below: TextView
+ private lateinit var age2Below: TextView
+ private lateinit var nextButton: MaterialButton
+ private lateinit var backButton: ImageButton
+ private var selectedGender: String? = null
+
+ private var currentAge = 25
+ private val minAge = 12
+ private val maxAge = 90
+
+ private var lastY: Float = 0f
+ private val scrollSensitivity = 15f // 调整这个值可以改变滑动灵敏度
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_age_selection)
+
+ selectedGender = intent.getStringExtra("selected_gender")
+
+ selectedAgeText = findViewById(R.id.selectedAgeText)
+ age1Above = findViewById(R.id.age1Above)
+ age2Above = findViewById(R.id.age2Above)
+ age1Below = findViewById(R.id.age1Below)
+ age2Below = findViewById(R.id.age2Below)
+ nextButton = findViewById(R.id.nextButton)
+ backButton = findViewById(R.id.backButton)
+
+ setupUI()
+ setupClickListeners()
+ }
+
+ private fun setupUI() {
+ updateAgeDisplay()
+ nextButton.isEnabled = true
+ }
+
+ private fun updateAgeDisplay() {
+ selectedAgeText.text = currentAge.toString()
+
+ // 更新上方年龄(显示较小的数字)
+ if (currentAge - 1 >= minAge) {
+ age1Above.text = (currentAge - 1).toString()
+ } else {
+ age1Above.text = ""
+ }
+ if (currentAge - 2 >= minAge) {
+ age2Above.text = (currentAge - 2).toString()
+ } else {
+ age2Above.text = ""
+ }
+
+ // 更新下方年龄(显示较大的数字)
+ if (currentAge + 1 <= maxAge) {
+ age1Below.text = (currentAge + 1).toString()
+ } else {
+ age1Below.text = ""
+ }
+ if (currentAge + 2 <= maxAge) {
+ age2Below.text = (currentAge + 2).toString()
+ } else {
+ age2Below.text = ""
+ }
+ }
+
+ private fun setupClickListeners() {
+ nextButton.setOnClickListener {
+ val intent = Intent(this, WeightSelectionActivity::class.java)
+ intent.putExtra("selected_gender", selectedGender)
+ intent.putExtra("selected_age", currentAge)
+ startActivity(intent)
+ finish()
+ }
+
+ backButton.setOnClickListener {
+ finish()
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ lastY = event.y
+ return true
+ }
+ MotionEvent.ACTION_MOVE -> {
+ val currentY = event.y
+ val deltaY = currentY - lastY
+
+ // 计算应该改变多少年龄
+ val change = -(deltaY / scrollSensitivity).toInt()
+ if (abs(change) > 0) {
+ // 更新年龄
+ val newAge = (currentAge + change).coerceIn(minAge, maxAge)
+ if (newAge != currentAge) {
+ currentAge = newAge
+ updateAgeDisplay()
+ lastY = currentY
+ }
+ }
+ return true
+ }
+ }
+ return super.onTouchEvent(event)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/CustomWeightPicker.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/CustomWeightPicker.kt
new file mode 100644
index 0000000..67fa700
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/CustomWeightPicker.kt
@@ -0,0 +1,105 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Path
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import androidx.core.content.ContextCompat
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
+
+class CustomWeightPicker @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+
+ private val paint = Paint().apply {
+ isAntiAlias = true
+ color = ContextCompat.getColor(context, R.color.purple_500)
+ }
+
+ private val path = Path()
+ private var lastX = 0f
+ private var scrollOffset = 0f
+ private var selectedWeight = 54
+ private val minWeight = 30
+ private val maxWeight = 200
+ private val scaleWidth = 40f // 每个刻度的宽度
+ private val scaleHeight = 20f // 刻度的高度
+ private val centerLineWidth = 2f
+
+ private var onWeightChangeListener: ((Int) -> Unit)? = null
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ val centerX = width / 2f
+ val centerY = height / 2f
+
+ // 绘制刻度
+ val startX = centerX + scrollOffset
+ val startWeight = selectedWeight - (centerX / scaleWidth).toInt()
+
+ for (i in -10..10) {
+ val x = startX + i * scaleWidth
+ val weight = startWeight + i
+
+ if (weight in minWeight..maxWeight) {
+ // 绘制刻度线
+ canvas.drawLine(
+ x,
+ centerY - scaleHeight,
+ x,
+ centerY + scaleHeight,
+ paint
+ )
+
+ // 绘制刻度值
+ if (i % 2 == 0) {
+ canvas.drawText(
+ weight.toString(),
+ x - 10f,
+ centerY + scaleHeight + 20f,
+ paint
+ )
+ }
+ }
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ lastX = event.x
+ return true
+ }
+ MotionEvent.ACTION_MOVE -> {
+ val deltaX = event.x - lastX
+ scrollOffset += deltaX
+
+ // 计算新的体重值
+ val newWeight = selectedWeight - (deltaX / scaleWidth).toInt()
+ if (newWeight in minWeight..maxWeight) {
+ selectedWeight = newWeight
+ onWeightChangeListener?.invoke(selectedWeight)
+ }
+
+ lastX = event.x
+ invalidate()
+ return true
+ }
+ }
+ return super.onTouchEvent(event)
+ }
+
+ fun setOnWeightChangeListener(listener: (Int) -> Unit) {
+ onWeightChangeListener = listener
+ }
+
+ fun getSelectedWeight(): Int = selectedWeight
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/GenderSelectionActivity.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/GenderSelectionActivity.kt
new file mode 100644
index 0000000..202b14e
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/GenderSelectionActivity.kt
@@ -0,0 +1,70 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Intent
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.cardview.widget.CardView
+import com.google.android.material.button.MaterialButton
+
+class GenderSelectionActivity : AppCompatActivity() {
+ private lateinit var maleButton: CardView
+ private lateinit var femaleButton: CardView
+ private lateinit var maleText: TextView
+ private lateinit var femaleText: TextView
+ private lateinit var nextButton: MaterialButton
+ private var selectedGender: String? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_gender_selection)
+
+ maleButton = findViewById(R.id.maleButton)
+ femaleButton = findViewById(R.id.femaleButton)
+ maleText = findViewById(R.id.maleText)
+ femaleText = findViewById(R.id.femaleText)
+ nextButton = findViewById(R.id.nextButton)
+
+ setupClickListeners()
+ }
+
+ private fun setupClickListeners() {
+ maleButton.setOnClickListener {
+ updateSelection("male")
+ }
+
+ femaleButton.setOnClickListener {
+ updateSelection("female")
+ }
+
+ nextButton.setOnClickListener {
+ // 跳转到年龄选择页面,并传递性别信息
+ val intent = Intent(this, AgeSelectionActivity::class.java)
+ intent.putExtra("selected_gender", selectedGender)
+ startActivity(intent)
+ }
+ }
+
+ private fun updateSelection(gender: String) {
+ selectedGender = gender
+
+ // 更新UI状态
+ when (gender) {
+ "male" -> {
+ maleButton.setCardBackgroundColor(getColor(android.R.color.holo_purple))
+ femaleButton.setCardBackgroundColor(getColor(android.R.color.darker_gray))
+ maleText.setTextColor(getColor(android.R.color.holo_purple))
+ femaleText.setTextColor(getColor(android.R.color.white))
+ }
+ "female" -> {
+ femaleButton.setCardBackgroundColor(getColor(android.R.color.holo_purple))
+ maleButton.setCardBackgroundColor(getColor(android.R.color.darker_gray))
+ femaleText.setTextColor(getColor(android.R.color.holo_purple))
+ maleText.setTextColor(getColor(android.R.color.white))
+ }
+ }
+
+ // 启用Next按钮
+ nextButton.isEnabled = true
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/HeightSelectionActivity.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/HeightSelectionActivity.kt
new file mode 100644
index 0000000..84f0974
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/HeightSelectionActivity.kt
@@ -0,0 +1,129 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.MotionEvent
+import android.widget.ImageButton
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.button.MaterialButton
+import kotlin.math.abs
+
+class HeightSelectionActivity : AppCompatActivity() {
+ private lateinit var selectedHeightText: TextView
+ private lateinit var heightUnit: TextView
+ private lateinit var height1Above: TextView
+ private lateinit var height2Above: TextView
+ private lateinit var height1Below: TextView
+ private lateinit var height2Below: TextView
+ private lateinit var nextButton: MaterialButton
+ private lateinit var backButton: ImageButton
+ private var selectedGender: String? = null
+ private var selectedAge: Int = 0
+ private var selectedWeight: Int = 0
+
+ private var currentHeight = 167
+ private val minHeight = 100
+ private val maxHeight = 220
+
+ private var lastY: Float = 0f
+ private val scrollSensitivity = 15f // 调整这个值可以改变滑动灵敏度
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_height_selection)
+
+ selectedGender = intent.getStringExtra("selected_gender")
+ selectedAge = intent.getIntExtra("selected_age", 0)
+ selectedWeight = intent.getIntExtra("selected_weight", 0)
+
+ selectedHeightText = findViewById(R.id.selectedHeightText)
+ heightUnit = findViewById(R.id.heightUnit)
+ height1Above = findViewById(R.id.height1Above)
+ height2Above = findViewById(R.id.height2Above)
+ height1Below = findViewById(R.id.height1Below)
+ height2Below = findViewById(R.id.height2Below)
+ nextButton = findViewById(R.id.nextButton)
+ backButton = findViewById(R.id.backButton)
+
+ setupUI()
+ setupClickListeners()
+ }
+
+ private fun setupUI() {
+ updateHeightDisplay()
+ nextButton.isEnabled = true
+ }
+
+ private fun updateHeightDisplay() {
+ selectedHeightText.text = currentHeight.toString()
+ // 上方身高
+ if (currentHeight - 1 >= minHeight) {
+ height1Above.text = (currentHeight - 1).toString()
+ } else {
+ height1Above.text = ""
+ }
+ if (currentHeight - 2 >= minHeight) {
+ height2Above.text = (currentHeight - 2).toString()
+ } else {
+ height2Above.text = ""
+ }
+ // 下方身高
+ if (currentHeight + 1 <= maxHeight) {
+ height1Below.text = (currentHeight + 1).toString()
+ } else {
+ height1Below.text = ""
+ }
+ if (currentHeight + 2 <= maxHeight) {
+ height2Below.text = (currentHeight + 2).toString()
+ } else {
+ height2Below.text = ""
+ }
+ }
+
+ private fun setupClickListeners() {
+ nextButton.setOnClickListener {
+ val intent = Intent(this, MainActivity::class.java)
+ intent.putExtra("selected_gender", selectedGender)
+ intent.putExtra("selected_age", selectedAge)
+ intent.putExtra("selected_weight", selectedWeight)
+ intent.putExtra("selected_height", currentHeight)
+ startActivity(intent)
+ finish()
+ }
+
+ backButton.setOnClickListener {
+ val intent = Intent(this, WeightSelectionActivity::class.java)
+ intent.putExtra("selected_gender", selectedGender)
+ intent.putExtra("selected_age", selectedAge)
+ intent.putExtra("selected_weight", selectedWeight)
+ startActivity(intent)
+ finish()
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ lastY = event.y
+ return true
+ }
+ MotionEvent.ACTION_MOVE -> {
+ val currentY = event.y
+ val deltaY = currentY - lastY
+ val change = -(deltaY / scrollSensitivity).toInt()
+ if (abs(change) > 0) {
+ val newHeight = (currentHeight + change).coerceIn(minHeight, maxHeight)
+ if (newHeight != currentHeight) {
+ currentHeight = newHeight
+ updateHeightDisplay()
+ lastY = currentY
+ }
+ }
+ return true
+ }
+ }
+ return super.onTouchEvent(event)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding1Fragment.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding1Fragment.kt
new file mode 100644
index 0000000..e4e910b
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding1Fragment.kt
@@ -0,0 +1,16 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+
+class Onboarding1Fragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.activity_onboarding1, container, false)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding2Fragment.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding2Fragment.kt
new file mode 100644
index 0000000..4157c36
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding2Fragment.kt
@@ -0,0 +1,16 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+
+class Onboarding2Fragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.activity_onboarding2, container, false)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding3Fragment.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding3Fragment.kt
new file mode 100644
index 0000000..3ad9896
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/Onboarding3Fragment.kt
@@ -0,0 +1,29 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.fragment.app.Fragment
+
+class Onboarding3Fragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val view = inflater.inflate(R.layout.activity_onboarding3, container, false)
+
+ // 找到Start now按钮并设置点击事件
+ val startButton = view.findViewById(R.id.small_butto_container)
+ startButton.setOnClickListener {
+ // 跳转到性别选择页面
+ val intent = Intent(requireActivity(), GenderSelectionActivity::class.java)
+ startActivity(intent)
+ requireActivity().finish() // 结束当前的OnboardingActivity
+ }
+
+ return view
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/OnboardingActivity.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/OnboardingActivity.kt
new file mode 100644
index 0000000..aa1a487
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/OnboardingActivity.kt
@@ -0,0 +1,15 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.viewpager2.widget.ViewPager2
+
+class OnboardingActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_onboarding)
+
+ val viewPager = findViewById(R.id.viewPager)
+ viewPager.adapter = OnboardingAdapter(this)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/OnboardingAdapter.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/OnboardingAdapter.kt
new file mode 100644
index 0000000..7c91147
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/OnboardingAdapter.kt
@@ -0,0 +1,18 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.adapter.FragmentStateAdapter
+
+class OnboardingAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
+ override fun getItemCount(): Int = 3
+
+ override fun createFragment(position: Int): Fragment {
+ return when (position) {
+ 0 -> Onboarding1Fragment()
+ 1 -> Onboarding2Fragment()
+ 2 -> Onboarding3Fragment()
+ else -> Onboarding1Fragment()
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/SplashActivity.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/SplashActivity.kt
new file mode 100644
index 0000000..546417c
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/SplashActivity.kt
@@ -0,0 +1,50 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Intent
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import android.widget.RelativeLayout
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+
+class SplashActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_splash)
+
+ val textView = findViewById(R.id.some_id)
+ val vector1 = findViewById(R.id.vector1)
+ val vector2 = findViewById(R.id.vector2)
+
+ // 获取屏幕高度
+ val screenHeight = resources.displayMetrics.heightPixels
+ val margin = screenHeight / 8
+
+ textView.post {
+ val textViewLocation = IntArray(2)
+ textView.getLocationOnScreen(textViewLocation)
+ val textViewTop = textViewLocation[1]
+ val textViewBottom = textViewTop + textView.height
+
+ // 设置vector1在TextView上方,右对齐
+ val params1 = vector1.layoutParams as RelativeLayout.LayoutParams
+ params1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
+ params1.topMargin = textViewTop - margin - vector1.layoutParams.height
+ vector1.layoutParams = params1
+
+ // 设置vector2在TextView下方,左对齐
+ val params2 = vector2.layoutParams as RelativeLayout.LayoutParams
+ params2.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
+ params2.topMargin = textViewBottom + margin
+ vector2.layoutParams = params2
+ }
+
+ // 2秒后跳转到引导页
+ Handler(Looper.getMainLooper()).postDelayed({
+ startActivity(Intent(this, OnboardingActivity::class.java))
+ finish()
+ }, 2000)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/WeightSelectionActivity.kt b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/WeightSelectionActivity.kt
new file mode 100644
index 0000000..1384a9b
--- /dev/null
+++ b/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/WeightSelectionActivity.kt
@@ -0,0 +1,128 @@
+package org.tensorflow.lite.examples.poseestimation
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.MotionEvent
+import android.widget.ImageButton
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.button.MaterialButton
+import kotlin.math.abs
+
+class WeightSelectionActivity : AppCompatActivity() {
+ private lateinit var selectedWeightText: TextView
+ private lateinit var weight1Above: TextView
+ private lateinit var weight2Above: TextView
+ private lateinit var weight1Below: TextView
+ private lateinit var weight2Below: TextView
+ private lateinit var nextButton: MaterialButton
+ private lateinit var backButton: ImageButton
+
+ private var selectedGender: String? = null
+ private var selectedAge: Int = 0
+ private var currentWeight = 54
+
+ private var lastY: Float = 0f
+ private val scrollSensitivity = 15f // 调整这个值可以改变滑动灵敏度
+ private val minWeight = 30
+ private val maxWeight = 200
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_weight_selection)
+
+ // 获取从上一个页面传递的数据
+ selectedGender = intent.getStringExtra("selected_gender")
+ selectedAge = intent.getIntExtra("selected_age", 0)
+
+ // 初始化视图
+ selectedWeightText = findViewById(R.id.selectedWeightText)
+ weight1Above = findViewById(R.id.weight1Above)
+ weight2Above = findViewById(R.id.weight2Above)
+ weight1Below = findViewById(R.id.weight1Below)
+ weight2Below = findViewById(R.id.weight2Below)
+ nextButton = findViewById(R.id.nextButton)
+ backButton = findViewById(R.id.backButton)
+
+ setupUI()
+ setupClickListeners()
+ }
+
+ private fun setupUI() {
+ updateWeightDisplay()
+ nextButton.isEnabled = true
+ }
+
+ private fun updateWeightDisplay() {
+ selectedWeightText.text = currentWeight.toString()
+
+ // 更新上方体重(显示较小的数字)
+ if (currentWeight - 1 >= minWeight) {
+ weight1Above.text = (currentWeight - 1).toString()
+ } else {
+ weight1Above.text = ""
+ }
+ if (currentWeight - 2 >= minWeight) {
+ weight2Above.text = (currentWeight - 2).toString()
+ } else {
+ weight2Above.text = ""
+ }
+
+ // 更新下方体重(显示较大的数字)
+ if (currentWeight + 1 <= maxWeight) {
+ weight1Below.text = (currentWeight + 1).toString()
+ } else {
+ weight1Below.text = ""
+ }
+ if (currentWeight + 2 <= maxWeight) {
+ weight2Below.text = (currentWeight + 2).toString()
+ } else {
+ weight2Below.text = ""
+ }
+ }
+
+ private fun setupClickListeners() {
+ nextButton.setOnClickListener {
+ val intent = Intent(this, HeightSelectionActivity::class.java)
+ intent.putExtra("selected_gender", selectedGender)
+ intent.putExtra("selected_age", selectedAge)
+ intent.putExtra("selected_weight", currentWeight)
+ startActivity(intent)
+ finish()
+ }
+
+ backButton.setOnClickListener {
+ val intent = Intent(this, AgeSelectionActivity::class.java)
+ intent.putExtra("selected_gender", selectedGender)
+ startActivity(intent)
+ finish()
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ lastY = event.y
+ return true
+ }
+ MotionEvent.ACTION_MOVE -> {
+ val currentY = event.y
+ val deltaY = currentY - lastY
+
+ // 计算应该改变多少体重
+ val change = -(deltaY / scrollSensitivity).toInt()
+ if (abs(change) > 0) {
+ // 更新体重
+ val newWeight = (currentWeight + change).coerceIn(minWeight, maxWeight)
+ if (newWeight != currentWeight) {
+ currentWeight = newWeight
+ updateWeightDisplay()
+ lastY = currentY
+ }
+ }
+ return true
+ }
+ }
+ return super.onTouchEvent(event)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/circle_button_background.xml b/android/app/src/main/res/drawable/circle_button_background.xml
new file mode 100644
index 0000000..70ad4dd
--- /dev/null
+++ b/android/app/src/main/res/drawable/circle_button_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/female.png b/android/app/src/main/res/drawable/female.png
new file mode 100644
index 0000000..c32634a
Binary files /dev/null and b/android/app/src/main/res/drawable/female.png differ
diff --git a/android/app/src/main/res/drawable/ic_back.xml b/android/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 0000000..791df00
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/indicator1.png b/android/app/src/main/res/drawable/indicator1.png
new file mode 100644
index 0000000..6dd2dab
Binary files /dev/null and b/android/app/src/main/res/drawable/indicator1.png differ
diff --git a/android/app/src/main/res/drawable/indicator2.png b/android/app/src/main/res/drawable/indicator2.png
new file mode 100644
index 0000000..8f9e328
Binary files /dev/null and b/android/app/src/main/res/drawable/indicator2.png differ
diff --git a/android/app/src/main/res/drawable/indicator3.png b/android/app/src/main/res/drawable/indicator3.png
new file mode 100644
index 0000000..94fdbb2
Binary files /dev/null and b/android/app/src/main/res/drawable/indicator3.png differ
diff --git a/android/app/src/main/res/drawable/male.png b/android/app/src/main/res/drawable/male.png
new file mode 100644
index 0000000..1ac479c
Binary files /dev/null and b/android/app/src/main/res/drawable/male.png differ
diff --git a/android/app/src/main/res/drawable/onboarding1_man.png b/android/app/src/main/res/drawable/onboarding1_man.png
new file mode 100644
index 0000000..1b661d4
Binary files /dev/null and b/android/app/src/main/res/drawable/onboarding1_man.png differ
diff --git a/android/app/src/main/res/drawable/onboarding2_woman.png b/android/app/src/main/res/drawable/onboarding2_woman.png
new file mode 100644
index 0000000..0865aad
Binary files /dev/null and b/android/app/src/main/res/drawable/onboarding2_woman.png differ
diff --git a/android/app/src/main/res/drawable/onboarding3_man.png b/android/app/src/main/res/drawable/onboarding3_man.png
new file mode 100644
index 0000000..58103e6
Binary files /dev/null and b/android/app/src/main/res/drawable/onboarding3_man.png differ
diff --git a/android/app/src/main/res/drawable/small_butto.xml b/android/app/src/main/res/drawable/small_butto.xml
new file mode 100644
index 0000000..3bcd78e
--- /dev/null
+++ b/android/app/src/main/res/drawable/small_butto.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/vector.xml b/android/app/src/main/res/drawable/vector.xml
new file mode 100644
index 0000000..69f30de
--- /dev/null
+++ b/android/app/src/main/res/drawable/vector.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/font/.placeholder b/android/app/src/main/res/font/.placeholder
new file mode 100644
index 0000000..bbc10f8
--- /dev/null
+++ b/android/app/src/main/res/font/.placeholder
@@ -0,0 +1 @@
+// 该文件仅用于占位,实际字体文件请放在本目录下。
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_age_selection.xml b/android/app/src/main/res/layout/activity_age_selection.xml
new file mode 100644
index 0000000..38f37ef
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_age_selection.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_gender_selection.xml b/android/app/src/main/res/layout/activity_gender_selection.xml
new file mode 100644
index 0000000..0e26131
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_gender_selection.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_height_selection.xml b/android/app/src/main/res/layout/activity_height_selection.xml
new file mode 100644
index 0000000..6f2700c
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_height_selection.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_onboarding.xml b/android/app/src/main/res/layout/activity_onboarding.xml
new file mode 100644
index 0000000..75e823b
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_onboarding.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_onboarding1.xml b/android/app/src/main/res/layout/activity_onboarding1.xml
new file mode 100644
index 0000000..9126c96
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_onboarding1.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/activity_onboarding2.xml b/android/app/src/main/res/layout/activity_onboarding2.xml
new file mode 100644
index 0000000..8784432
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_onboarding2.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/activity_onboarding3.xml b/android/app/src/main/res/layout/activity_onboarding3.xml
new file mode 100644
index 0000000..62346ee
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_onboarding3.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/activity_splash.xml b/android/app/src/main/res/layout/activity_splash.xml
new file mode 100644
index 0000000..b7f3476
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_splash.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/activity_weight_selection.xml b/android/app/src/main/res/layout/activity_weight_selection.xml
new file mode 100644
index 0000000..de0443a
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_weight_selection.xml
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
index 648cfde..cfa4818 100644
--- a/android/app/src/main/res/values/colors.xml
+++ b/android/app/src/main/res/values/colors.xml
@@ -6,4 +6,6 @@
#FF018786#FF000000#FFFFFFFF
+ #757575
+ #E0E0E0
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 79bfc21..e0e323a 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,4 +1,5 @@
+
TFL Pose EstimationThis app needs camera permission.Score: %.2f
@@ -27,4 +28,12 @@
BoundingBoxKeypoint
+
+
+ 形动力
+ Meet your coach,\nstart your journey
+ Create a workout plan\nto stay fit
+ Action is the\nkey to all success
+ Start Now
+ Next
diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml
index 0414b39..29c4a51 100644
--- a/android/app/src/main/res/values/themes.xml
+++ b/android/app/src/main/res/values/themes.xml
@@ -1,5 +1,5 @@
-
+
+
diff --git a/android/local.properties b/android/local.properties
index 5112cf9..bc2a713 100644
--- a/android/local.properties
+++ b/android/local.properties
@@ -4,5 +4,5 @@
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
-#Mon Apr 21 08:12:48 CST 2025
+#Fri Apr 25 19:53:17 CST 2025
sdk.dir=/Users/ziyue/Library/Android/sdk
diff --git a/android1/.gradle/8.5/checksums/checksums.lock b/android1/.gradle/8.5/checksums/checksums.lock
new file mode 100644
index 0000000..5edef46
Binary files /dev/null and b/android1/.gradle/8.5/checksums/checksums.lock differ
diff --git a/android/.gradle/8.5/checksums/md5-checksums.bin b/android1/.gradle/8.5/checksums/md5-checksums.bin
similarity index 85%
rename from android/.gradle/8.5/checksums/md5-checksums.bin
rename to android1/.gradle/8.5/checksums/md5-checksums.bin
index 8c46e40..b64501d 100644
Binary files a/android/.gradle/8.5/checksums/md5-checksums.bin and b/android1/.gradle/8.5/checksums/md5-checksums.bin differ
diff --git a/android/.gradle/8.5/checksums/sha1-checksums.bin b/android1/.gradle/8.5/checksums/sha1-checksums.bin
similarity index 94%
rename from android/.gradle/8.5/checksums/sha1-checksums.bin
rename to android1/.gradle/8.5/checksums/sha1-checksums.bin
index 26e9184..59cbbbb 100644
Binary files a/android/.gradle/8.5/checksums/sha1-checksums.bin and b/android1/.gradle/8.5/checksums/sha1-checksums.bin differ
diff --git a/android1/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock b/android1/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock
new file mode 100644
index 0000000..1737bfd
Binary files /dev/null and b/android1/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock differ
diff --git a/android1/.gradle/8.5/dependencies-accessors/gc.properties b/android1/.gradle/8.5/dependencies-accessors/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/android1/.gradle/8.5/executionHistory/executionHistory.bin b/android1/.gradle/8.5/executionHistory/executionHistory.bin
new file mode 100644
index 0000000..5ea72cf
Binary files /dev/null and b/android1/.gradle/8.5/executionHistory/executionHistory.bin differ
diff --git a/android1/.gradle/8.5/executionHistory/executionHistory.lock b/android1/.gradle/8.5/executionHistory/executionHistory.lock
new file mode 100644
index 0000000..7abdfdb
Binary files /dev/null and b/android1/.gradle/8.5/executionHistory/executionHistory.lock differ
diff --git a/android1/.gradle/8.5/fileChanges/last-build.bin b/android1/.gradle/8.5/fileChanges/last-build.bin
new file mode 100644
index 0000000..f76dd23
Binary files /dev/null and b/android1/.gradle/8.5/fileChanges/last-build.bin differ
diff --git a/android1/.gradle/8.5/fileHashes/fileHashes.bin b/android1/.gradle/8.5/fileHashes/fileHashes.bin
new file mode 100644
index 0000000..cf82da6
Binary files /dev/null and b/android1/.gradle/8.5/fileHashes/fileHashes.bin differ
diff --git a/android1/.gradle/8.5/fileHashes/fileHashes.lock b/android1/.gradle/8.5/fileHashes/fileHashes.lock
new file mode 100644
index 0000000..0459c1e
Binary files /dev/null and b/android1/.gradle/8.5/fileHashes/fileHashes.lock differ
diff --git a/android1/.gradle/8.5/fileHashes/resourceHashesCache.bin b/android1/.gradle/8.5/fileHashes/resourceHashesCache.bin
new file mode 100644
index 0000000..3bf848f
Binary files /dev/null and b/android1/.gradle/8.5/fileHashes/resourceHashesCache.bin differ
diff --git a/android1/.gradle/8.5/gc.properties b/android1/.gradle/8.5/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/android1/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android1/.gradle/buildOutputCleanup/buildOutputCleanup.lock
new file mode 100644
index 0000000..e5679c3
Binary files /dev/null and b/android1/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/android1/.gradle/buildOutputCleanup/cache.properties b/android1/.gradle/buildOutputCleanup/cache.properties
new file mode 100644
index 0000000..f3476bd
--- /dev/null
+++ b/android1/.gradle/buildOutputCleanup/cache.properties
@@ -0,0 +1,2 @@
+#Mon Apr 21 08:14:16 CST 2025
+gradle.version=8.5
diff --git a/android1/.gradle/buildOutputCleanup/outputFiles.bin b/android1/.gradle/buildOutputCleanup/outputFiles.bin
new file mode 100644
index 0000000..f1bac22
Binary files /dev/null and b/android1/.gradle/buildOutputCleanup/outputFiles.bin differ
diff --git a/android1/.gradle/config.properties b/android1/.gradle/config.properties
new file mode 100644
index 0000000..9f9d1d6
--- /dev/null
+++ b/android1/.gradle/config.properties
@@ -0,0 +1,2 @@
+#Mon Apr 21 08:12:48 CST 2025
+java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
diff --git a/android1/.gradle/file-system.probe b/android1/.gradle/file-system.probe
new file mode 100644
index 0000000..06ff6fa
Binary files /dev/null and b/android1/.gradle/file-system.probe differ
diff --git a/android1/.gradle/vcs-1/gc.properties b/android1/.gradle/vcs-1/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/android1/.idea/.gitignore b/android1/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/android1/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android1/.idea/.name b/android1/.idea/.name
new file mode 100644
index 0000000..1c0e605
--- /dev/null
+++ b/android1/.idea/.name
@@ -0,0 +1 @@
+TFLite Pose Estimation
\ No newline at end of file
diff --git a/android1/.idea/AndroidProjectSystem.xml b/android1/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/android1/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/compiler.xml b/android1/.idea/compiler.xml
new file mode 100644
index 0000000..b86273d
--- /dev/null
+++ b/android1/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/deploymentTargetSelector.xml b/android1/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..b268ef3
--- /dev/null
+++ b/android1/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/kotlinc.xml b/android1/.idea/kotlinc.xml
new file mode 100644
index 0000000..ae3f30a
--- /dev/null
+++ b/android1/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/migrations.xml b/android1/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/android1/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/misc.xml b/android1/.idea/misc.xml
new file mode 100644
index 0000000..b2c751a
--- /dev/null
+++ b/android1/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/runConfigurations.xml b/android1/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/android1/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/.idea/vcs.xml b/android1/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/android1/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android1/README.md b/android1/README.md
new file mode 100644
index 0000000..2293f99
--- /dev/null
+++ b/android1/README.md
@@ -0,0 +1,73 @@
+# TensorFlow Lite Pose Estimation Android Demo
+
+### Overview
+This is an app that continuously detects the body parts in the frames seen by
+your device's camera. These instructions walk you through building and running
+the demo on an Android device. Camera captures are discarded immediately after
+use, nothing is stored or saved.
+
+The app demonstrates how to use 4 models:
+
+* Single pose models: The model can estimate the pose of only one person in the
+input image. If the input image contains multiple persons, the detection result
+can be largely incorrect.
+ * PoseNet
+ * MoveNet Lightning
+ * MoveNet Thunder
+* Multi pose models: The model can estimate pose of multiple persons in the
+input image.
+ * MoveNet MultiPose: Support up to 6 persons.
+
+See this [blog post](https://blog.tensorflow.org/2021/05/next-generation-pose-detection-with-movenet-and-tensorflowjs.html)
+for a comparison between these models.
+
+
+
+## Build the demo using Android Studio
+
+### Prerequisites
+
+* If you don't have it already, install **[Android Studio](
+ https://developer.android.com/studio/index.html)** 4.2 or
+ above, following the instructions on the website.
+
+* Android device and Android development environment with minimum API 21.
+
+### Building
+* Open Android Studio, and from the `Welcome` screen, select
+`Open an existing Android Studio project`.
+
+* From the `Open File or Project` window that appears, navigate to and select
+ the `lite/examples/pose_estimation/android` directory from wherever you
+ cloned the `tensorflow/examples` GitHub repo. Click `OK`.
+
+* If it asks you to do a `Gradle Sync`, click `OK`.
+
+* You may also need to install various platforms and tools, if you get errors
+ like `Failed to find target with hash string 'android-21'` and similar. Click
+ the `Run` button (the green arrow) or select `Run` > `Run 'android'` from the
+ top menu. You may need to rebuild the project using `Build` > `Rebuild Project`.
+
+* If it asks you to use `Instant Run`, click `Proceed Without Instant Run`.
+
+* Also, you need to have an Android device plugged in with developer options
+ enabled at this point. See **[here](
+ https://developer.android.com/studio/run/device)** for more details
+ on setting up developer devices.
+
+
+### Model used
+Downloading, extraction and placement in assets folder has been managed
+ automatically by `download.gradle`.
+
+If you explicitly want to download the model, you can download it from here:
+
+* [Posenet](https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite)
+* [Movenet Lightning](https://kaggle.com/models/google/movenet/frameworks/tfLite/variations/singlepose-lightning)
+* [Movenet Thunder](https://www.kaggle.com/models/google/movenet/frameworks/tfLite/variations/singlepose-thunder)
+* [Movenet MultiPose](https://www.kaggle.com/models/google/movenet/frameworks/tfLite/variations/multipose-lightning-tflite-float16)
+
+### Additional Note
+_Please do not delete the assets folder content_. If you explicitly deleted the
+ files, then please choose `Build` > `Rebuild` from menu to re-download the
+ deleted model files into assets folder.
diff --git a/android1/app/.gitignore b/android1/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/android1/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android1/app/build.gradle b/android1/app/build.gradle
new file mode 100644
index 0000000..d1545ae
--- /dev/null
+++ b/android1/app/build.gradle
@@ -0,0 +1,56 @@
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "org.tensorflow.lite.examples.poseestimation"
+ minSdkVersion 23
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ namespace "org.tensorflow.lite.examples.poseestimation"
+
+ buildTypes {
+ release {
+ minifyEnabled 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'
+ }
+}
+
+// Download tflite model
+apply from:"download.gradle"
+
+dependencies {
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.5.0'
+ implementation 'androidx.appcompat:appcompat:1.3.0'
+ implementation 'com.google.android.material:material:1.3.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation "androidx.activity:activity-ktx:1.2.3"
+ implementation 'androidx.fragment:fragment-ktx:1.3.5'
+ implementation 'org.tensorflow:tensorflow-lite:2.14.0'
+ implementation 'org.tensorflow:tensorflow-lite-gpu:2.5.0'
+ implementation 'org.tensorflow:tensorflow-lite-support:0.3.0'
+
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ androidTestImplementation "com.google.truth:truth:1.1.3"
+}
diff --git a/android1/app/download.gradle b/android1/app/download.gradle
new file mode 100644
index 0000000..423344d
--- /dev/null
+++ b/android1/app/download.gradle
@@ -0,0 +1,67 @@
+task downloadPosenetModel(type: DownloadUrlTask) {
+ def modelPosenetDownloadUrl = "https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite"
+ doFirst {
+ println "Downloading ${modelPosenetDownloadUrl}"
+ }
+ sourceUrl = "${modelPosenetDownloadUrl}"
+ target = file("src/main/assets/posenet.tflite")
+}
+
+task downloadMovenetLightningModel(type: DownloadUrlTask) {
+ def modelMovenetLightningDownloadUrl = "https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/float16/4?lite-format=tflite"
+ doFirst {
+ println "Downloading ${modelMovenetLightningDownloadUrl}"
+ }
+ sourceUrl = "${modelMovenetLightningDownloadUrl}"
+ target = file("src/main/assets/movenet_lightning.tflite")
+}
+
+task downloadMovenetThunderModel(type: DownloadUrlTask) {
+ def modelMovenetThunderDownloadUrl = "https://tfhub.dev/google/lite-model/movenet/singlepose/thunder/tflite/float16/4?lite-format=tflite"
+ doFirst {
+ println "Downloading ${modelMovenetThunderDownloadUrl}"
+ }
+ sourceUrl = "${modelMovenetThunderDownloadUrl}"
+ target = file("src/main/assets/movenet_thunder.tflite")
+}
+
+task downloadMovenetMultiPoseModel(type: DownloadUrlTask) {
+ def modelMovenetThunderDownloadUrl = "https://tfhub.dev/google/lite-model/movenet/multipose/lightning/tflite/float16/1?lite-format=tflite"
+ doFirst {
+ println "Downloading ${modelMovenetThunderDownloadUrl}"
+ }
+ sourceUrl = "${modelMovenetThunderDownloadUrl}"
+ target = file("src/main/assets/movenet_multipose_fp16.tflite")
+}
+
+task downloadPoseClassifierModel(type: DownloadUrlTask) {
+ def modelPoseClassifierDownloadUrl = "https://storage.googleapis.com/download.tensorflow.org/models/tflite/pose_classifier/yoga_classifier.tflite"
+ doFirst {
+ println "Downloading ${modelPoseClassifierDownloadUrl}"
+ }
+ sourceUrl = "${modelPoseClassifierDownloadUrl}"
+ target = file("src/main/assets/classifier.tflite")
+}
+
+task downloadModel {
+ dependsOn downloadPosenetModel
+ dependsOn downloadMovenetLightningModel
+ dependsOn downloadMovenetThunderModel
+ dependsOn downloadPoseClassifierModel
+ dependsOn downloadMovenetMultiPoseModel
+}
+
+class DownloadUrlTask extends DefaultTask {
+ @Input
+ String sourceUrl
+
+ @OutputFile
+ File target
+
+ @TaskAction
+ void download() {
+ ant.get(src: sourceUrl, dest: target)
+ }
+}
+
+preBuild.dependsOn downloadModel
diff --git a/android1/app/proguard-rules.pro b/android1/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/android1/app/proguard-rules.pro
@@ -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
diff --git a/android1/app/src/androidTest/assets/image1.png b/android1/app/src/androidTest/assets/image1.png
new file mode 100644
index 0000000..4085b3d
Binary files /dev/null and b/android1/app/src/androidTest/assets/image1.png differ
diff --git a/android1/app/src/androidTest/assets/image2.jpg b/android1/app/src/androidTest/assets/image2.jpg
new file mode 100644
index 0000000..8db9892
Binary files /dev/null and b/android1/app/src/androidTest/assets/image2.jpg differ
diff --git a/android1/app/src/androidTest/assets/image3.jpeg b/android1/app/src/androidTest/assets/image3.jpeg
new file mode 100644
index 0000000..f310928
Binary files /dev/null and b/android1/app/src/androidTest/assets/image3.jpeg differ
diff --git a/android1/app/src/androidTest/assets/image_credits.txt b/android1/app/src/androidTest/assets/image_credits.txt
new file mode 100644
index 0000000..b3c3888
--- /dev/null
+++ b/android1/app/src/androidTest/assets/image_credits.txt
@@ -0,0 +1,3 @@
+Image1: https://pixabay.com/illustrations/woman-stand-wait-person-shoes-1427073/
+Image2: https://pixabay.com/photos/businessman-suit-germany-black-1146791/
+Image3: https://pixabay.com/photos/tree-pose-yoga-yogini-lifestyle-4823155/
diff --git a/android1/app/src/androidTest/assets/pose_landmark_truth.csv b/android1/app/src/androidTest/assets/pose_landmark_truth.csv
new file mode 100644
index 0000000..2a57769
--- /dev/null
+++ b/android1/app/src/androidTest/assets/pose_landmark_truth.csv
@@ -0,0 +1,3 @@
+nose_x,nose_y,left_eye_x,left_eye_y,right_eye_x,right_eye_y,left_ear_x,left_ear_y,right_ear_x,right_ear_y,left_shoulder_x,left_shoulder_y,right_shoulder_x,right_shoulder_y,left_elbow_x,left_elbow_y,right_elbow_x,right_elbow_y,left_wrist_x,left_wrist_y,right_wrist_x,right_wrist_y,left_hip_x,left_hip_y,right_hip_x,right_hip_y,left_knee_x,left_knee_y,right_knee_x,right_knee_y,left_ankle_x,left_ankle_y,right_ankle_x,right_ankle_y
+186,89,200,77,177,78,224,86,167,85,244,158,154,154,258,248,143,239,265,327,136,313,234,311,170,311,247,446,134,445,262,561,92,571
+182,84,191,73,171,74,202,75,157,77,220,119,139,136,260,192,185,230,268,209,246,217,221,288,176,294,205,421,174,421,186,538,155,564
diff --git a/android1/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/EvaluationUtils.kt b/android1/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/EvaluationUtils.kt
new file mode 100644
index 0000000..e7ce855
--- /dev/null
+++ b/android1/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/EvaluationUtils.kt
@@ -0,0 +1,113 @@
+/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
+
+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
+
+ http://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.
+==============================================================================
+*/
+
+package org.tensorflow.lite.examples.poseestimation.ml
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.PointF
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.tensorflow.lite.examples.poseestimation.data.BodyPart
+import org.tensorflow.lite.examples.poseestimation.data.Person
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import kotlin.math.pow
+
+object EvaluationUtils {
+
+ /**
+ * Assert whether the detected person from the image match with the expected result.
+ * Detection result is accepted as correct if it is within the acceptableError range from the
+ * expected result.
+ */
+ fun assertPoseDetectionResult(
+ person: Person,
+ expectedResult: Map,
+ acceptableError: Float
+ ) {
+ // Check if the model is confident enough in detecting the person
+ assertThat(person.score).isGreaterThan(0.5f)
+
+ for ((bodyPart, expectedPointF) in expectedResult) {
+ val keypoint = person.keyPoints.firstOrNull { it.bodyPart == bodyPart }
+ assertWithMessage("$bodyPart must exist").that(keypoint).isNotNull()
+
+ val detectedPointF = keypoint!!.coordinate
+ val distanceFromExpectedPointF = distance(detectedPointF, expectedPointF)
+ assertWithMessage("Detected $bodyPart must be close to expected result")
+ .that(distanceFromExpectedPointF).isAtMost(acceptableError)
+ }
+ }
+
+ /**
+ * Load an image from assets folder using its name.
+ */
+ fun loadBitmapAssetByName(name: String): Bitmap {
+ val testContext = InstrumentationRegistry.getInstrumentation().context
+ val testInput = testContext.assets.open(name)
+ return BitmapFactory.decodeStream(testInput)
+ }
+
+ /**
+ * Load csv from assets folder
+ */
+ fun loadCSVAsset(name: String): List