Storing short[] in a file using Libgdx filehandle - libgdx

I record some audio using libgdx AudioRecorder which return short[]. I think I´ve converted it into byte[] instead so it can be stored in a file cause I need the audio to be able to playback anytime.
Here´s some variables I use:
final int samples = 44100;
boolean isMono = true;
final short[] data = new short[samples * 5];
final AudioRecorder recorder = Gdx.audio.newAudioRecorder(samples, isMono);
final AudioDevice player = Gdx.audio.newAudioDevice(samples, isMono);
And here I start and play the audio, also I think I´ve converted the short[] into byte[] , correct me if Im wrong.
public void startRecord(){
new Thread(new Runnable() {
#Override
public void run() {
System.out.println("Record start:");
recorder.read(data, 0, data.length);
recorder.dispose();
ShortBuffer buffer = BufferUtils.newShortBuffer(2);
buffer.put(data[1]);
}
}).start();
}
public void playRecorded(){
new Thread(new Runnable() {
#Override
public void run() {
player.writeSamples(data, 0, data.length);
player.dispose();
}
}).start();
}
Here´s an example on how I´ve stored byte[] before. But I can´t implement this teqhnique on this method. Bare in mind I need it to be a libgdx solution.
public void onImagePicked(final InputStream stream) {
loading = "Loading";
pending = executor.submit(new AsyncTask<Pixmap>() {
#Override
public Pixmap call() throws Exception {
StreamUtils.copyStream(stream, file.write(false));
final byte[] bytes = file.readBytes();
final Pixmap pix = new Pixmap(bytes, 0, bytes.length);
return pix;
}
});
}

Hi it is actually very simple. You can convert a byte array to short like this:
// get all bytes
byte[] temp = ...
// create short with half the length (short = 2 bytes)
short[] data = new short[temp.length / 2];
// cast a byte array to short array
ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(data);
//data now has the short array in it
You can convert a short array to byte like this:
// audio data
short[] data = ...;
//create a byte array to hold the data passed (short = 2 bytes)
byte[] temp = new byte[data.length * 2];
// cast a short array to byte array
ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(data);
// temp now has the byte array
Sample project
I created a sample project in github that you can clone to see how this works.
Sample project here
However this is the Game class for the sample project if you only want to take a quick look at it:
package com.leonziyo.recording;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.AudioDevice;
import com.badlogic.gdx.audio.AudioRecorder;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.utils.GdxRuntimeException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MainGame extends ApplicationAdapter {
boolean recordTurn = true;
final int samples = 44100;
boolean isMono = true, recording = false, playing = false;
#Override
public void create () {}
#Override
public void render () {
/*Changing the color just to know when it is done recording or playing audio (optional)*/
if(recording)
Gdx.gl.glClearColor(1, 0, 0, 1);
else if(playing)
Gdx.gl.glClearColor(0, 1, 0, 1);
else
Gdx.gl.glClearColor(0, 0, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// We trigger recording and playing with touch for simplicity
if(Gdx.input.justTouched()) {
if(recordTurn)
recordToFile("sound.bin", 3); //pass file name and number of seconds to record
else
playFile("sound.bin"); //file name to play
recordTurn = !recordTurn;
}
}
#Override
public void dispose() {
}
private void recordToFile(final String filename, final int seconds) {
//Start a new thread to do the recording, because it will block and render won't be called if done in the main thread
new Thread(new Runnable() {
#Override
public void run() {
try {
recording = true;
short[] data = new short[samples * seconds];
AudioRecorder recorder = Gdx.audio.newAudioRecorder(samples, isMono);
recorder.read(data, 0, data.length);
recorder.dispose();
saveAudioToFile(data, filename);
}
catch(GdxRuntimeException e) {
Gdx.app.log("test", e.getMessage());
}
finally {
recording = false;
}
}
}).start();
}
private void playFile(final String filename) {
//Start a new thread to play the file, because it will block and render won't be called if done in the main thread
new Thread(new Runnable() {
#Override
public void run() {
try {
playing = true;
short[] data = getAudioDataFromFile(filename); //get audio data from file
AudioDevice device = Gdx.audio.newAudioDevice(samples, isMono);
device.writeSamples(data, 0, data.length);
device.dispose();
}
catch(GdxRuntimeException e) {
Gdx.app.log("test", e.getMessage());
}
finally {
playing = false;
}
}
}).start();
}
private short[] getAudioDataFromFile(String filename) {
FileHandle file = Gdx.files.local(filename);
byte[] temp = file.readBytes(); // get all bytes from file
short[] data = new short[temp.length / 2]; // create short with half the length (short = 2 bytes)
ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(data); // cast a byte array to short array
return data;
}
private void saveAudioToFile(short[] data, String filename) {
byte[] temp = new byte[data.length * 2]; //create a byte array to hold the data passed (short = 2 bytes)
ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(data); // cast a short array to byte array
FileHandle file = Gdx.files.local(filename);
file.writeBytes(temp, false); //save bytes to file
}
}
Don't forget to add permission in case you are writing to external storage and permission for audio recording:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Related

how to write GameObject position in the Scene to json file?

on Clicking the button, I m loading the function WriteJsonForLevel(). I have placed three GameObject with the tag name "RedCoin" and I want to write the position of the GameObject to a JSON file. I can get the position of the object, but it's all overwritten. I can only see the last GameObject position (i.e the completion of the loop)
public List<GameObject> levelObjects;
public string level;
public Vector3 pos;
// Start is called before the first frame update
void Start()
{
levelObjects = new List<GameObject>();
}
// Update is called once per frame
void Update()
{
}
public void WritejsonForAll()
{
WriteJsonForLevel();
}
public void WriteJsonForLevel()
{
/* FileStream fs = new FileStream(Application.dataPath + "/sample.json",FileMode.Create);
StreamWriter writer= new StreamWriter(fs);*/
GameObject[] coinObjRed = GameObject.FindGameObjectsWithTag("RedCoin");
putAllObjectInList(coinObjRed);
}
public void putAllObjectInList(GameObject[] p)
{
string path = Application.dataPath + "/text.json";
foreach (GameObject q in p)
{
levelObjects.Add(q);
}
for (int i = 0; i < levelObjects.Count; i++)
{
GameObject lvlObj = levelObjects[i];
Vector3 pos = lvlObj.transform.position;
string posOutput = JsonUtility.ToJson(pos);
File.WriteAllText(path,posOutput);
Debug.Log("position:" + posOutput);
}
}
}
You are using WriteAllText which will overwrite the file every time it is called. As it is overwriting each time it is in the loop, it will only write the last object to the file as every other previous write is overwritten. I would consider making a serialized class of data, assigning the data to it, converting it to a JSON string then saving that.
// stores individual locations for saving
[System.Serializable]
public class IndividualLocation
{
public IndividualLocation(Vector3 pos)
{
xPos = pos.x;
yPos = pos.y;
zPos = pos.z;
}
public float xPos;
public float yPos;
public float zPos;
}
// stores all game locations for saving
[System.Serializable]
public class AllGameLocations
{
public List<IndividualLocation> Locations = new List<IndividualLocation>();
}
public void PutAllObjectInList(in GameObject[] p)
{
string path = Application.dataPath + "/text.json";
// create a new object to write to
AllGameLocations data = new AllGameLocations();
// iterate the objects adding each to our structure
foreach(GameObject obj in p)
{
data.Locations.Add(new IndividualLocation(obj.transform.position));
}
// now that the data is filled, write out to the file
File.WriteAllText(path, JsonUtility.ToJson(AllGameLocations));
}
If you need a snippet on how to load the data properly I can add one.
Edit: Here is a load snippet
public void LoadJSONObject()
{
string path = Application.dataPath + "/text.json";
// if the file path or name does not exist
if (!Directory.Exists(Path.GetDirectoryName(path)))
{
Debug.LogWarning("File or path does not exist! " + path);
return
}
// load in the save data as byte array
byte[] jsonDataAsBytes = null;
try
{
jsonDataAsBytes = File.ReadAllBytes(path);
Debug.Log("<color=green>Loaded all data from: </color>" + path);
}
catch (Exception e)
{
Debug.LogWarning("Failed to load data from: " + path);
Debug.LogWarning("Error: " + e.Message);
return;
}
if (jsonDataAsBytes == null)
return;
// convert the byte array to json
string jsonData;
// convert the byte array to json
jsonData = Encoding.ASCII.GetString(jsonDataAsBytes);
// convert to the specified object type
AllGameLocations returnedData;
JsonUtility.FromJsonOverwrite<AllGameLocations>(jsonData, AllGameLocations);
// use returnedData as a normal object now
float firstObjectX = returnedData.Locations[0].xPos;
}
}
Let me know if the Load works, just typed it up untested. Added some error handling as well to assure data exists and the load properly works.

How to correctly handle data management with SharedPreferences?

Right now, I am in the process of "optimizing" my app. I am still a beginner, so what I am doing is basically moving methods from my MainActivity.class to their separate class. I believe it's called Encapsulation (Please correct me if I'm wrong).
My application needs to :
Get a YouTube Playlist Link from the YouTube App (with an Intent, android.intent.action.SEND).
Use the link to fetch data from the Google Servers with the YouTubeApi and Volley.
Read the data received and add it to an arrayList<String>.
What my YouTubeUsage.java class is supposed to do, is fetch data with the YouTubeApi and Volley then store the data using SharedPreferences. Once the data is saved, the data is being read in my ConvertActivity.class (It's an activity specifically created for android.intent.action.SEND) with my method getVideoIds() before setting an adapter for my listView in my createRecyclerView() method.
YouTubeUsage.java
public class YoutubeUsage {
private Boolean results = false;
private String mResponse;
private ArrayList<String> videoIds = new ArrayList<>();
String Url;
public String getUrl(String signal) {
String playlistId = signal.substring(signal.indexOf("=") + 1);
this.Url = "https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails%2C%20snippet%2C%20id&playlistId=" +
playlistId + "&maxResults=25&key=" + "API_KEY";
return this.Url;
}
public void fetch(String Url, final Context context){
RequestQueue queue = Volley.newRequestQueue(context);
StringRequest request = new StringRequest(Request.Method.GET, Url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
sharedPreferences(response, context);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("VolleyError", Objects.requireNonNull(error.getMessage()));
}
});
queue.add(request);
}
private void sharedPreferences(String response, Context context){
SharedPreferences m = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = m.edit();
if (m.contains("serverResponse")){
if (!m.getString("serverResponse", "").equals(response)){
editor.remove("serverResponse");
editor.apply();
updateSharedPreferences(response, context);
}
} else{
updateSharedPreferences(response, context);
}
}
private void updateSharedPreferences(String mResponse, Context mContext){
SharedPreferences m = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = m.edit();
editor.putString("serverResponse", mResponse);
editor.apply();
}
}
ConvertActivity.java
public class ConvertActivity extends AppCompatActivity {
YoutubeUsage youtubeUsage = new YoutubeUsage();
ArrayList<String> videoIDs = new ArrayList<>();
String Url = "";
ListView listView;
MyCustomAdapter myCustomAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_convert);
listView = findViewById(R.id.listview_convert);
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if ("android.intent.action.SEND".equals(action) && "text/plain".equals(type)) {
Url = youtubeUsage.getUrl(Objects.requireNonNull(intent.getStringExtra("android.intent.extra.TEXT")));
}
//I would like to avoid the try/catch below
try {
videoIDs = getVideoIDs(Url, this);
createRecyclerView(videoIDs);
Log.i("ResponseVideoIDs", String.valueOf(videoIDs.size()));
} catch (JSONException e) {
e.printStackTrace();
}
}
private ArrayList<String> getVideoIDs(String Url, Context context) throws JSONException {
ArrayList<String> rawVideoIDs = new ArrayList<>();
youtubeUsage.fetch(Url, context);
SharedPreferences m = PreferenceManager.getDefaultSharedPreferences(context);
String serverResponse = m.getString("serverResponse", "");
JSONObject jsonObject = new JSONObject(serverResponse);
JSONArray jsonArray = jsonObject.getJSONArray("items");
for (int i = 0; i<jsonArray.length(); i++){
JSONObject jsonObject1 = jsonArray.getJSONObject(i);
JSONObject jsonVideoId = jsonObject1.getJSONObject("contentDetails");
rawVideoIDs.add(jsonVideoId.getString("videoId"));
}
return rawVideoIDs;
}
private void createRecyclerView(ArrayList<String> videoIDs){
myCustomAdapter = new MyCustomAdapter(this, videoIDs);
listView.setAdapter(myCustomAdapter);
myCustomAdapter.notifyDataSetChanged();
}
}
Everything works fine, however, my sharedPreferences never gets updated. Which means, if I share a YouTube playlist from the YouTube App to my app with 3 items in it, it will work fine. The Listview will show 3 items with their corresponding IDs as it should. But, if I share a YouTube playlist again, my app will still hold on to the data of the previous playlist I shared (even if I close it), showing the item number and the IDs of the previous link. If i continue to share the same playlist over and over, it will eventually show the correct number of items and the correct IDs.
I could totally put all my methods from the YouTubeUsage.java in my ConvertActivity.class preventing me from using SharedPreferences to transfer data between the two java classes. However, JSON throws an exception. That means I have to encapsulate my code with try/catch. I would like to avoid those since I need to do a lot of operations on the data just received by Volley (check a class size, look for certains strings). I find that doing this in these try/catch don't work like I want. (i.e. outside the try/catch, the values remains the same even if I updated them in the try/catch).
I want to know two things.
How can I correct this problem?
Is this the most efficient way to do this (optimization)? (I though of maybe
converting the VolleyResponse to a string with Gson then store the String file, but I don't know if that's the best way to do it since it's supposed to be
provisional data. It feels like just more of the same).
Thank You!
There is an issue with making assumptions about order of events. Volley will handle requests asynchronously, so it is advisable to implement the observer pattern here.
Create a new Java file that just contains:
interface MyNetworkResponse {
void goodResponse(String responseString);
}
Then make sure ConvertActivity implements MyNetworkResponse and create method:
void goodResponse(String responseString) {
// handle a positive response here, i.e. extract the JSON and send to your RecyclerView.
}
within your Activity.
In your YoutubeUsage constructor, pass in the Activity context (YoutubeUsage) and then store this in a YoutubeUsage instance variable called ctx.
In onCreate, create an instance of YoutubeUsage and pass in this.
In onResponse just call ctx.goodResponse(response).
Amend the following block to:
if ("android.intent.action.SEND".equals(action) && "text/plain".equals(type)) {
Url = youtubeUsage.getUrl(Objects.requireNonNull(intent.getStringExtra("android.intent.extra.TEXT")));
youtubeUsage.fetch(Url);
}
Delete the try/catch from onCreate.
And no need to use SharedPreferences at all.
UPDATE
Try this code:
MyNetworkResponse.java
interface MyNetworkResponse {
void goodResponse(String responseString);
void badResponse(VolleyError error);
}
YoutubeUsage.java
class YoutubeUsage {
private RequestQueue queue;
private MyNetworkResponse callback;
YoutubeUsage(Object caller) {
this.callback = (MyNetworkResponse) caller;
queue = Volley.newRequestQueue((Context) caller);
}
static String getUrl(String signal) {
String playlistId = signal.substring(signal.indexOf("=") + 1);
return "https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails%2C%20snippet%2C%20id&playlistId=" + playlistId + "&maxResults=25&key=" + "API_KEY";
}
void fetch(String url){
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
callback.goodResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.badResponse(error);
}
});
queue.add(request);
}
}
ConvertActivity.java
public class ConvertActivity extends AppCompatActivity implements MyNetworkResponse {
YoutubeUsage youtubeUsage;
ArrayList<String> videoIDs = new ArrayList<>();
ListView listView;
MyCustomAdapter myCustomAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_convert);
listView = findViewById(R.id.listview_convert);
youtubeUsage = new YoutubeUsage(this);
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if ("android.intent.action.SEND".equals(action) && "text/plain".equals(type)) {
String url = YoutubeUsage.getUrl(Objects.requireNonNull(intent.getStringExtra("android.intent.extra.TEXT")));
youtubeUsage.fetch(url);
}
}
private ArrayList<String> getVideoIDs(String serverResponse) throws JSONException {
ArrayList<String> rawVideoIDs = new ArrayList<>();
JSONObject jsonObject = new JSONObject(serverResponse);
JSONArray jsonArray = jsonObject.getJSONArray("items");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject1 = jsonArray.getJSONObject(i);
JSONObject jsonVideoId = jsonObject1.getJSONObject("contentDetails");
rawVideoIDs.add(jsonVideoId.getString("videoId"));
}
return rawVideoIDs;
}
private void createRecyclerView(ArrayList<String> videoIDs) {
myCustomAdapter = new MyCustomAdapter(this, videoIDs);
listView.setAdapter(myCustomAdapter);
myCustomAdapter.notifyDataSetChanged();
}
#Override
public void goodResponse(String responseString) {
Log.d("Convert:goodResp", "[" + responseString + "]");
try {
ArrayList<String> rawVideoIDs = getVideoIDs(responseString);
createRecyclerView(rawVideoIDs);
} catch (JSONException e) {
// handle JSONException, e.g. malformed response from server.
}
}
#Override
public void badResponse(VolleyError error) {
// handle unwanted server response.
}
}

Accurate Windows phone 8.1 geolocation?

Im working with windows phone 8.1 geolocation. The problem that I currently have is that my code only shows the first numbers of my coordinate. Example: If the coordinate is "41.233" the app only shows "41.00" . I need it to be as accurate as possible. In case it matters, im using windows phone 8.1 emulator to try the app, not an actual phone.
My code:
public sealed partial class MainPage : Page
{
bool shouldSend = false;
DispatcherTimer timer = new DispatcherTimer();
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private async Task GetLocation()
{
Geolocator geolocator = new Geolocator();
geolocator.DesiredAccuracy = Windows.Devices.Geolocation.PositionAccuracy.High;
try
{
Geoposition geoposition = await geolocator.GetGeopositionAsync(
maximumAge: TimeSpan.FromSeconds(1),
timeout: TimeSpan.FromSeconds(10)
);
LatitudeTxt.Text = geoposition.Coordinate.Latitude.ToString("0.00");
LongitudeTxt.Text = geoposition.Coordinate.Longitude.ToString("0.00");
LatLonTxt.Text = LatitudeTxt.Text + ", " + LongitudeTxt.Text;
var speed = geoposition.Coordinate.Speed.ToString();
ProcessingTxt.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
string result = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
"http://proyecto-busways.rhcloud.com/colectivos?p=lta123&l=80&d=moyano&lat=" + LatitudeTxt.Text + "&lon=" + LongitudeTxt.Text + "&v=" + speed + "&Accion=Agregar");
request.ContinueTimeout = 4000;
request.Credentials = CredentialCache.DefaultNetworkCredentials;
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
{
if (response.StatusCode == HttpStatusCode.OK)
{
//To obtain response body
using (Stream streamResponse = response.GetResponseStream())
{
using (StreamReader streamRead = new StreamReader(streamResponse, Encoding.UTF8))
{
result = streamRead.ReadToEnd();
}
}
}
}
}
catch (Exception ex)
{
ProcessingTxt.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
if ((uint)ex.HResult == 0x80004004)
{
// the application does not have the right capability or the location master switch is off
}
//else
{
// something else happened acquring the location
}
}
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
}
private async void StartSending_Click(object sender, RoutedEventArgs e)
{
await GetLocation();
timer.Tick += timer_Tick;
timer.Interval = new TimeSpan(0, 0, 5);
timer.Start();
StartSending.IsEnabled = false;
}
async void timer_Tick(object sender, object e)
{
ProcessingTxt.Visibility = Windows.UI.Xaml.Visibility.Visible;
await GetLocation();
}
private void EndSending_Click(object sender, RoutedEventArgs e)
{
timer.Tick -= timer_Tick;
timer.Stop();
StartSending.IsEnabled = true;
EndSending.IsEnabled = false;
}
private void GPS_Tapped(object sender, TappedRoutedEventArgs e)
{
Frame.Navigate(typeof(ContactPage));
}
}
Thanks for your help!
Did you try out the Geolocator.DesiredAccuracyInMeters property?
geolocator.DesiredAccuracyInMeters = 3;
Reference & Sample
In this point LatitudeTxt.Text = geoposition.Coordinate.Latitude.ToString("0.00");
LongitudeTxt.Text = geoposition.Coordinate.Longitude.ToString("0.00");
You indicated that you have 0.00 decimals, for more accuracy you should put 0.000000

WebClient event firing order

I'm new to WP7 app development and I'm having trouble passing parameters to an API on a website.
It's my understanding that the onNavigatedTo() is fired first when a page is open on the WP7, however when I try to grab the parameters the webClient_DownloadStringCompleted() is fired first.
public partial class Ranks : PhoneApplicationPage
{
private WebClient webClient;
private string pageType;
private string pagePosition;
public Ranks()
{
InitializeComponent();
this.webClient = new WebClient();
string header_auth = "application/json";
this.webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
this.webClient.Headers[HttpRequestHeader.Authorization] = header_auth;
Uri serviceUri = new Uri(#"http://www.example.com/api/API.php?type=" + pageType + "&position=" + pagePosition);
this.webClient.DownloadStringAsync(serviceUri);
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string type, position;
if (NavigationContext.QueryString.TryGetValue("type", out type))
{
pageType = type;
}
if (NavigationContext.QueryString.TryGetValue("pos", out position))
{
pagePosition = position;
}
}
void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string myJsonString = e.Result;
List<PlayerDetails> dataSource = new List<PlayerDetails>();
//load into memory stream
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(myJsonString)))
{
//parse into jsonser
var ser = new DataContractJsonSerializer(typeof(PlayerDetails[]));
PlayerDetails[] obj = (PlayerDetails[])ser.ReadObject(ms);
foreach (PlayerDetails plyr in obj)
{
dataSource.Add(plyr);
}
playerList.ItemsSource = dataSource;
}
}
Whenever the URI string is built it's missing the parameters 'pageType' and 'pagePosition'
Any help would be greatly appreciated!
The class constructor will always get called before OnNavigatedTo. you should move that code from the constructor, and into OnNavigatedTo (or Loaded).
I'm guessing that you have that code in the constructor because you only want it to happen once per page load (i.e. not when the user navigates Back onto the page). If that's the case, you can check the NavigationMode.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.New)
{
string type, position;
if (NavigationContext.QueryString.TryGetValue("type", out type))
{
pageType = type;
}
if (NavigationContext.QueryString.TryGetValue("pos", out position))
{
pagePosition = position;
}
this.webClient = new WebClient();
string header_auth = "application/json";
this.webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
this.webClient.Headers[HttpRequestHeader.Authorization] = header_auth;
Uri serviceUri = new Uri(#"http://www.example.com/api/API.php?type=" + pageType + "&position=" + pagePosition);
this.webClient.DownloadStringAsync(serviceUri);
}
}

Java Reflection Problem

Hi I am currently doing my final year project; I need to develop an algorithm visualization tool. I need to cater for user-defined algo; that is animate the algorithm the user types in a text-editor provided in my tool.
I am using the Java Compiler API to compile the code that the user has typed and saved. My tool offers a set of classes that the user can use in his/her algo.
For example:
myArray(this class is provided by my tool)
import java.awt.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
public class myArray extends JComponent {
int size = 0;
int count = 0;
int[]hold;
Thread th;
public myArray(int[]arr)//pass user array as parameter
{
//th = new Thread();
size=arr.length;
hold = arr;//make a copy of the array so as to use later in swap operation
}
public int length()
{
return hold.length;
}
public void setAccessibleContext(AccessibleContext accessibleContext) {
this.accessibleContext = accessibleContext;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
this.setPreferredSize(new Dimension(360,100));
for(int i=1; i<=size; i++)
{
g2d.drawRect((i*30), 30, 30, 50);
}
for(int i=1; i<=size; i++)
{
g2d.drawString(Integer.toString(hold[i-1]), (i*30)+15, 30+25);
}
}
public void set(int i, int j)//position of the two elements to swap in the array
{
try {
th.sleep(2000);//sleep before swapping because else user won't see original array since it would swap and then sleep
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int temp = hold[i];
hold[i] = hold[j];
hold[j] = temp;
hold[i]=j;
this.repaint();//can use eapint with a class that extends JPanel
}
public void swap(int i, int j)//position of the two elements to swap in the array
{
try {
th.sleep(2000);//sleep before swapping because else user won't see original array since it would swap and then sleep
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int temp = hold[i];
hold[i] = hold[j];
hold[j] = temp;
this.repaint();//can use eapint with a class that extends JPanel
}
public int get(int pos)
{
return hold[pos];
}
}
This is a portion of my GUI that will cause the compilation:
JavaCompiler jc = null;
StandardJavaFileManager sjfm = null;
File javaFile = null;
String[] options = null;
File outputDir = null;
URL[] urls = null;
URLClassLoader ucl = null;
Class clazz = null;
Method method = null;
Object object = null;
try
{
jc = ToolProvider.getSystemJavaCompiler();
sjfm = jc.getStandardFileManager(null, null, null);
File[] files = new File[1];
//files[0] = new File("C:/Users/user/Documents/NetBeansProjects/My_Final_Year_Project/myArray.java");
//files[1] = new File("C:/Users/user/Documents/NetBeansProjects/My_Final_Year_Project/Tool.java");
files[0] = new File("C:/Users/user/Documents/NetBeansProjects/My_Final_Year_Project/userDefined.java");
// getJavaFileObjects’ param is a vararg
Iterable fileObjects = sjfm.getJavaFileObjects(files);
jc.getTask(null, sjfm, null, null, null, fileObjects).call();
// Add more compilation tasks
sjfm.close();
options = new String[]{"-d", "C:/Users/user/Documents/NetBeansProjects/My_Final_Year_Project"};
jc.getTask(null, sjfm, null, Arrays.asList(options), null, fileObjects).call();
outputDir = new File("C:/Users/user/Documents/NetBeansProjects/My_Final_Year_Project");
urls = new URL[]{outputDir.toURL()};
ucl = new URLClassLoader(urls);
clazz = ucl.loadClass("userDefined");
method = clazz.getMethod("user", null);
object = clazz.newInstance();
Object ob = method.invoke(object, null);
}
This is an example of a user-defined algo(userDefined.java):
import java.awt.*;
import javax.swing.*;
public class userDefined
{
public void user()
{
int [] numArr = {1,3,1,-1,5,-5,0,7,12,-36};
myArray myArray = new myArray(numArr);
JFrame frame = new JFrame("Rectangles");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(360, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.add(myArray);
for (int i=myArray.length(); i>1; i--)
{
for (int j=0; j<i-1; j++)
{
if (myArray.get(j) > myArray.get(j+1))
{
myArray.swap(j, j+1);
}
}
}
}
}
The problem I am getting is that if I try to use reflection like above; I only get a white window which does not show the animation) but just displays the result at the very end.
However if I use this instead of reflection(and change the method void user() to static void main(string args) in userDefined.java):
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if(compiler.run(null, null, null, "userDefined.java") != 0) {
System.err.println("Could not compile.");
System.exit(0);
}
try {
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec("java "+"userDefined");
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line=null;
while((line=input.readLine()) != null) {
System.out.println(line);
}
} catch(Exception e) {
System.out.println(e.toString());
e.printStackTrace();
it woks provided that after first compilation I place the myArray class in the same folder as the userDefined.java. In this case I can see the animation take place correctly.
How do I use reflection to invoke the main method instead of using an instance of the class.
Please I really need some help with this. Thanks!
You a violating / missusing the first rule of swing: acces swing components only in the EDT (Event Dispatch Thread).
When you start your program using the main method, you are violating that rule. This happens to work, but might have all kinds of weird effects. This is not a theoretic warning, it happend to me and it is not nice.
When you run it using reflection from your code, you are most likely in the EDT, so your algorithm runs completely before the GUI gets updated again (which also happens on the EDT). Thats why you see only the final result of the algorithm.
The correct way to do this would be:
Run the algorithm in a seperate thread and make sure all changes to your myArray Component happen in the EDT, using SwingUtilities.invokeAndWait or SwingUtilities.invokeLater