From 21146a11fdbe4c03ff494e713b31f6554de2ec46 Mon Sep 17 00:00:00 2001 From: darrelmarek Date: Tue, 10 Apr 2018 12:44:50 -0500 Subject: [PATCH] Added pedometer --- MoCha/Assets/Pedometer.meta | 9 ++ MoCha/Assets/Pedometer/Editor.meta | 9 ++ .../Pedometer/Editor/PedometerEditor.cs | 53 +++++++++ .../Pedometer/Editor/PedometerEditor.cs.meta | 12 ++ MoCha/Assets/Pedometer/Plugins.meta | 9 ++ MoCha/Assets/Pedometer/Plugins/Android.meta | 9 ++ .../Plugins/Android/AndroidManifest.xml | 13 +++ .../Plugins/Android/AndroidManifest.xml.meta | 8 ++ .../Assets/Pedometer/Plugins/Android/Makefile | 16 +++ .../Pedometer/Plugins/Android/Makefile.meta | 8 ++ .../Plugins/Android/PedometerActivity.jar | Bin 0 -> 2025 bytes .../Android/PedometerActivity.jar.meta | 33 ++++++ .../Plugins/Android/PedometerActivity.java | 69 ++++++++++++ .../Android/PedometerActivity.java.meta | 8 ++ MoCha/Assets/Pedometer/Plugins/Managed.meta | 9 ++ .../Pedometer/Plugins/Managed/IPedometer.cs | 33 ++++++ .../Plugins/Managed/IPedometer.cs.meta | 12 ++ .../Pedometer/Plugins/Managed/Pedometer.cs | 94 ++++++++++++++++ .../Plugins/Managed/Pedometer.cs.meta | 12 ++ .../Pedometer/Plugins/Managed/Platforms.meta | 9 ++ .../Managed/Platforms/PedometerAndroid.cs | 58 ++++++++++ .../Platforms/PedometerAndroid.cs.meta | 12 ++ .../Plugins/Managed/Platforms/PedometerGPS.cs | 103 ++++++++++++++++++ .../Managed/Platforms/PedometerGPS.cs.meta | 12 ++ .../Plugins/Managed/Platforms/PedometeriOS.cs | 68 ++++++++++++ .../Managed/Platforms/PedometeriOS.cs.meta | 12 ++ .../Pedometer/Plugins/Managed/Utilities.meta | 9 ++ .../Managed/Utilities/PedometerHelper.cs | 60 ++++++++++ .../Managed/Utilities/PedometerHelper.cs.meta | 12 ++ MoCha/Assets/Pedometer/Plugins/iOS.meta | 9 ++ .../Assets/Pedometer/Plugins/iOS/Pedometer.mm | 42 +++++++ .../Pedometer/Plugins/iOS/Pedometer.mm.meta | 39 +++++++ MoCha/Assets/Pedometer/README.md | 42 +++++++ MoCha/Assets/Pedometer/README.md.meta | 8 ++ MoCha/Assets/Scenes/Main.unity | Bin 45280 -> 46176 bytes MoCha/Assets/Scenes/StepCounter.unity | Bin 0 -> 48904 bytes MoCha/Assets/Scenes/StepCounter.unity.meta | 8 ++ MoCha/Assets/Scripts/SceneLoader.cs | 11 ++ MoCha/Assets/Scripts/SceneLoader.cs.meta | 13 +++ MoCha/Assets/Scripts/SpinningCube.cs | 3 +- MoCha/Assets/Scripts/StepCounter.cs | 35 ++++++ MoCha/Assets/Scripts/StepCounter.cs.meta | 12 ++ .../ProjectSettings/EditorBuildSettings.asset | Bin 4104 -> 4208 bytes MoCha/ProjectSettings/UnityAdsSettings.asset | Bin 4116 -> 0 bytes 44 files changed, 991 insertions(+), 2 deletions(-) create mode 100644 MoCha/Assets/Pedometer.meta create mode 100644 MoCha/Assets/Pedometer/Editor.meta create mode 100644 MoCha/Assets/Pedometer/Editor/PedometerEditor.cs create mode 100644 MoCha/Assets/Pedometer/Editor/PedometerEditor.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Android.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/Makefile create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/Makefile.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/PedometerActivity.jar create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/PedometerActivity.jar.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/PedometerActivity.java create mode 100644 MoCha/Assets/Pedometer/Plugins/Android/PedometerActivity.java.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/IPedometer.cs create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/IPedometer.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Utilities.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs create mode 100644 MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/iOS.meta create mode 100644 MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm create mode 100644 MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm.meta create mode 100644 MoCha/Assets/Pedometer/README.md create mode 100644 MoCha/Assets/Pedometer/README.md.meta create mode 100644 MoCha/Assets/Scenes/StepCounter.unity create mode 100644 MoCha/Assets/Scenes/StepCounter.unity.meta create mode 100644 MoCha/Assets/Scripts/SceneLoader.cs create mode 100644 MoCha/Assets/Scripts/SceneLoader.cs.meta create mode 100644 MoCha/Assets/Scripts/StepCounter.cs create mode 100644 MoCha/Assets/Scripts/StepCounter.cs.meta delete mode 100644 MoCha/ProjectSettings/UnityAdsSettings.asset diff --git a/MoCha/Assets/Pedometer.meta b/MoCha/Assets/Pedometer.meta new file mode 100644 index 0000000..e5f04d3 --- /dev/null +++ b/MoCha/Assets/Pedometer.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7a73552b2c9c547dc8b9a92c15744bd8 +folderAsset: yes +timeCreated: 1497547079 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Editor.meta b/MoCha/Assets/Pedometer/Editor.meta new file mode 100644 index 0000000..ce34b3c --- /dev/null +++ b/MoCha/Assets/Pedometer/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 188f5c9ce0ac2439499a73685331a44d +folderAsset: yes +timeCreated: 1497553151 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Editor/PedometerEditor.cs b/MoCha/Assets/Pedometer/Editor/PedometerEditor.cs new file mode 100644 index 0000000..f6beef1 --- /dev/null +++ b/MoCha/Assets/Pedometer/Editor/PedometerEditor.cs @@ -0,0 +1,53 @@ +/* +* Pedometer +* Copyright (c) 2017 Yusuf Olokoba +*/ + +namespace PedometerU.Utilities { + + using UnityEditor; + using UnityEditor.Build; + using System; + using System.IO; + + #if UNITY_IOS + using UnityEditor.Callbacks; + using UnityEditor.iOS.Xcode; + #endif + + public class PedometerEditor : IPreprocessBuild, IPostprocessBuild, IOrderedCallback { + + private string AndroidPlugins {get {return Path.Combine(Environment.CurrentDirectory, "Assets/Plugins/Android");}} + int IOrderedCallback.callbackOrder {get {return 0;}} + + private const string + MotionUsageKey = @"NSMotionUsageDescription", + MotionUsageDescription = @"Allow this app to use the pedometer."; // Change this as necessary + + void IPreprocessBuild.OnPreprocessBuild(BuildTarget target, string path) { + #if UNITY_ANDROID + // Create the Android plugins directory + Directory.CreateDirectory(AndroidPlugins); + // Copy the manifest into it // This is the only place where Unity picks up manifests, so we have to copy into it + File.Copy(Path.Combine(Environment.CurrentDirectory, "Assets/Pedometer/Plugins/Android/AndroidManifest.xml"), Path.Combine(AndroidPlugins, "AndroidManifest.xml")); + #elif UNITY_IOS + // Get the property list + string plistPath = path + "/Info.plist"; + PlistDocument plist = new PlistDocument(); + // Read it + plist.ReadFromString(File.ReadAllText(plistPath)); + PlistElementDict rootDictionary = plist.root; + // Add the motion usage description + rootDictionary.SetString(MotionUsageKey, MotionUsageDescription); + File.WriteAllText(plistPath, plist.WriteToString()); + #endif + } + + void IPostprocessBuild.OnPostprocessBuild (BuildTarget target, string path) { + #if UNITY_ANDROID + // Delete the Android manifest from Plugins/Android + File.Delete(Path.Combine(AndroidPlugins, "AndroidManifest.xml")); + #endif + } + } +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Editor/PedometerEditor.cs.meta b/MoCha/Assets/Pedometer/Editor/PedometerEditor.cs.meta new file mode 100644 index 0000000..57dd850 --- /dev/null +++ b/MoCha/Assets/Pedometer/Editor/PedometerEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: dec45390b23614bf898574d3ef1a2278 +timeCreated: 1497553151 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins.meta b/MoCha/Assets/Pedometer/Plugins.meta new file mode 100644 index 0000000..ee177cd --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 16f5885743c8a4311b5892c1f5bc06ad +folderAsset: yes +timeCreated: 1497486100 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Android.meta b/MoCha/Assets/Pedometer/Plugins/Android.meta new file mode 100644 index 0000000..b756517 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Android.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f118efd25bcfb44008cbb6a4bd8d38aa +folderAsset: yes +timeCreated: 1497486100 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml b/MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml new file mode 100644 index 0000000..01810c7 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml.meta b/MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml.meta new file mode 100644 index 0000000..26524dd --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Android/AndroidManifest.xml.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6d12e0df1fc5342fca4a7a81d9a17a2d +timeCreated: 1497486100 +licenseType: Pro +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Android/Makefile b/MoCha/Assets/Pedometer/Plugins/Android/Makefile new file mode 100644 index 0000000..ab79838 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Android/Makefile @@ -0,0 +1,16 @@ +# +# Pedometer +# Copyright (c) 2017 Yusuf Olokoba +# + +SDKLIB := /Users/yusuf/Documents/ADT/SDK/platforms/android-23/android.jar # Replace with your path to the Android base class library +UNITYLIB := /Applications/Unity/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar # Replace with your path to Unity's classes.jar +SRC := PedometerActivity + +build: + @echo "Building..." + javac -source 1.6 -target 1.6 "$(SRC).java" -bootclasspath $(SDKLIB) -classpath $(UNITYLIB) -d . + jar cf "$(SRC).jar" com + @rm -rf com + @echo "Completed" + @echo "------------------------------------------------------ " \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Android/Makefile.meta b/MoCha/Assets/Pedometer/Plugins/Android/Makefile.meta new file mode 100644 index 0000000..47af32d --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Android/Makefile.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 100f626688ea24b02a075800806d5cfc +timeCreated: 1497486100 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Android/PedometerActivity.jar b/MoCha/Assets/Pedometer/Plugins/Android/PedometerActivity.jar new file mode 100644 index 0000000000000000000000000000000000000000..ad19570156cf7b6f11281a7d555bec96ee1e6a0f GIT binary patch literal 2025 zcmWIWW@Zs#;Nak3n3;FWivbC6GO#fCx`sIFdiuHP|2xINz|0Wf&CUT*!30$nfK#&w zPz7AGucM!*n`>~0p0C?y-!rFuymj?1@_OrPojY@WbCAIm;|EWR^t^m^Jbf>gu43Vg zcp-U2T1rYYn;X0a}(7T&O5i|^6oQVbc-$@&!`S#a_;${r$-<-cL_kOkTu_TH4?H?nY+>Qy&I}vfL zt4Du>RxBO2RrZan?`r28$awV&3qPKocUv>7er2mwJ2}P?`<`p`* zo_9L?n}ex6PkPzIJE1D`J$~3pNicSA3ys%?QOx`&3lerNSk?i*0Woy!o2cUKbtn;%h7W@ z%BnZEL@e9CCgaQem4?wzmQ5CjDmqq_+k0NJsrrT5O#a7%Fgh zLz7}m>I_+n+C<^>h}~Q}LLlWe)7&8#;B{7IsZNnXxKJ+}7%? zaA#gX>5i4!H^j8q$|Tm!38;K?eI;9+c;u!Kq(uO>Io=2^Q)>!Z$8%Y7R*9yWDnlGJEO3qRw^ zd0xcE-Sg1AruQFg50{=jb<{iW;N2~ouC3d;@YrW}xkt-a`FRQ#gilF~>3_aCt1}@k zRBZ3_u-KNEr`mS;>O|F=yOR4> z?ut||u2@u^yz-${`>wAZPpsU`=51TSEm65A>TohIpQcg5XWfH$?fMiV{~O3M3YYR} z%I3HoH+nQ{`C0z`iz^G$ZfhuBwKbEl_B%0itwzx1wPJsq&J>hxV*7br1HCkr(9B_)VAO&uSJwThN+*ASXW+Ib|lR&>F?X^3++4? zZOFW7IdzB66^_KeCHlPc-7ZcI-GBAu#_fx4e65Vr2$0MEUA_EX)UxT(_Ip)d201YQ zeX(zGczW}W=uEzgcClMc`Yx>MPjf4Nd?G*L_m=|&Zv|MkT&&p3^q12y-`v*dlMwIv zi$+46atl7{x74+%Y>QiXPHdUx83g#hHvjIFqkBA#F^_M^Q1Dx4JwIW>{$+ExH0nzJ=*J%y~e3{ z5)ap`x$}-QZ|O5wN#DJXqBLsy$EvygC5YCFoj_%X3iOfdIBZCR{60O#(83ivf4J zk1*jdkO`>;aO(io0|@XG$OP#?s}*o-2h|G*Ai{`6J8~_78f?fx3aTd%z?XnQ*lUdd SZ&o&t8Eil}9cY3Lm + /// Event used to propagate step events + /// + event StepCallback OnStep; + /// + /// Is this implementation supported by the current platform? + /// + bool IsSupported {get;} + #endregion + + + #region --Client API-- + /// + /// Initialize this Pedometer implementation + /// + IPedometer Initialize (); + /// + /// Teardown this Pedometer implementation and release any resources + /// + void Release (); + #endregion + } +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/IPedometer.cs.meta b/MoCha/Assets/Pedometer/Plugins/Managed/IPedometer.cs.meta new file mode 100644 index 0000000..63a79d7 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/IPedometer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 88d6047c336bd4e2b96e973d7f5e2cb6 +timeCreated: 1497547083 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs b/MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs new file mode 100644 index 0000000..d0e3220 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs @@ -0,0 +1,94 @@ +/* +* Pedometer +* Copyright (c) 2017 Yusuf Olokoba +*/ + +namespace PedometerU { + + using Platforms; + using System; + using System.Linq; + + public sealed class Pedometer : IDisposable { + + #region --Properties-- + + /// + /// How many updates has this pedometer received? Useful for calculating pedometer precision + /// + public int updateCount {get; private set;} + + /// + /// Pedometer implementation for the current device. Do not use unless you know what you are doing + /// + public static IPedometer Implementation { + get { + return _Implementation = _Implementation ?? new IPedometer[] { + new PedometerAndroid(), + new PedometeriOS(), + new PedometerGPS() // Always supported, uses GPS (so highly inaccurate) + }.First(impl => impl.IsSupported).Initialize(); + } + } + private static IPedometer _Implementation; + #endregion + + + #region --Op vars-- + private int initialSteps; // Some step counters count from device boot, so subtract the initial count we get + private double initialDistance; + private readonly StepCallback callback; + #endregion + + + #region --Ctor-- + + /// + /// Create a new pedometer and start listening for updates + /// + public Pedometer (StepCallback callback) { + // Save the callback + this.callback = callback; + // Register callback + Implementation.OnStep += OnStep; + } + #endregion + + + #region --Operations-- + + /// + /// Stop listening for pedometer updates and dispose the object + /// + public void Dispose () { + // Unregister callback + Implementation.OnStep -= OnStep; + } + + /// + /// Release Pedometer and all of its resources + /// + public static void Release () { + if (_Implementation == null) return; + // Release and dereference + _Implementation.Release(); + _Implementation = null; + } + + private void OnStep (int steps, double distance) { // DEPLOY // UpdateCount post increment + // Set initials and increment update count + initialSteps = updateCount++ == 0 ? steps : initialSteps; + initialDistance = steps == initialSteps ? distance : initialDistance; + // If this is not the first step, then invoke the callback + if (steps != initialSteps) if (callback != null) callback(steps - initialSteps, distance - initialDistance); + } + #endregion + } + + /// + /// A delegate used to pass pedometer information + /// + /// Number of steps taken + /// Distance walked in meters + public delegate void StepCallback (int steps, double distance); +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs.meta new file mode 100644 index 0000000..4ff810c --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Pedometer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c21bada5b411643bbac7a50e3505bfb4 +timeCreated: 1497547083 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms.meta new file mode 100644 index 0000000..1adfba2 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 977b4cd8abd1a471daca27683d9cca3a +folderAsset: yes +timeCreated: 1497488311 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs new file mode 100644 index 0000000..67bf272 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs @@ -0,0 +1,58 @@ +/* +* Pedometer +* Copyright (c) 2017 Yusuf Olokoba +*/ + +namespace PedometerU.Platforms { + + using UnityEngine; + using Utilities; + + public sealed class PedometerAndroid : IPedometer { + + #region --Properties-- + + public event StepCallback OnStep { + add { + PedometerHelper.Instance.OnStep += value; + } remove { + PedometerHelper.Instance.OnStep -= value; + } + } + + public bool IsSupported { + get { + #if !UNITY_ANDROID || UNITY_EDITOR + return false; + #endif + #pragma warning disable 0162 + // Get a reference to PedometerActivity + using (var player = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) pedometer = player.GetStatic("currentActivity"); + // Check if supported + return pedometer.Call("isSupported"); + #pragma warning restore 0162 + } + } + #endregion + + + #region --Op vars-- + private AndroidJavaObject pedometer; + #endregion + + + #region --Client API-- + + public IPedometer Initialize () { + // Initialize pedometer natively + pedometer.Call("initialize"); + return this; + } + + public void Release () { + // Release pedometer natively + pedometer.Call("release"); + } + #endregion + } +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs.meta new file mode 100644 index 0000000..3fef5bb --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerAndroid.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a4ea9668c61c34ca1bdb687aa038db95 +timeCreated: 1497547449 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs new file mode 100644 index 0000000..d0aba76 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs @@ -0,0 +1,103 @@ +/* +* Pedometer +* Copyright (c) 2017 Yusuf Olokoba +*/ + +namespace PedometerU.Platforms { + + using UnityEngine; + using System; + using System.Collections; + using Utilities; + + public sealed class PedometerGPS : IPedometer { + + #region --Properties-- + public event StepCallback OnStep; + public bool IsSupported {get {return true;}} + public bool Running {get; private set;} + private const double + LocationTimeout = 20f, + EarthRadius = 6378.137d, // Meters + DegreesToRadians = Math.PI / 180d, + StepsToMeters = 0.715f; + #endregion + + + #region --Client API-- + + public IPedometer Initialize () { + // Check that the user has granted permissions + if (!Input.location.isEnabledByUser) Debug.LogError("Pedometer Error: GPS backend requires GPS permissions"); + // Initialize location services + else PedometerHelper.Instance.Dispatch(Update()); + // Log + Debug.Log("Pedometer: Initialized GPS backend"); + return this; + } + + public void Release () { + // Stop running + Running = false; + // Log + Debug.Log("Pedometer: Released GPS backend"); + } + #endregion + + + #region --Operations-- + + private IEnumerator Update () { + // Start location service + Input.location.Start(0.5f, 3f); + // Wait until service initializes + var time = Time.realtimeSinceStartup; + yield return new WaitWhile(() => Input.location.status == LocationServiceStatus.Initializing && Time.realtimeSinceStartup - time < LocationTimeout); + // Check that service initialized + if (Input.location.status != LocationServiceStatus.Running) { + Debug.LogError("Pedometer Error: Failed to initialize location service with status: "+Input.location.status); + yield break; + } + // Get current location + var lastData = Input.location.lastData; + var distance = 0.0d; + Running = true; + // Update location + while (Running) { + // State checking + if (Input.location.status != LocationServiceStatus.Running) yield break; + // Check that the data was updated + if (Math.Abs(Input.location.lastData.timestamp - lastData.timestamp) < 1e-10) goto end; + // Accumulate haversine distance + var currentData = Input.location.lastData; + distance += Distance(lastData.latitude, lastData.longitude, currentData.latitude, currentData.longitude); + // Invoke event + if (OnStep != null) OnStep((int)(distance / StepsToMeters), distance); + // Update data + lastData = currentData; + // Next frame + end: yield return null; + } + // Stop location service + Input.location.Stop(); + } + + /// + /// Calculate the haversine distance between two latitude-longitude pairs + /// Implemented from: https://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates + /// + private static double Distance (double lat1, double lon1, double lat2, double lon2) { + double + dlat = (lat2 - lat1) * DegreesToRadians, + dlong = (lon2 - lon1) * DegreesToRadians, + a = Math.Pow(Math.Sin(dlat / 2.0), 2) + + Math.Cos(lat1 * DegreesToRadians) * + Math.Cos(lat2 * DegreesToRadians) * + Math.Pow(Math.Sin(dlong / 2.0), 2), + c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a)), + distance = EarthRadius * c; + return distance; + } + #endregion + } +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs.meta new file mode 100644 index 0000000..d1c862b --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometerGPS.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 35cee1988de7f4d63b77211cc91fb2c4 +timeCreated: 1497635191 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs new file mode 100644 index 0000000..eb9b7bf --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs @@ -0,0 +1,68 @@ +/* +* Pedometer +* Copyright (c) 2017 Yusuf Olokoba +*/ + +namespace PedometerU.Platforms { + + using System.Runtime.InteropServices; + using Utilities; + + public sealed class PedometeriOS : IPedometer { + + #region --Properties-- + + public event StepCallback OnStep { + add { + PedometerHelper.Instance.OnStep += value; + } remove { + PedometerHelper.Instance.OnStep -= value; + } + } + + public bool IsSupported { + get { + #if !UNITY_IOS || UNITY_EDITOR + return false; + #endif + #pragma warning disable 0162 + return PDIsSupported(); + #pragma warning restore 0162 + } + } + #endregion + + + #region --Client API-- + + public IPedometer Initialize () { + // Initialize pedometer natively + PDInitialize(); + return this; + } + + public void Release () { + // Release pedometer natively + PDRelease(); + } + #endregion + + + #region --Bridge-- + + #if UNITY_IOS + [DllImport("__Internal")] + private static extern void PDInitialize (); + [DllImport("__Internal")] + private static extern void PDRelease (); + [DllImport("__Internal")] + private static extern bool PDIsSupported (); + + #else // Keep IL2CPP happy + private static void PDInitialize () {} + private static void PDRelease () {} + private static bool PDIsSupported () {return false;} + #endif + #endregion + } +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs.meta new file mode 100644 index 0000000..21952db --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Platforms/PedometeriOS.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 70a78ba5eb6f443e6acf356ebd08f735 +timeCreated: 1497547449 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Utilities.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Utilities.meta new file mode 100644 index 0000000..497a0b8 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Utilities.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ee053aa1ec5124ef392cdea2fdae0609 +folderAsset: yes +timeCreated: 1497549080 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs b/MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs new file mode 100644 index 0000000..d3dfb12 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs @@ -0,0 +1,60 @@ +/* +* Pedometer +* Copyright (c) 2017 Yusuf Olokoba +*/ + +namespace PedometerU.Utilities { + + using UnityEngine; + using System.Collections; + + /// + /// Helper class used for dispatching coroutines and receiving native events raised by UnitySendMessage + /// + public class PedometerHelper : MonoBehaviour { + + + #region --Properties-- + public event StepCallback OnStep; + public static readonly PedometerHelper Instance; + #endregion + + + #region --Client API-- + + /// + /// Dispatch a coroutine to be invoked + /// + /// The queued coroutine + public Coroutine Dispatch (IEnumerator routine) { + return StartCoroutine(routine); + } + + /// + /// Event used by native implementations to report pedometer events + /// + private void OnEvent (string data) { + // Schema: "steps:distance" + int steps; double distance; string[] tokens = data.Split(':'); + // Parse + if (OnStep == null || !int.TryParse(tokens[0], out steps) || !double.TryParse(tokens[1], out distance)) return; + // Raise event + OnStep(steps, distance); + } + #endregion + + + #region --Initialization-- + + static PedometerHelper () { + // Create the singleton + Instance = new GameObject("Pedometer").AddComponent(); + } + + private void Awake () { + // Preserve across scenes + DontDestroyOnLoad(this); + } + #endregion + } +} \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs.meta b/MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs.meta new file mode 100644 index 0000000..5a5e677 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/Managed/Utilities/PedometerHelper.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9304dcc86ca774a288d7b682a7e9c429 +timeCreated: 1497549080 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/iOS.meta b/MoCha/Assets/Pedometer/Plugins/iOS.meta new file mode 100644 index 0000000..6538350 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/iOS.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6eb84ce3919f84b27ba30affccff2479 +folderAsset: yes +timeCreated: 1497486100 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm b/MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm new file mode 100644 index 0000000..dc6e852 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm @@ -0,0 +1,42 @@ +// +// Pedometer.mm +// Pedometer +// +// Created by Yusuf on 06/15/17. +// Copyright (c) 2017 Yusuf Olokoba +// + +#import + +#define BRIDGE extern "C" +#define STEP2METERS 0.715 + +static CMPedometer* pedometer; + +BRIDGE void PDInitialize () { + // Create an instance + pedometer = [CMPedometer new]; + // Start updates + [pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData* data, NSError* error) { + // Extract data + int steps = data.numberOfSteps.intValue; + double distance = CMPedometer.isDistanceAvailable ? data.distance.doubleValue : steps * STEP2METERS; + // Send to Unity + UnitySendMessage("Pedometer", "OnEvent", [[NSString stringWithFormat:@"%i:%f", steps, distance] UTF8String]); + }]; + // Log + NSLog(@"%s", "Pedometer: Initialized iOS backend"); +} + +BRIDGE void PDRelease () { + // Release and dereference + if (pedometer) [pedometer stopPedometerUpdates]; + pedometer = nil; + // Log + NSLog(@"%s", "Pedometer: Released iOS backend"); +} + +BRIDGE bool PDIsSupported () { + // Check if step counting is available + return CMPedometer.isStepCountingAvailable; +} diff --git a/MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm.meta b/MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm.meta new file mode 100644 index 0000000..76b9100 --- /dev/null +++ b/MoCha/Assets/Pedometer/Plugins/iOS/Pedometer.mm.meta @@ -0,0 +1,39 @@ +fileFormatVersion: 2 +guid: 0c8960212d3114db98deabda72834807 +timeCreated: 1497547087 +licenseType: Pro +PluginImporter: + serializedVersion: 2 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + isOverridable: 0 + platformData: + data: + first: + Any: + second: + enabled: 0 + settings: {} + data: + first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + data: + first: + iPhone: iOS + second: + enabled: 1 + settings: {} + data: + first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Pedometer/README.md b/MoCha/Assets/Pedometer/README.md new file mode 100644 index 0000000..2cfbcfd --- /dev/null +++ b/MoCha/Assets/Pedometer/README.md @@ -0,0 +1,42 @@ +# Pedometer API +Pedometer is a native pedometer API for the Unity game engine. The API provides sensor information about step count and distance covered (in meters). It is a minimalist API with native backends on iOS and Android. There is also a backend that uses GPS in the case that a hardware pedometer is not available for use. Download the unitypackage [here](). + +## Pedometer +To use the API, simply create a `Pedometer` instance: +```csharp +// Create a pedometer +// The OnStep callback will be invoked each time a step is detected +var pedometer = new Pedometer(OnStep); + +void OnStep (int steps, double distance) { + // Display the values + stepText.text = steps.ToString(); + // Display distance in feet + distanceText.text = (distance * 3.28084).ToString("F2") + " ft"; +} +``` + +When you are done detecting steps from a `Pedometer` instance, you must dispose it: +```csharp +// Dispose of this pedometer instance +pedometer.Dispose(); +``` + +Because the API uses native resources, you may wish to release these resources (like stopping the step sensor to save power). To do so, use the `Release` function: +```csharp +// Release Pedometer's native resources +Pedometer.Release(); +``` + +## Architecture +The Pedometer API is modeled after the NatCam API, comprising of a unified front end and several platform-specific backend implementations. Each backend implementation must implement the `IPedometer` interface. These backend implementations are responsible for managing native resources and sensor events. On Android, Pedometer uses `SensorManager` whereas on iOS, Pedometer uses `CMPedometer`. + +## Modifications +To make modifications, simply edit the sources in Pedometer>Plugins. The `Android` directory contains the Android source, a Makefile, and an AndroidManifest.xml (which must not be renamed or moved). To build your Android changes, modify the Makefile with paths specific to your installations then run `make` in the `Android` directory. The java source will be compiled into a .jar which will be included in the application binary. + +On iOS, simply make changes you want. No need to build anything; the Unity build process will copy the Objective-C++ sources to the XCode project. + +Please contribute your changes back to the repository! Since Pedometer is open source, its development must be driven by the community. + +## Credits +- [Yusuf Olokoba](mailto:olokobayusuf@gmail.com) \ No newline at end of file diff --git a/MoCha/Assets/Pedometer/README.md.meta b/MoCha/Assets/Pedometer/README.md.meta new file mode 100644 index 0000000..1fdffab --- /dev/null +++ b/MoCha/Assets/Pedometer/README.md.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ce546a2d42ba34e34a05f4ef8c708d74 +timeCreated: 1497488311 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/MoCha/Assets/Scenes/Main.unity b/MoCha/Assets/Scenes/Main.unity index cda3c805306223fb5b557c4142fb5e944d6bb4c3..1ab26f5b7bec890367b680cd8bef63dd9ccc5c0b 100644 GIT binary patch delta 1403 zcmYL}Ye-a45Xa~4s;QZ7ZJC*hhSDu9iUKt7ZcP_gyckcY=oS8W@_pJY;!5=V~ zh}@|}w`@c-9`BZB(?liZW>$OltXd$7p-(544gM@ETy^k%`p1hS&v!J{SZ!p`f2wDmn3-Pk21FQ)4SjY>*Oa+Pp zU?-rV=q0n7NTSQI;~=k?C83nd*f9=lnAspoF@eQ{y=LY^DJqej5&(C69l}9~ego=B zs+l=r!bx^AQ&Fv+WEV3Zs@0S1X68p_ND|E9D9D{Eb3~)E{m}9t^#w~p&;jTLkS96H z3zJZ;imEYQI>yX`YW08*+KV#aqG~a8+HCj3(+>+D06k)l3tMYo;{l?FS8o3A{-|X zEO|@s94HzaVsHGTuqBdT80fYs^g6lu)}+6!kE)fywsE+ttg= z2UZRn`KgbYipzTswj91IuXr021;oWhv)z^!F4N7P-(WfXSqNrodbrHd=d-uV2v)E< zXj$erWH3J!Iw$ZtBFiwjKtx*K?gZaY=%dy`V=Kf`_^$< xQH)w$=Urn4x*7cG|3C7+l&U@bf?>Wyo=Q+>2B|hmA z?{!9^D|?7y2_jmJ_s)PZUolUuAE{FJRfo4Lnd*;@gB41xXzg>?-b9cA7VK`r6T5Uo zo8Z%6f?0yu3a}6}qxjxu@;db(pc$|MUWtQsLTADTnYmD6H*^+kR<}`sqcGS7cv<}w zGYiU&La&6qW;P=kc4ULSVb+;WWK_sYIe>2&`p|_HdKK)*{9`mt6amwUc$nGHnO5W| zv$$kjq=T6cU7*NPjbqIG=t8UEtMhI%%OVc}Pr}QSbk{928#)U^%afdEHUnlw!d$Q( zW=V9bC44ecUx6hmLcke#neQnxKUfSJmrKu>IlWNYsK*YxT!%G^f%U`7&Ew2m_zdJV z!Ow?-D}0@TLe9Cs|SBv=@0a{POlT`EqE zzlB+>B$Y6-IJ8UVi-UE;%Lz^}bK|1rv6vhv2BDEY);?Sqw~TI>^jbj^87HGlE&yT5h@-i58w$AC?Jk0G@@HU4LO_u@Ln_ zLZ%^o}D zv5#NtwTsc9Rpz0(tT}tf^rAhl$KoLJIq(-iPOF#?HWvIE`(S@j`PaU6gK23=hLe~L zn^d)QpCdj40ZS5uwK!tpReW` y;C_#^>(hTOo_5T=Yf;;8O({y6nlpM?5yrtn_1@UHUi1zPi-yS=#1=<)X8r@*a|37q diff --git a/MoCha/Assets/Scenes/StepCounter.unity b/MoCha/Assets/Scenes/StepCounter.unity new file mode 100644 index 0000000000000000000000000000000000000000..a5befc3c57b75bf003d2028fa12396f45c6ac9b2 GIT binary patch literal 48904 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 S>iJh5o+}^pFLB6GQUUnGu delta 120 zcmeyM(4k<*z`)qYz#zcEz`(!{q=6IzgQ=deo^hHX!@vLk|1&ZI1w>gG7*c0#On=Q^ zVrJesD`kyNJp(sT++dS6ov0JYW=BI!JXTRyFv}(wp|o0C@QtO5{V?0NSjDAE_9Vw@C6h%?seg# zxb#glo-;`m(I=1@xHI>Db8|8|vx-P*N@Q_aL>91O5!tA_b+_M;k1WfS0;plSeR*;p z%Ex!Ft4H^h_g=LM?jg9(u_=TSHIj8vHDOR9m4L0$${O>#%xz*g`Kb*9NpIC21f zfnYuJ#dCe^8U0zTf9cP$zxi9gLT=)x{(QlEQK+w5e$oxJ4x{r#hNm5%&>Ms|!CutW z-bi0vf*uBm-i`eLX*=@H27$jn@N_>O4b3xgvgHkfj*snDwtB@I`F?7z`q}ViN_!H39@@)`~Uy|