I've been struggling to find the solution to loading some SharedPreferences values into SensorEventListener. I keep on getting blank values, null or it just crashes from example code that I found on Stackoverflow.
Here are a few of my references:
Android - How to use SharedPreferences in non-Activity class?
Android: Using SharedPreferences in a Shake Listener Class
Here is the Shake Detection code from below:
Android: I want to shake it
I'm getting these values not from a different activity, and I'd like to load them from SharedPreferences into the public class ShakeEventListener and change the static values for MIN_FORCE, MIN_DIRECTION_CHANGE and MAX_TOTAL_DURATION_OF_SHAKE to the SharedPreferences values based on the stored values.
Example of my SharedPerferences code from Motion.java:
SharedPerferences for value: MIN_FORCE
SharedPreferences spa = getSharedPreferences("ForceStore", Activity.MODE_PRIVATE);
int ValueForce = spa.getInt("Force", 15);
SharedPerferences for value: MIN_DIRECTION_CHANGE
SharedPreferences spb = getSharedPreferences("DirectionStore", MODE_PRIVATE);
int ValueDirection = spb.getInt("Direction", 2);
SharedPerferences for value: MAX_TOTAL_DURATION_OF_SHAKE
SharedPreferences spc = getSharedPreferences("ShakeStore", MODE_PRIVATE);
int ValueShake = spc.getInt("Shake", 200);
Example code from MainActivity.java:
((ShakeEventListener) mSensorListener).setOnShakeListener(new ShakeEventListener.OnShakeListener() {
public void onShake() {
// My code here to react to the Motion detected
}
}
});
}
Example code of public class ShakeEventListener:
public class ShakeEventListener implements SensorEventListener {
private static final int MIN_FORCE = 15;
private static final int MIN_DIRECTION_CHANGE = 2;
/** Maximum pause between movements. */
private static final int MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE = 200;
/** Maximum allowed time for shake gesture. */
private static final int MAX_TOTAL_DURATION_OF_SHAKE = 200;
/** Time when the gesture started. */
private long mFirstDirectionChangeTime = 0;
/** Time when the last movement started. */
private long mLastDirectionChangeTime;
/** How many movements are considered so far. */
private int mDirectionChangeCount = 0;
/** The last x position. */
private float lastX = 0;
/** The last y position. */
private float lastY = 0;
/** The last z position. */
private float lastZ = 0;
/** OnShakeListener that is called when shake is detected. */
private OnShakeListener mShakeListener;
public interface OnShakeListener {
void onShake();
}
public void setOnShakeListener(OnShakeListener listener) {
mShakeListener = listener;
}
#Override
public void onSensorChanged(SensorEvent se) {
// get sensor data
//float x = se.values[0];
//float y = se.values[1];
float z = se.values[2];
// calculate movement
//float totalMovement = Math.abs(x + y + z - lastX - lastY - lastZ);
float totalMovement = Math.abs(z - lastZ);
if (totalMovement > MIN_FORCE) {
// get time
long now = System.currentTimeMillis();
// store first movement time
if (mFirstDirectionChangeTime == 0) {
mFirstDirectionChangeTime = now;
mLastDirectionChangeTime = now;
}
// check if the last movement was not long ago
long lastChangeWasAgo = now - mLastDirectionChangeTime;
if (lastChangeWasAgo < MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE) {
// store movement data
mLastDirectionChangeTime = now;
mDirectionChangeCount++;
// store last sensor data
//lastX = x;
//lastY = y;
lastZ = z;
// check how many movements are so far
if (mDirectionChangeCount >= MIN_DIRECTION_CHANGE) {
// check total duration
long totalDuration = now - mFirstDirectionChangeTime;
if (totalDuration < MAX_TOTAL_DURATION_OF_SHAKE) {
mShakeListener.onShake();
resetShakeParameters();
}
}
} else {
resetShakeParameters();
}
}
}
private void resetShakeParameters() {
mFirstDirectionChangeTime = 0;
mDirectionChangeCount = 0;
mLastDirectionChangeTime = 0;
// lastX = 0;
// lastY = 0;
lastZ = 0;
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Related
I inherited by my ex collegue a Unity game. Now I need to implement this behaviour. The game consist in a car drive by user using Logitech steering.
Now I need to show a Text every X minute in a part of the screen like "What level of anxiety do you have ? " Ad the user should to set a value from 0 to 9 using the Logitech steering but I really don't know where to start.
This is my manager class:
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
namespace DrivingTest
{
// Braking test Manager
public class BtManager : BaseTestManger
{
// Public variables
public BreakCar breakCar;
public float minBrakeThreshold = 0.2f;
internal int vehicalSpeed;
internal int noOfEvents;
internal float durationOfTest;
// Private variables
private IRDSPlayerControls playerControlCar;
protected int xSpeed;
protected int ySpeed;
private float brakeEventStartTime;
private float brakeEventDifferenceTime;
private SafetyDistanceCheck safetyDistanceCheck;
protected float totalSafetyDistance;
protected int totalSafetyDistanceCount;
private List<float> averageEventReactionTime = new List<float>();
#region Init
// Use this for initialization
public override void Start()
{
base.Start();
playerControlCar = car.GetComponent<IRDSPlayerControls>();
safetyDistanceCheck = car.GetComponent<SafetyDistanceCheck>();
}
public override void OnEnable()
{
base.OnEnable();
BreakCar.BrakeStart += BrakeCarEvent;
BreakCar.BrakeEventEnd += CallTestOver;
}
public override void OnDisable()
{
base.OnDisable();
BreakCar.BrakeStart -= BrakeCarEvent;
BreakCar.BrakeEventEnd -= CallTestOver;
}
protected override void SetUpCar()
{
car.AddComponent<SelfDriving>();
}
#endregion
/// <summary>
/// Calls the main Test over method.
/// </summary>
private void CallTestOver()
{
GameManager.Instance.Testover();
}
protected override void OnSceneLoaded(eTESTS test)
{
UiManager.Instance.UpdateInstructions(Constants.BT_INSTRUCTION);
}
protected override void GetApiParams()
{
NextTestOutput nextTestOutput = GameManager.Instance.currentTest;
if (nextTestOutput.content != null && nextTestOutput.content.Count > 0)
{
foreach (var item in nextTestOutput.content[0].listInputParameter)
{
switch ((ePARAMETERS)item.id)
{
case ePARAMETERS.X_SPEED:
xSpeed = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.Y_SPEED:
ySpeed = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.SPEED_OF_VEHICLE:
vehicalSpeed = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.NUMBER_OF_EVENTS:
noOfEvents = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.DURATION_OF_TEST:
durationOfTest = float.Parse(item.Value) * 60; //converting minutes into seconds
break;
}
}
SetupBrakeCar();
}
}
protected virtual void SetupBrakeCar()
{
DOTween.Clear();
durationOfTest = noOfEvents * Random.Range(10,20); // The random value is the time between two consecutive tests.
breakCar.SetInitalParams(new BrakeCarInit
{
carMaxSpeed = vehicalSpeed * 0.95f,
durationOfTest = durationOfTest,
noOfEvents = noOfEvents,
xSpeed = xSpeed,
ySpeed = ySpeed
});
breakCar.distanceCheck = true;
}
protected override void SubmitRestultToServer()
{
SubmitTest submitTest = new SubmitTest
{
outputParameters = new List<SaveOutputParameter>
{
new SaveOutputParameter
{
id = (int)ePARAMETERS.OUT_REACTION_TIME,
value = ((int)(GetEventReactionTimeAvg () * Constants.FLOAT_TO_INT_MULTIPLAYER)).ToString()
},
new SaveOutputParameter
{
id = (int)ePARAMETERS.OUT_AVG_RT,
value = GetReactionResult().ToString()
}
}
};
WebService.Instance.SendRequest(Constants.API_URL_FIRST_SAVE_TEST_RESULT, JsonUtility.ToJson(submitTest), SubmitedResultCallback);
}
protected override void ShowResult()
{
UiManager.Instance.UpdateStats(
(
"1. " + Constants.EVENT_REACTION + (GetEventReactionTimeAvg() * Constants.FLOAT_TO_INT_MULTIPLAYER).ToString("0.00") + "\n" +
"2. " + Constants.REACTION_TIME + reactionTest.GetReactionTimeAvg().ToString("0.00")
));
}
public void LateUpdate()
{
//Debug.Log("TH " + .Car.ThrottleInput1 + " TH " + playerControlCar.Car.ThrottleInput + " brake " + playerControlCar.Car.Brake + " brake IP " + playerControlCar.Car.BrakeInput);
//Debug.Log("BrakeCarEvent "+ brakeEventStartTime + " Player car " + playerControlCar.ThrottleInput1 + " minBrakeThreshold " + minBrakeThreshold);
if (brakeEventStartTime > 0 && playerControlCar.Car.ThrottleInput1 > minBrakeThreshold)
{
brakeEventDifferenceTime = Time.time - brakeEventStartTime;
Debug.Log("Brake event diff " + brakeEventDifferenceTime);
Debug.Log("Throttle " + playerControlCar.Car.ThrottleInput1);
averageEventReactionTime.Add(brakeEventDifferenceTime);
brakeEventStartTime = 0;
safetyDistanceCheck.GetSafetyDistance(SafetyDistance);
}
}
private void SafetyDistance(float obj)
{
totalSafetyDistance += obj;
++totalSafetyDistanceCount;
}
/// <summary>
/// Records the time when the car ahead starts braking
/// </summary>
protected void BrakeCarEvent()
{
brakeEventStartTime = Time.time;
Debug.Log("BrakeCarEvent ");
}
/// <summary>
/// calculates the average time taken to react
/// </summary>
public float GetEventReactionTimeAvg()
{
float avg = 0;
foreach (var item in averageEventReactionTime)
{
avg += item;
}//noofevents
return avg / (float)averageEventReactionTime.Count;
}
}
}
EDIT
I create new class FearTest:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace DrivingTest
{
public class FearTest : MonoBehaviour
{
//public GameObject redDot;
public TextMeshPro textAnsia2;
public TextMesh textAnsia3;
public int shouldEvaluateTest = 0; // 0 - false, 1 - true
public int secondsInterval;
public int secondsToDisaply;
public int radiusR1;
public int radiusR2;
public int reactionTestDuration;
public int maxDots;
public float dotRadius = 1;
private float endTime;
private float dotDisaplyAtTime;
private List<float> dotDisplayReactTime = new List<float>();
private int missedSignals;
private int currentDotsCount;
private bool isReactionAlreadyGiven;
public bool ShouldEvaluateTest
{
get
{
return shouldEvaluateTest != 0;
}
}
void OnEnable()
{
GameManager.TestSceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
GameManager.TestSceneLoaded -= OnSceneLoaded;
}
#region Scene Loaded
private void OnSceneLoaded(eTESTS test)
{
if (SceneManager.GetActiveScene().name != test.ToString())
return;
Reset();
GetApiParams();
}
#endregion
private void GetApiParams()
{
#if UNITY_EDITOR
if (!GameManager.Instance)
{
Init();
return;
}
#endif
Debug.Log("called rt");
NextTestOutput nextTestOutput = GameManager.Instance.currentTest;
if (nextTestOutput.content != null && nextTestOutput.content.Count > 0)
{
foreach (var item in nextTestOutput.content[0].listInputParameter)
{
switch ((ePARAMETERS)item.id)
{
case ePARAMETERS.RT_ENABLE:
shouldEvaluateTest = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RT_DURATION:
reactionTestDuration = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_FREQ:
secondsInterval = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_MAX_COUNT:
maxDots = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_RADIUS_R1:
radiusR1 = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_RADIUS_R2:
radiusR2 = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_SIZE:
dotRadius = float.Parse(item.Value)/10f;
break;
case ePARAMETERS.RED_DOT_TIME:
secondsToDisaply = System.Convert.ToInt32(item.Value);
break;
}
}
Debug.Log("called rt "+ shouldEvaluateTest);
/*if (ShouldEvaluateTest)
{
Init();
}*/
Init();//dopo bisogna sistemare il shoudl evaluatetest
}
}
Coroutine displayCoroutine;
public void Init()
{
endTime = Time.time + reactionTestDuration + 10;
SetRedDotSize();
displayCoroutine = StartCoroutine(RedDotDisplay());
}
private IEnumerator RedDotDisplay()
{
yield return new WaitForSeconds(2);
while (true)
{
SetRandomDotPosition();
RedDot(true);
isReactionAlreadyGiven = false;
dotDisaplyAtTime = Time.time;
currentDotsCount++;
yield return new WaitForSeconds(secondsToDisaply);
if(!isReactionAlreadyGiven)
{
missedSignals++;
}
RedDot(false);
if ((reactionTestDuration > 0 && endTime <= Time.time) || (maxDots > 0 && currentDotsCount >= maxDots))
break;
float waitTime = secondsInterval - secondsToDisaply;
yield return new WaitForSeconds(waitTime);
}
}
private void Update()
{
if (!ShouldEvaluateTest)
return;
if (!isReactionAlreadyGiven && /*redDot.activeSelf &&*/ (LogitechGSDK.LogiIsConnected(0) && LogitechGSDK.LogiButtonIsPressed(0, 23)))
{
isReactionAlreadyGiven = true;
float reactionTime = Time.time - dotDisaplyAtTime;
Debug.Log("Reaction Time RT : " + reactionTime);
RedDot(false);
dotDisplayReactTime.Add(reactionTime);
}
}
public double GetReactionTimeAvg()
{
double avg = 0;
foreach (var item in dotDisplayReactTime)
{
avg += item;
}
//avg / currentDotsCount
return avg / (float)dotDisplayReactTime.Count;
}
public double GetMissedSignals()
{
return ((float) missedSignals / (float) currentDotsCount) * 100;
}
private void RedDot(bool state)
{
//redDot.SetActive(state);
textAnsia2.SetText("Pippo 2");
}
private void SetRedDotSize()
{
//redDot.transform.localScale *= dotRadius;
textAnsia2.transform.localScale *= dotRadius;
textAnsia3.transform.localScale *= dotRadius;
}
private void SetRandomDotPosition()
{
//redDot.GetComponent<RectTransform>().anchoredPosition = GetRandomPointBetweenTwoCircles(radiusR1, radiusR2)*scale;
float scale = ((Screen.height*0.9f) / radiusR2) * 0.9f;
Vector3 pos = GetRandomPointBetweenTwoCircles(radiusR1, radiusR2) * scale;
Debug.Log("RT temp pos : " + pos);
pos = new Vector3(500, 500, 0);
Debug.Log("RT pos : " + pos);
// redDot.transform.position = pos;
Vector3 pos2 = new Vector3(20, 20, 0);
Debug.Log("text ansia 2 : " + pos2);
textAnsia2.transform.position = pos2;
textAnsia3.transform.position = pos;
}
#region Getting Red Dot b/w 2 cricles
/*
Code from : https://gist.github.com/Ashwinning/89fa09b3aa3de4fd72c946a874b77658
*/
/// <summary>
/// Returns a random point in the space between two concentric circles.
/// </summary>
/// <param name="minRadius"></param>
/// <param name="maxRadius"></param>
/// <returns></returns>
Vector3 GetRandomPointBetweenTwoCircles(float minRadius, float maxRadius)
{
//Get a point on a unit circle (radius = 1) by normalizing a random point inside unit circle.
Vector3 randomUnitPoint = Random.insideUnitCircle.normalized;
//Now get a random point between the corresponding points on both the circles
return GetRandomVector3Between(randomUnitPoint * minRadius, randomUnitPoint * maxRadius);
}
/// <summary>
/// Returns a random vector3 between min and max. (Inclusive)
/// </summary>
/// <returns>The <see cref="UnityEngine.Vector3"/>.</returns>
/// <param name="min">Minimum.</param>
/// <param name="max">Max.</param>
Vector3 GetRandomVector3Between(Vector3 min, Vector3 max)
{
return min + Random.Range(0, 1) * (max - min);
}
#endregion
#region Reset
private void Reset()
{
if (displayCoroutine != null)
StopCoroutine(displayCoroutine);
RedDot(false);
shouldEvaluateTest = 0;
reactionTestDuration = 0;
secondsInterval = 0;
missedSignals = 0;
maxDots = 0;
radiusR1 = 0;
radiusR2 = 0;
dotRadius = 1;
secondsToDisaply = 0;
endTime = 0;
dotDisaplyAtTime = 0;
dotDisplayReactTime.Clear();
//redDot.transform.localScale = Vector3.one;
textAnsia2.transform.localScale = Vector3.one;
textAnsia3.transform.localScale = Vector3.one;
currentDotsCount = 0;
isReactionAlreadyGiven = true;
}
#endregion
}
}
When "SetRandomDotPosition" method is called, in my scene I can display in a random position of the screen the RedDot (that is a simple image with a red dot), but I m not able to display my textAnsia2. How can I fixed it ?
I hope this will help you to get started :
https://vimeo.com/709527359
the scripts:
https://imgur.com/a/csNKwEh
I hope that in the video i covered every step that i've made.
Edit: The only thing is for you to do is to make the input of the Wheel to work on the slider. Try to make a simple script: when the text is enabled then the input from a joystick to work on that . When is disabled switch it back for his purpose .
I am making a simple voice visualization program. My goals are:
Playback microphone input
Visualize voice spectrum and gain in real time
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VisualizeVoice : MonoBehaviour
{
private const int NUM_SPECTRUM_SAMPLES = 256;
private const int NUM_SPECTRUM_BARS = 32;
private const int NUM_PCM_SAMPLES = 16000;
private const float BAR_DROP_SPEED = 1e-3f;
private const int NUM_SAMPLES_TO_AVERAGE = 8;
private string _deviceName;
private float[] _spectrumData = new float[NUM_SPECTRUM_SAMPLES];
private float[] _fPCMData = new float[NUM_PCM_SAMPLES];
private float _gain = 0;
private AudioClip _audio; // Audio from microphone
private AudioSource _playback; // To play the audio from microphone
// For visualization
private GameObject[] _spectrumBars = new GameObject[NUM_SPECTRUM_BARS];
private GameObject _gainBar;
// Start is called before the first frame update
void Start()
{
if (Microphone.devices.Length == 0) {
Debug.LogError("No Microphone");
return;
}
_deviceName = Microphone.devices[0];
Debug.Log("Current microphone is " + _deviceName);
if ((_playback = this.GetComponent<AudioSource>()) == null) {
_playback = this.gameObject.AddComponent<AudioSource>();
}
_playback.loop = true;
_playback.bypassEffects = true;
_playback.bypassListenerEffects = true;
_playback.bypassReverbZones = true;
_playback.priority = 0;
_playback.pitch = 1;
_playback.clip = _audio = Microphone.Start(_deviceName, true, 1, AudioSettings.outputSampleRate);
// Sync microphone and playback, but it always fails
float waitTime = 0;
while (!(Microphone.GetPosition(_deviceName) > 0) && waitTime <= 2)
waitTime += Time.deltaTime;
if (waitTime > 2) {
Debug.LogError("time out waiting for microphone");
}
_playback.Play();
InitVisualization();
}
// Update is called once per frame
void Update()
{
// Get PCM data and calculate gain
var audioPosition = Microphone.GetPosition(_deviceName);
_audio.GetData(_fPCMData, audioPosition);
UpdateGain();
// Get spectrum data
_playback.GetSpectrumData(_spectrumData, 0, FFTWindow.BlackmanHarris);
// Update visualization
UpdateVisualization();
}
private void InitVisualization()
{
// Initialize spectrum bars
for (int ibar = 0; ibar < NUM_SPECTRUM_BARS; ibar++) {
_spectrumBars[ibar] = GameObject.CreatePrimitive(PrimitiveType.Cube);
_spectrumBars[ibar].transform.parent = this.transform;
_spectrumBars[ibar].transform.localPosition = new Vector3(ibar, 0, 0);
_spectrumBars[ibar].transform.localScale = new Vector3(1, 0, 1);
}
// Initialize gain bar
_gainBar = GameObject.CreatePrimitive(PrimitiveType.Cube);
_gainBar.transform.parent = this.transform;
_gainBar.transform.localPosition = new Vector3(-5, 0, 0);
_gainBar.transform.localScale = new Vector3(4, 0, 1);
// Overall dimension
this.transform.localScale = new Vector3(0.2f, 10.0f, 0.2f);
}
private void UpdateVisualization()
{
// Update spectrum bars
int nSamplesPerBar = NUM_SPECTRUM_SAMPLES / NUM_SPECTRUM_BARS;
for (int ibar = 0; ibar < NUM_SPECTRUM_BARS; ibar++) {
// Calculate value of each bar
float value = 0;
for (int isample = 0; isample < nSamplesPerBar; isample++) {
value += _spectrumData[ibar * nSamplesPerBar + isample];
}
value /= nSamplesPerBar;
// Use current value if increasing, or slowly drop previous value if decreasing
float prevValue = _spectrumBars[ibar].transform.localScale.y;
if (value < prevValue)
value = prevValue - BAR_DROP_SPEED;
// Y scale is set to value
_spectrumBars[ibar].transform.localScale = new Vector3(1, value, 1);
}
// Update gain bar
_gainBar.transform.localScale = new Vector3(4, _gain, 1);
}
private void UpdateGain()
{
_gain = 0;
for(int i = 0; i < NUM_SAMPLES_TO_AVERAGE; i++) {
_gain += Mathf.Abs(_fPCMData[NUM_PCM_SAMPLES - i - 1]);
}
_gain /= NUM_SAMPLES_TO_AVERAGE;
}
}
Here are my questions:
I can't use while (!Microphone.GetPosition(_deviceName) > 0)); to avoid latency from microphone to speaker. If I use it, my application just freezes. If I add code to allow time-out, it has time-out every time.
The gain bar seems irrelevant with my voice. I don't know if my calculation is right.
I'm not sure if I need to average over multiple samples calculating gains, and how many samples I need to average over. I need this gain value later to detect silent moments and cut audio data.
To 1.
You can. Unity allows to define Start as a Coroutine
private IEnumerator Start()
{
...
}
On this way you can use a non blocking
while (!Microphone.GetPosition(_deviceName) > 0))
{
yield return null;
}
I am wondering if it's possible to start the Watchface Service from an activity?
I tried to start the service in the onCreate method of my activity but it does not show the Watchface:
Intent serviceIntent = new Intent(this, CustomWatchFaceService);
startService(serviceIntent);
update
Here is the code for the WatchfaceService
public class CustomWatchFaceService extends CanvasWatchFaceService {
private static final String TAG = "DigitalWatchFaceService";
private static final Typeface BOLD_TYPEFACE =
Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
private static final Typeface NORMAL_TYPEFACE =
Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
private static final long NORMAL_UPDATE_RATE_MS = 500;
private static final long MUTE_UPDATE_RATE_MS = TimeUnit.MINUTES.toMillis(1);
#Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
static final String COLON_STRING = ":";
static final int MUTE_ALPHA = 100;
static final int NORMAL_ALPHA = 255;
static final int MSG_UPDATE_TIME = 0;
long mInteractiveUpdateRateMs = NORMAL_UPDATE_RATE_MS;
final Handler mUpdateTimeHandler = new Handler() {
#Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_TIME:
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "updating time");
}
invalidate();
if (shouldTimerBeRunning()) {
long timeMs = System.currentTimeMillis();
long delayMs =
mInteractiveUpdateRateMs - (timeMs % mInteractiveUpdateRateMs);
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
}
break;
}
}
};
Paint mBackgroundPaint;
Bitmap mBackgroundBitmap;
Bitmap wifiIconOn;
Bitmap wifiIconOff;
Paint mDatePaint;
Paint mNotificationPaint;
Paint mNotificationMax;
Paint mNotificationHigh;
Paint mHourPaint;
Paint mMinutePaint;
Paint mSecondPaint;
Paint mAmPmPaint;
Paint mColonPaint;
float mColonWidth;
boolean mMute;
Calendar mCalendar;
Date mDate;
SimpleDateFormat mDayOfWeekFormat;
java.text.DateFormat mDateFormat;
boolean mShouldDrawColons;
float mXOffset;
float mYOffset;
float mLineHeight;
int mInteractiveBackgroundColor =
R.color.interactive_bg;
int mInteractiveNotificationMax =
R.color.notification_max;
int mInteractiveNotificationHigh =
R.color.notification_high;
int mInteractiveNotificationColor =
R.color.notification;
int mInteractiveHourDigitsColor =
R.color.interactive_time;
int mInteractiveMinuteDigitsColor =
R.color.interactive_time;
int mInteractiveSecondDigitsColor =
R.color.interactive_time;
boolean mLowBitAmbient;
#Override
public void onCreate(SurfaceHolder holder) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onCreate");
}
super.onCreate(holder);
Locale locale = new Locale("de");
Locale.setDefault(locale);
Configuration config = getResources().getConfiguration();
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
setWatchFaceStyle(new WatchFaceStyle.Builder(CustomWatchFaceService.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
Resources resources = CustomWatchFaceService.this.getResources();
mYOffset = resources.getDimension(R.dimen.digital_y_offset);
mLineHeight = resources.getDimension(R.dimen.digital_line_height);
setInteractiveColors();
// Not sure why the text color needs to be set here again ... it should be set in setDefaultColors()!
mDatePaint.setColor(getColor(R.color.digital_date));
mNotificationPaint.setColor(getColor(R.color.notification));
mNotificationMax.setColor(getColor(R.color.notification_max));
mNotificationHigh.setColor(getColor(R.color.notification_high));
mHourPaint.setColor(getColor(R.color.interactive_time));
mMinutePaint.setColor(getColor(R.color.interactive_time));
mSecondPaint.setColor(getColor(R.color.interactive_time));
mColonPaint.setColor(getColor(R.color.interactive_time));
//Images should be loaded here so they can be called during the Draw Method
wifiIconOn = BitmapFactory.decodeResource(CustomWatchFaceService.this.getResources(), R.drawable.wifi_on_small);
wifiIconOff = BitmapFactory.decodeResource(CustomWatchFaceService.this.getResources(), R.drawable.wifi_off_small);
mBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.customcart_logo_240_alpha);
mCalendar = Calendar.getInstance();
mDate = new Date();
initFormats();
}
public void setInteractiveColors() {
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(getColor(mInteractiveBackgroundColor));
mNotificationPaint = createTextPaint(mInteractiveNotificationColor);
mNotificationMax = createTextPaint(mInteractiveNotificationMax);
mNotificationHigh = createTextPaint(mInteractiveNotificationHigh);
mDatePaint = createTextPaint(R.color.digital_date);
mHourPaint = createTextPaint(mInteractiveHourDigitsColor, BOLD_TYPEFACE);
mMinutePaint = createTextPaint(mInteractiveMinuteDigitsColor);
mSecondPaint = createTextPaint(mInteractiveSecondDigitsColor);
mColonPaint = createTextPaint(R.color.digital_colons);
}
#Override
public void onDestroy() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
super.onDestroy();
}
private Paint createTextPaint(int defaultInteractiveColor) {
return createTextPaint(defaultInteractiveColor, NORMAL_TYPEFACE);
}
private Paint createTextPaint(int defaultInteractiveColor, Typeface typeface) {
Paint paint = new Paint();
paint.setColor(defaultInteractiveColor);
paint.setTypeface(typeface);
paint.setAntiAlias(true);
return paint;
}
#Override
public void onVisibilityChanged(boolean visible) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onVisibilityChanged: " + visible);
}
super.onVisibilityChanged(visible);
updateTimer();
}
private void initFormats() {
mDayOfWeekFormat = new SimpleDateFormat("EEEE", Locale.getDefault());
mDayOfWeekFormat.setCalendar(mCalendar);
mDateFormat = DateFormat.getDateFormat(CustomWatchFaceService.this);
mDateFormat.setCalendar(mCalendar);
}
#Override
public void onApplyWindowInsets(WindowInsets insets) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square"));
}
super.onApplyWindowInsets(insets);
// Load resources that have alternate values for round watches.
Resources resources = CustomWatchFaceService.this.getResources();
boolean isRound = insets.isRound();
mXOffset = resources.getDimension(isRound
? R.dimen.digital_x_offset_round : R.dimen.digital_x_offset);
float textSize = resources.getDimension(isRound
? R.dimen.digital_text_size_round : R.dimen.digital_text_size);
float notificationTextSize = resources.getDimension(isRound
? R.dimen.notification_text_size : R.dimen.notification_text_size);
mDatePaint.setTextSize(resources.getDimension(R.dimen.digital_date_text_size));
mHourPaint.setTextSize(textSize);
mMinutePaint.setTextSize(textSize);
mSecondPaint.setTextSize(textSize);
mColonPaint.setTextSize(textSize);
mNotificationPaint.setTextSize(notificationTextSize);
mColonWidth = mColonPaint.measureText(COLON_STRING);
}
#Override
public void onPropertiesChanged(Bundle properties) {
super.onPropertiesChanged(properties);
boolean burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
mHourPaint.setTypeface(burnInProtection ? NORMAL_TYPEFACE : BOLD_TYPEFACE);
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection
+ ", low-bit ambient = " + mLowBitAmbient);
}
}
#Override
public void onTimeTick() {
super.onTimeTick();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
}
invalidate();
}
#Override
public void onAmbientModeChanged(boolean inAmbientMode) {
super.onAmbientModeChanged(inAmbientMode);
if (!isInAmbientMode()) {
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(getColor(R.color.interactive_bg));
mDatePaint.setColor(getColor(R.color.digital_date));
mHourPaint.setColor(getColor(R.color.interactive_time));
mMinutePaint.setColor(getColor(R.color.interactive_time));
mSecondPaint.setColor(getColor(R.color.interactive_time));
mColonPaint.setColor(getColor(R.color.interactive_time));
}
else {
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(getColor(R.color.ambient_bg));
mDatePaint.setColor(getColor(R.color.digital_date));
mHourPaint.setColor(getColor(R.color.ambient_time));
mMinutePaint.setColor(getColor(R.color.ambient_time));
mSecondPaint.setColor(getColor(R.color.ambient_time));
mColonPaint.setColor(getColor(R.color.ambient_time));
}
//Log.d("XXX", "onAmbientModeChanged: " + inAmbientMode);
if (mLowBitAmbient) {
boolean antiAlias = !inAmbientMode;
mDatePaint.setAntiAlias(antiAlias);
mHourPaint.setAntiAlias(antiAlias);
mMinutePaint.setAntiAlias(antiAlias);
mSecondPaint.setAntiAlias(antiAlias);
mAmPmPaint.setAntiAlias(antiAlias);
mColonPaint.setAntiAlias(antiAlias);
}
invalidate();
// Whether the timer should be running depends on whether we're in ambient mode (as well
// as whether we're visible), so we may need to start or stop the timer.
updateTimer();
}
#Override
public void onInterruptionFilterChanged(int interruptionFilter) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onInterruptionFilterChanged: " + interruptionFilter);
}
super.onInterruptionFilterChanged(interruptionFilter);
boolean inMuteMode = interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE;
// We only need to update once a minute in mute mode.
setInteractiveUpdateRateMs(inMuteMode ? MUTE_UPDATE_RATE_MS : NORMAL_UPDATE_RATE_MS);
if (mMute != inMuteMode) {
mMute = inMuteMode;
int alpha = inMuteMode ? MUTE_ALPHA : NORMAL_ALPHA;
mDatePaint.setAlpha(alpha);
mHourPaint.setAlpha(alpha);
mMinutePaint.setAlpha(alpha);
mColonPaint.setAlpha(alpha);
mAmPmPaint.setAlpha(alpha);
invalidate();
}
}
public void setInteractiveUpdateRateMs(long updateRateMs) {
if (updateRateMs == mInteractiveUpdateRateMs) {
return;
}
mInteractiveUpdateRateMs = updateRateMs;
// Stop and restart the timer so the new update rate takes effect immediately.
if (shouldTimerBeRunning()) {
updateTimer();
}
}
private String formatTwoDigitNumber(int hour) {
return String.format("%02d", hour);
}
#Override
public void onDraw(Canvas canvas, Rect bounds) {
long now = System.currentTimeMillis();
int width = bounds.width();
int height = bounds.height();
mCalendar.setTimeInMillis(now);
mDate.setTime(now);
boolean is24Hour = DateFormat.is24HourFormat(CustomWatchFaceService.this);
// Draw the background.
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
//Draw the background Image
if (mBackgroundBitmap == null
|| mBackgroundBitmap.getWidth() != width
|| mBackgroundBitmap.getHeight() != height) {
mBackgroundBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
width, height, true /* filter */);
}
//Log.d("XXX", "Width: "+ mBackgroundBitmap.getWidth() + "Height: "+mBackgroundBitmap.getHeight() );
if (isInAmbientMode() && (mLowBitAmbient)) {
canvas.drawColor(Color.BLACK);
} else if (isInAmbientMode()) {
canvas.drawColor(Color.BLACK);
} else {
canvas.drawBitmap(mBackgroundBitmap, 0, 0, mBackgroundPaint);
}
// Show colons for the first half of each second so the colons blink on when the time updates.
mShouldDrawColons = (System.currentTimeMillis() % 1000) < 500;
// Draw the hours.
float x = mXOffset;
String hourString;
if (is24Hour) {
hourString = formatTwoDigitNumber(mCalendar.get(Calendar.HOUR_OF_DAY));
} else {
int hour = mCalendar.get(Calendar.HOUR);
if (hour == 0) {
hour = 12;
}
hourString = String.valueOf(hour);
}
canvas.drawText(hourString, x, mYOffset, mHourPaint);
x += mHourPaint.measureText(hourString);
// In ambient and mute modes, always draw the first colon. Otherwise, draw the
// first colon for the first half of each second.
if (isInAmbientMode() || mMute || mShouldDrawColons) {
canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
}
x += mColonWidth;
// Draw the minutes.
String minuteString = formatTwoDigitNumber(mCalendar.get(Calendar.MINUTE));
canvas.drawText(minuteString, x, mYOffset, mMinutePaint);
x += mMinutePaint.measureText(minuteString);
// In unmuted interactive mode, draw a second blinking colon followed by the seconds.
// Otherwise, if we're in 12-hour mode, draw AM/PM
if (!isInAmbientMode() && !mMute) {
if (mShouldDrawColons) {
canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
}
x += mColonWidth;
canvas.drawText(formatTwoDigitNumber(
mCalendar.get(Calendar.SECOND)), x, mYOffset, mSecondPaint);
} else if (!is24Hour) {
x += mColonWidth;
}
// Only render the day of week and date if there is no peek card, so they do not bleed
// into each other in ambient mode.
if (getPeekCardPosition().isEmpty()) {
// Day of week
canvas.drawText(
mDayOfWeekFormat.format(mDate),
mXOffset, mYOffset + mLineHeight, mDatePaint);
// Date
canvas.drawText(
mDateFormat.format(mDate),
mXOffset, mYOffset + mLineHeight * 2, mDatePaint);
}
}
/**
* Starts the {#link #mUpdateTimeHandler} timer if it should be running and isn't currently
* or stops it if it shouldn't be running but currently is.
*/
private void updateTimer() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "updateTimer");
}
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
if (shouldTimerBeRunning()) {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
/**
* Returns whether the {#link #mUpdateTimeHandler} timer should be running. The timer should
* only run when we're visible and in interactive mode.
*/
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
}
}
Yesterday as I was working on my game and I decided I wanted to include some sort of a video showing sneak peaks of the themes and tutorials and such. I then decided to check how to record my scene or nodes. After looking around i concluded there was no easy way to do it so I decided to make my own utility which I want to share with all of you.
Please know I am more or less a beginner and I havent been coding for long. I know it is probably not properly done and that it can be done in much better ways. Anyways here it goes.
/*
* JavaFX SceneCaptureUtility 2016/07/02
*
* The author of this software "Eudy Contreras" grants you ("Licensee")
* a non-exclusive, royalty free, license to use,modify and redistribute this
* software in source and binary code form.
*
* Please be aware that this software is simply part of a personal test
* and may in fact be unstable. The software in its current state is not
* considered a finished product and has plenty of room for improvement and
* changes due to the range of different approaches which can be used to
* achieved the desired result of the software.
*
* BEWARE that because of the nature of this software and because of the way
* this software functions the ability of this software to be able to operate
* without probable malfunction is strictly based on factors such as the amount
* of processing power the system running the software has, and the resolution of
* the screen being recorded. The amount of nodes on the scene will have an impact
* as well as the size and recording rate to which this software will be subjected
* to. IN CASE OF MEMORY RELATED PROBLEMS SUCH AS BUT NOT LIMITEd TO LACK OF REMAINING
* HEAP SPACE PLEASE CONSIDER LOWERING THE RESOLUTION OF THE SCENE BEING RECORDED.
*
* BEWARE STABILITY ISSUES MAY ARISE!
* BEWARE SAVING AND LOADING THE RECORDED VIDEO MAY TAKE TIME DEPENDING ON YOUR SYSTEM
*
* PLEASE keep track of the console for useful information and feedback
*/
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.util.Duration;
* #author Eudy Contreras
*
* This program records a javaFX scene by taking continuous snapshots of the scene
* which are than stored and saved to a predefined destination. The program allows the
* user to save and retrieve the frame based videos make by this program which can
* then be play on a specified layer or "screen".
public class SceneCaptureUtility {
private int frame = 0; // The current frame which is being displayed
private int timer = 0; // recording timer.
private int record_time; // Amount of time the recorder will record
private int counter = 0;
private int width;
private int height;
private float frameRate = 60.0f;
private long capture_rate = (long) (1000f / frameRate); // Rate at which the recorder will recored. Default rate: 60FPS
private PlaybackSettings playSettings;
private Double frameCap = 1.0 / frameRate; // Framerate at which the recorded video will play
private Double video_size_scale = 1.0; // Scale of the video relative to its size: 0.5 = half, 2.0 = double the size
private Double bounds_scale_X = 0.5; // Scale factor for scaling relative to assigned or obtained resolution
private Double bounds_scale_Y = 0.5;
private Boolean saveFrames = false; //If true saves the individual frames of the video as images
private Boolean loadFrames = false; //If true allows retrieving previously saved frames for playback
private Boolean allowRecording = false;
private Boolean allowPlayback = false;
private Boolean showIndicators = false;
private Pane indicator_layer;
private Pane video_screen;
private Scene scene;
private Timeline videoPlayer;
private ArrayList<Image> recorded_frames; //Stores recorded frames
private ArrayList<ImageView> video_frames; //Stores frames for playback
private ArrayList<byte[]> temp_frames; //Stores frames for saving
private final SnapshotParameters parameters = new SnapshotParameters();
private final ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
private final Indicator recording = new Indicator(Color.RED, " Recording..");
private final Indicator playing = new Indicator(Color.GREEN, " Playing..");
private final Indicator idle = new Indicator(Color.YELLOW, "paused..");
private final String VIDEO_NAME = "recording4.FXVideo";
private final String FRAME_NAME = "image";
private final String DIRECTORY_NAME = "Snake Game Videos"+ File.separator;
private final String PATH_ROOT = System.getProperty("user.home") + "/Desktop" + File.separator +DIRECTORY_NAME;
private final String FILE_EXTENSION = "jpg";
private final String PATH_FRAME = PATH_ROOT + FRAME_NAME;
private final String PATH_VIDEO = PATH_ROOT + VIDEO_NAME;
/**
* Constructs a scene capture utility with a default scene, a pane which
* will be used to diplay the state indicators, the amount of time which
* the recorder will be running and a condition to whether or not the indicators
* will be shown.
* #param scene: scene which will be recored.
* #param indicatorLayer: layer which will be used to show the state indicators.
* #param record_time: time in minutes for which the recorder will be recording
* #param showIndicators: condition which determines if the indicators will be shown.
*/
public SceneCaptureUtility(Scene scene, Pane indicatorLayer, int record_time, boolean showIndicators) {
this.scene = scene;
this.width = (int) scene.getWidth();
this.height = (int) scene.getHeight();
this.showIndicators = showIndicators;
this.record_time = record_time * 60;
this.initStorages(indicatorLayer);
this.loadRecording();
this.scaleResolution(0, 0, false);
}
/*
* Initializes the list used to store the captured frames.
*/
private void initStorages(Pane layer) {
if(showIndicators)
this.indicator_layer = layer;
video_frames = new ArrayList<ImageView>();
recorded_frames = new ArrayList<Image>();
temp_frames = new ArrayList<byte[]>();
}
/**
* loads recordings and or frames from a specified location
*/
private void loadRecording(){
if (loadFrames) {
loadFromFile();
} else {
retrieveRecording();
}
}
/*
* Resets the list
*/
private void resetStorage() {
if (video_frames != null)
video_frames.clear();
if (recorded_frames != null)
recorded_frames.clear();
if (video_screen != null)
video_screen.getChildren().clear();
}
/**
* Method which when called will start recording the given scene.
*/
public void startRecorder() {
if (!allowRecording) {
resetStorage();
if(showIndicators)
showIndicator(indicator_layer.getChildren(), recording);
videoRecorder();
allowRecording(true);
logState("Recording...");
}
}
/**
* Method which when called will stop the recording
*/
public void stopRecorder() {
if (allowRecording) {
if(showIndicators)
showIndicator(indicator_layer.getChildren(), idle);
allowRecording(false);
logState("Recording stopped");
logState("Amount of recorded frames: " + recorded_frames.size());
processVideo();
saveVideo();
}
}
/**
* Method which when called will start playback of the recorded video onto
* a given screen or layer.
* #param output_screen: layer used to display the video
* #param settings: video settings that determine the playback conditions.
*/
public void starPlayer(Pane output_screen, PlaybackSettings settings) {
video_screen = output_screen;
playSettings = settings;
if(showIndicators)
showIndicator(indicator_layer.getChildren(), playing);
if (video_frames.size() > 0) {
logState("Video playback..");
resetPlayback();
if (videoPlayer == null)
videoPlayer();
else {
videoPlayer.play();
}
allowPlayback(true);
}
else{
logState("Nothing to play!");
}
}
/**
* Method which when called will stop the playback of the video
*/
public void stopPlayer() {
if(showIndicators)
showIndicator(indicator_layer.getChildren(), idle);
if (videoPlayer != null)
videoPlayer.stop();
logState("Playback stopped");
allowPlayback(false);
}
/*
* Method which creates a task which records the video at
* a specified rate for a specifed time
*/
private void videoRecorder() {
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
while (true) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (allowRecording && record_time > 0) {
recorded_frames.add(create_frame());
}
recordingTimer();
}
});
Thread.sleep(capture_rate);
}
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/*
* Method which creates a timeline which plays the video
* at a specified frameRate onto a given screen or layer.
*/
private void videoPlayer() {
videoPlayer = new Timeline();
videoPlayer.setCycleCount(Animation.INDEFINITE);
KeyFrame keyFrame = new KeyFrame(Duration.seconds(frameCap),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
if (allowPlayback) {
playbackVideo();
}
}
});
videoPlayer.getKeyFrames().add(keyFrame);
videoPlayer.play();
}
**/**
* Calls to this method will decreased the time left on the
* recording every second until the recording time reaches
* zero. this will cause the recording to stop.
*/**
private void recordingTimer() {
timer++;
if (allowRecording && timer >= frameRate) {
record_time -= 1;
timer = 0;
if (record_time <= 0) {
record_time = 0;
}
}
}
/**
* A call to this method will add the recorded frames to the video list
* making them reading for playback.
*/
private void processVideo() {
logState("Processing video...");
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
for (int i = 0; i < recorded_frames.size(); i++) {
video_frames.add(new ImageView(recorded_frames.get(i)));
}
logState("Video has been processed.");
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* Call to this method will play the video on the given screen
* adding a removing frames.
* #return: screen in which the frames are being rendered
*/
private final Pane playbackVideo() {
if (video_screen.getChildren().size() > 0)
video_screen.getChildren().remove(0);
video_screen.getChildren().add(video_frames.get(frame));
frame += 1;
if (frame > video_frames.size() - 1) {
if(playSettings == PlaybackSettings.CONTINUOUS_REPLAY){
frame = 0;
}
else if(playSettings == PlaybackSettings.PLAY_ONCE){
frame = video_frames.size() - 1;
allowPlayback = false;
}
}
return video_screen;
}
public void setVideoScale(double scale) {
this.video_size_scale = scale;
}
/**
* A called to this method will scale the video
* to a given scale.
* #param scale: new scale of the video. 1.0 is normal
* 0.5 is half and 2.0 is twice the size.
*/
public void scaleVideo(double scale) {
this.video_size_scale = scale;
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
if (video_frames.size() > 0) {
logState("Scaling video...");
for (int i = 0; i < video_frames.size(); i++) {
video_frames.get(i).setFitWidth(video_frames.get(i).getImage().getWidth() * video_size_scale);
video_frames.get(i).setFitHeight(video_frames.get(i).getImage().getHeight() * video_size_scale);
}
logState("Video has been scaled!");
}
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* A called to this method will attempt to prepare the video and or frames
* for saving
*/
private void saveVideo() {
File root = new File(PATH_ROOT);
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
root.mkdirs();
for (int i = 0; i < recorded_frames.size(); i++) {
saveToFile(recorded_frames.get(i));
}
saveRecording(temp_frames);
logState("Amount of compiled frames: " + temp_frames.size());
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* A called to this method will add the frames store is array list
* to the video list.
* #param list: list containing the byte arrays of the frames
*/
private void loadFrames(ArrayList<byte[]> list) {
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
logState("loading frames...");
for (int i = 0; i < list.size(); i++) {
video_frames.add(byteToImage(list.get(i)));
}
logState("frames have been added!");
scaleVideo(video_size_scale);
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* Method which when called will add and display a indicator.
* #param rootPane: list to which the indicator will be added
* #param indicator: indicator to be displayed
*/
private void showIndicator(ObservableList<Node> rootPane, Indicator indicator) {
rootPane.removeAll(playing, idle, recording);
indicator.setTranslateX(width - ScaleX(150));
indicator.setTranslateY(ScaleY(100));
rootPane.add(indicator);
}
/**
* Calls to this method will save each frame if conditions are met
* and will also store each frame into a list of byte arrays.
* #param image: image to be saved to file and or converted and store as
* a byte array.
*/
private void saveToFile(Image image) {
counter += 1;
BufferedImage BImage = SwingFXUtils.fromFXImage(image, null);
temp_frames.add(ImageToByte(BImage));
if (saveFrames) {
File video = new File(PATH_FRAME + counter + "." + FILE_EXTENSION);
try {
ImageIO.write(BImage, FILE_EXTENSION, video);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* Method which when called loads images from a predefined
* directory in order to play them as a video.
*/
private void loadFromFile() {
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
for (int i = 1; i < 513; i++) {
File video = new File(PATH_FRAME + i + "." + FILE_EXTENSION);
video_frames.add(new ImageView(new Image(video.toURI().toString())));
}
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* Method which when called will attemp to save the video
* to a specified directory.
* #param list
*/
private void saveRecording(ArrayList<byte[]> list) {
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
File root = new File(PATH_ROOT);
File video = new File(PATH_VIDEO);
video.delete();
logState("Saving video...");
try {
root.mkdirs();
FileOutputStream fileOut = new FileOutputStream(PATH_VIDEO);
BufferedOutputStream bufferedStream = new BufferedOutputStream(fileOut);
ObjectOutputStream outputStream = new ObjectOutputStream(bufferedStream);
outputStream.writeObject(list);
outputStream.close();
fileOut.close();
logState("Video saved.");
} catch (IOException e) {
logState("Failed to save, I/O exception");
e.printStackTrace();
}
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* Method which when called attempts to retrieve the video
* from a specified directory
*/
#SuppressWarnings("unchecked")
private void retrieveRecording() {
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
File root = new File(PATH_ROOT);
File video = new File(PATH_VIDEO);
if (root.exists() && video.exists()) {
try {
FileInputStream fileIn = new FileInputStream(PATH_VIDEO);
ObjectInputStream inputStream = new ObjectInputStream(fileIn);
temp_frames = (ArrayList<byte[]>) inputStream.readObject();
inputStream.close();
fileIn.close();
logState("\nLoading video");
loadFrames(temp_frames);
} catch (IOException | ClassNotFoundException e) {
logState("Failed to load! " + e.getLocalizedMessage());
}
} else {
logState("Nothing to load.");
}
return null;
}
};
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
/**
* Method which when call creates a frame or snapshot of the
* given scene to be recorded.
* #return: frame taken from the scene.
*/
private synchronized Image create_frame() {
WritableImage wi = new WritableImage(width, height);
if (scene != null)
scene.snapshot(wi);
try {
return wi;
} finally {
wi = null;
}
}
/**
* Method which when called crates a frame or snapshot of the
* given node.
* #param node: node to be recorded
* #return: image or frame of recorded node.
*/
#SuppressWarnings("unused")
private synchronized Image create_node_frame(Node node) {
parameters.setFill(Color.TRANSPARENT);
WritableImage wi = new WritableImage((int)node.getBoundsInLocal().getWidth(), (int) node.getBoundsInLocal().getHeight());
node.snapshot(parameters, wi);
return wi;
}
/**
* Method which when called will create a scale relative to a base
* and current resolution.
* #param scaleX: x scaling factor used for manual scaling
* #param scaleY: y scaling factor used for manual scaling
* #param manualScaling: determines if a manual scaling will be applied or not
*/
public void scaleResolution(double scaleX, double scaleY, boolean manualScaling) {
double resolutionX = Screen.getPrimary().getBounds().getWidth();
double resolutionY = Screen.getPrimary().getBounds().getHeight();
double baseResolutionX = 1920;
double baseResolutionY = 1080;
bounds_scale_X = baseResolutionX / resolutionX;
bounds_scale_Y = baseResolutionY / resolutionY;
if(manualScaling==true){
bounds_scale_X = bounds_scale_X*scaleX;
bounds_scale_Y = bounds_scale_Y*scaleY;
}
}
public void allowRecording(boolean state) {
allowRecording = state;
logState("allowed recording: " + state);
}
public void allowPlayback(boolean state) {
allowPlayback = state;
logState("allowed playback: " + state);
}
public void setLocation(double x, double y) {
video_screen.setTranslateX(x);
video_screen.setTranslateY(y);
}
public void setDimensions(double width, double height) {
video_screen.setPrefSize(width, height);;
}
public void resetPlayback() {
this.frame = 0;
}
public double Scale(double value) {
double newSize = value * (bounds_scale_X + bounds_scale_Y)/2;
return newSize;
}
public double ScaleX(double value) {
double newSize = value * bounds_scale_X;
return newSize;
}
public double ScaleY(double value) {
double newSize = value * bounds_scale_Y;
return newSize;
}
public double getVideoWidth(){
if(!video_frames.isEmpty())
return video_frames.get(0).getImage().getWidth() * video_size_scale;
else{
return 0;
}
}
public double getVideoHeight(){
if(!video_frames.isEmpty())
return video_frames.get(0).getImage().getWidth() * video_size_scale;
else{
return 0;
}
}
#SuppressWarnings("unused")
private String loadResource(String image) {
String url = PATH_ROOT + image;
return url;
}
/**
* Method which converts a bufferedimage to byte array
* #param image: image to be converted
* #return: byte array of the image
*/
public final byte[] ImageToByte(BufferedImage image) {
byte[] imageInByte = null;
try {
if (image != null) {
ImageIO.write(image, FILE_EXTENSION, byteOutput);
imageInByte = byteOutput.toByteArray();
byteOutput.flush();
}
} catch (IOException | IllegalArgumentException e) {
e.printStackTrace();
}
try {
return imageInByte;
} finally {
byteOutput.reset();
}
}
/**
* Method which converts a byte array to a Imageview
* #param data: byte array to be converted.
* #return: imageview of the byte array
*/
public final ImageView byteToImage(byte[] data) {
BufferedImage newImage = null;
ImageView imageView = null;
Image image = null;
try {
InputStream inputStream = new ByteArrayInputStream(data);
newImage = ImageIO.read(inputStream);
inputStream.close();
image = SwingFXUtils.toFXImage(newImage, null);
imageView = new ImageView(image);
} catch (IOException e) {
e.printStackTrace();
}
return imageView;
}
private void logState(String state) {
System.out.println("JAVA_FX SCREEN RECORDER: " + state);
}
public enum PlaybackSettings {
CONTINUOUS_REPLAY, PLAY_ONCE,
}
/**
* Class which crates a simple indicator which can be
* used to display a recording or playing state
* #author Eudy Contreras
*
*/
private class Indicator extends HBox {
public Indicator(Color color, String message) {
Circle indicator = new Circle(Scale(15), color);
Text label = new Text(message);
label.setFont(Font.font("", FontWeight.EXTRA_BOLD, Scale(20)));
label.setFill(Color.WHITE);
setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, null, null)));
getChildren().addAll(indicator, label);
}
}
}
If you have any suggestions of things that will make this program more effecient please share with me. Thanks
I'm working in Eclipse and using float for my variables and values. The only issue I am getting is the huge number of digits (1.563524354) after the decimal. Can I somehow limit them to one digit after the decimal like (1.5) or no digit or decimal at all simply like 1. ?
My code:
float nCurrentSpeed;
private long startTime;
private int count;
private int pointAverage;
private Float nMaxSpeed = 0F;
DecimalFormat df = new DecimalFormat("#");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LocationManager lm = (LocationManager) this
.getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
this.onLocationChanged(null);
}
#Override
public void onLocationChanged(Location location) {
TextView tv = (TextView) findViewById(R.id.textView1);
TextView tvd = (TextView) findViewById(R.id.textView5);
if (location == null) {
startTime = System.currentTimeMillis();
count = 0;
pointAverage = 0;
actualizeTextField();
tv.setText("0.0");
} else {
nCurrentSpeed = location.getSpeed();
tv.setText(nCurrentSpeed + "");
count += 1;
pointAverage += location.getSpeed();
actualizeTextField();
if (nMaxSpeed < nCurrentSpeed) {
nMaxSpeed = nCurrentSpeed;
}
}
tvd.setText(nMaxSpeed.toString());
}
private void actualizeTextField() {
// TODO Auto-generated method stub
TextView tf = (TextView) findViewById(R.id.textView3);
if (count > 0) {
long timeOver = System.currentTimeMillis() - startTime;
tf.setText(String.valueOf(pointAverage / (timeOver / 1000)));
} else {
tf.setText("0.0");
}
}
DecimalFormat df = new DecimalFormat("#.#");
df.format(0.91231);
returns:
0.9
If you don't want decimal places, why don't you cast it to int then?
Like this :
double d = 1.234567;
DecimalFormat df1 = new DecimalFormat("#.##"); // set your format
System.out.print(df1.format(d))
If you don't need point values, why are you using float? Check this one.
float nCurrentSpeed;
private long startTime;
private int count;
private int pointAverage;
private int nMaxSpeed = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LocationManager lm = (LocationManager) this
.getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
this.onLocationChanged(null);
}
#Override
public void onLocationChanged(Location location) {
TextView tv = (TextView) findViewById(R.id.textView1);
TextView tvd = (TextView) findViewById(R.id.textView5);
if (location == null) {
startTime = System.currentTimeMillis();
count = 0;
pointAverage = 0;
actualizeTextField();
tv.setText("0.0");
} else {
nCurrentSpeed = location.getSpeed();
tv.setText(nCurrentSpeed + "");
count += 1;
pointAverage += location.getSpeed();
actualizeTextField();
if (nMaxSpeed < nCurrentSpeed) {
nMaxSpeed = (int)nCurrentSpeed;
}
}
tvd.setText(String.valueOf(nMaxSpeed));
}
private void actualizeTextField() {
// TODO Auto-generated method stub
TextView tf = (TextView) findViewById(R.id.textView3);
if (count > 0) {
long timeOver = System.currentTimeMillis() - startTime;
tf.setText(String.valueOf(pointAverage / (timeOver / 1000)));
} else {
tf.setText("0.0");
}
}