SharedPreferences in public class - can't find value - android-activity

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

How to display text in Unity game

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 .

Microphone, gain value and spectrum values do not sync using Unity

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;
}

Start Android Wear Watchface from Activity

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();
}
}
}

JavaFX scene recording

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

How to limit a float to give one or no value after the decimal

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");
}
}