I created and published a game for android with AndEngine three years ago. Now I am preparing the game again with unity. When I send an update, all of the old data is deleted. Because Unity using 'PlayerPrefs' and Android using 'SharedPreferences'.
So, How do I read old data with Unity?
Here is my old code setup:
private SharedPreferences data;
private SharedPreferences.Editor editor;
// ======================================================================
public void setup(Activity activity) {
data = activity.getSharedPreferences("kayit", 0);
editor = data.edit();
}
I don't think its going to be easy accessing persistent data saved with a different engine - but a solution I can see is release an update with the old engine that will upload the data to a remote server, and then download it using Unity, but since you already released the unity version that might not work for you
PlayerPrefs and SharedPreferences are different things.
But there is a solution.
Unity allows to add native android Java or C++ code to your Unity project and use interfaces to access that code.
So you can write some code using Android SDK/NDK methods to access your SharedPreferences and add it as JAR or AAR to your Unity project.
Read about it in the official documentation https://docs.unity3d.com/Manual/PluginsForAndroid.html
private void ReadAndroidSharedPreferences(string packageName, string xmlDataName)
{
string path = "/data/data/" + packageName + "/shared_prefs/" + xmlDataName + ".xml";
FileStream levelFile = File.Open(path, FileMode.Open);
XmlDocument xmlDoc = new XmlDocument();
string xmlText = "";
using (StreamReader sr = new StreamReader(levelFile))
{
string line = null;
do
{
line = sr.ReadLine();
xmlText = xmlText + line + "\n";
} while (line != null);
sr.Close();
levelFile.Close();
}
xmlDoc.LoadXml(xmlText);
var baseNode = xmlDoc.DocumentElement;
foreach (XmlNode node in baseNode.ChildNodes)
{
Debug.Log("Type: " + node.Name);
if (node.Attributes["name"] != null)
Debug.Log("name: " + node.Attributes["name"].Value);
else
Debug.Log("Empty");
if (node.Attributes["value"] != null)
Debug.Log("Value: " + node.Attributes["value"].Value);
else
Debug.Log("Empty");
}
}
Usage:
packageName = "com.yourcompany.yourgame"
xmlDataName = getSharedPreferences file name (in my question, it's "kayit")
You can see all 'int' and 'bool' data on Android Device Monitor.
Related
Using Xamarin.Forms, has the way to write to file changed since Android 8.0?
I updated an existing project of mine, which includes a very simple function to write a text file to local storage, but after running the app and testing the write to file part, it just freezes and crashes out. There are no errors in the console or even evidence of it doing anything.
My function is as such:
public void Submit_Clicked(object sender, EventArgs e)
{
{
string text = NameEntry.Text;
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filename = Path.Combine(path, "DeviceProfile.txt");
File.WriteAllText(filename, text);
}
catch(Exception ex)
{
DisplayAlert("Error:", ex.Message.ToString(), "Ok");
}
}
I cant see why this shouldn't work. Does someone know any reason why this wouldn't?
Like Jason said, the code below and the code you provided both work to write a text file to local storage.
string text = NameEntry.Text;
var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "DeviceProfile.txt");
using (var writer = File.CreateText(backingFile))
{
await writer.WriteLineAsync(text);
}
You could read the text from the file to check.
public async Task<string> ReadCountAsync()
{
var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "DeviceProfile.txt");
if (backingFile == null || !File.Exists(backingFile))
{
return null;
}
var text = string.Empty;
using (var reader = new StreamReader(backingFile, true))
{
var str = await reader.ReadToEndAsync();
text = str;
}
return text;
}
The path Environment.SpecialFolder.Personal you used to save the file is internal storage.
In Internal Storage, you couldn't see the files without root permission. If you want to view it, you could use adb tool. Please check the way in link.
How to write the username in a local txt file when login success and check on file for next login?
Currently using LitJson in my development. Json is working perfectly fine in Unity Editor but not in android device. Tried various ways in loading the json file but nothing worked. Here is my latest code:
string path = Application.persistentDataPath + "/Products.json";
string jsonString = File.ReadAllText (path);
if(File.Exists(path)) {
jsonParser data = JsonUtility.FromJson<jsonParser>(jsonString);
data.products.Add (product);
Debug.Log (data.products [1].code);
string jsonString2 = JsonUtility.ToJson (data);
Debug.Log (jsonString2);
File.WriteAllText (path, jsonString2.ToString());
} else{
File.WriteAllText (path, jsonString);
}
Big thanks!
I suggest you using Resources loading instead of IO since the least is something platform specific.
So if you create a "Resources" folder in the root of Assets folder of your project and put your json file in it so it looks like this "/Assets/Resources/Products.json"
and change you code to this
string jsonString = Resources.Load<string>("Products.json");
if (jsonString != null)
{
jsonParser data = JsonUtility.FromJson<jsonParser>(jsonString);
data.products.Add(product);
Debug.Log(data.products[1].code);
jsonString = JsonUtility.ToJson(data);
Debug.Log(jsonString);
}
using (FileStream fs = new FileStream("Products.json", FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(fs))
{
writer.Write(jsonString);
}
}
UnityEditor.AssetDatabase.Refresh();
everything should work fine.
I am new to Unity and Android development, but please do not mark this as a duplicate - I've looked all over the stacks for an answer to this, but similar topics and questions haven't yielded any working solutions, or are wanting on details, or outdated, or seem not to fit my specific need.
Ok, I have a quiz app built from following part 1 & 2 of this tutorial. That link contains all the source files anyone might need for reference, and everything works fine on ios and in the unity editor, fyi.
The trouble is with the loadGameData function from the DataController script in Android. Again, everything works fine in iOS and the unity editor, but when I make an Android sdk, the quiz is blank and the console says the data couldn't be loaded.
Here is how the function is currently written (full file in tutorial link):
private void LoadGameData ()
{
string filePath = Path.Combine (Application.streamingAssetsPath, gameDataFileName);
if (File.Exists (result))
{
string dataAsJson = File.ReadAllText (result);
GameData loadedData = JsonUtility.FromJson<GameData> (dataAsJson);
allRoundData = loadedData.allRoundData;
} // #if(File.Exists...
else
{
Debug.LogError ("Cannot load game data!");
} // #else
} // #LoadGameData
If you check the same tutorial on youtube, you'll see lots of people have noted the same problem with the Android build and have been left unanswered. Same goes with unity forums - that's one reason why I don't think this question is a duplicate and could be helpful to others in a similar situation.
I've found that Android has always been sorta tricky with this and that there used to different ways of accessing a file based on platform, but these days "Application.streamingAssetsPath" should find the streaming assets directory on any platform, even Android.
What I've also learned is that in android, even if the path is correct, the file is compressed and will only return a url. So the url needs to be converted using unity's WWW class. I get that, but as of yet, I haven't been able to re-write my loadGameData function to work properly and load the allRoundData array.
Here's an example of some things I've tried:
IEnumerator androidData()
{
string filePath = Path.Combine (Application.streamingAssetsPath, gameDataFileName);
if (filePath.Contains("://"))
{
WWW www = new WWW(filePath);
yield return www;
result = www.text;
} // #if(filePath.Contains
} // #androidData
private void LoadGameData ()
{
androidData();
if (File.Exists (result))
{
string dataAsJson = File.ReadAllText (result);
GameData loadedData = JsonUtility.FromJson<GameData> (dataAsJson);
allRoundData = loadedData.allRoundData;
} // #if(File.Exists...
else
{
Debug.LogError ("Cannot load game data!");
} // #else
} // #LoadGameData
I know I'm close, and this is probably simple -- but I just can't seem to get to the finish line on this. Can some one help me figure out how to write the loadGameData function so it will load this allRoundData array on android?
An example code would be awesome and much appreciated, not just by me, but I'm sure many others would appreciate it also - Thank you!
UPDATE:
Based on the first answer, I've tested some code that works on the unity editor, but crashes in Android. In the Unity editor, I get the "file already exists" message. Here is the code I've tested:
Already had: private string gameDataFileName = "data.json";
I added the copyFile call above loadGameDate in Start() and wrote the copy file and loadGameData functions like so ..
int copyFileToPersistentDataPath(string gameDataFileName)
{
string persistentPath = Application.persistentDataPath + "/" + gameDataFileName;
try
{
//Copy only if gameDataFileName does not exist
if (!System.IO.File.Exists(persistentPath))
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, gameDataFileName);
WWW www = new WWW(path);
while (!www.isDone) { }
System.IO.File.WriteAllBytes(persistentPath, www.bytes);
Debug.Log(gameDataFileName + " Successfully Copied File to " + persistentPath);
return 1;
}
else
{
Debug.Log(gameDataFileName + " File already exist here. There is no need to copy it again");
return 0;
}
}
catch (Exception e)
{
Debug.Log(gameDataFileName + " Failed To Copy File. Reason: " + e.Message);
return -1;
}
}
private void LoadGameData ()
{
string tempPath = Path.Combine(Application.persistentDataPath, gameDataFileName);
string dataAsJson = File.ReadAllText(tempPath);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
allRoundData = loadedData.allRoundData;
} // #LoadGameData
This works with or without the call to copy the file in the editor, but crashes either way in Android.
I ended up putting the files in a Resources folder and going the resources.load a json file into a text asset and throw that into a string to parse route. I now have two different load functions, one that works in ios etc. and one that works in android. Here is the android function (the resourcesGameDataFile does not have the .json extension):
public void LoadDataForAndroid()
{
TextAsset dataFile = Resources.Load(resourcesGameDataFile) as TextAsset;
string dataAsJson = dataFile.ToString();
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
allRoundData = loadedData.allRoundData;
Debug.Log ("Android data loaded with" + resourcesGameDataFile);
} // #LoadDataForAndroid
And this works in the unity editor and in Bluestacks (android simulator).
As for loading and saving games, this is a duplicate. I marked and remove this as a duplicate because the answer in the duplicated questions did not explain how to read from the StreamingAssets folder. It only talked about saving and loading data.
Make sure that Write Permission is set to External (SDCard).
The first thing to do is to copy the file from StreamingAssets to the persistentDataPath location.
I've found reading data from Application.streamingAssetsPath problematic but I use two methods to solve this.
int copyFileToPersistentDataPath(string fileNameWithExtensionName)
{
string persistentPath = Application.persistentDataPath + "/" + fileNameWithExtensionName;
try
{
//Copy only if fileNameWithExtensionName does not exist
if (!System.IO.File.Exists(persistentPath))
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileNameWithExtensionName);
WWW www = new WWW(path);
while (!www.isDone) { }
System.IO.File.WriteAllBytes(persistentPath, www.bytes);
Debug.Log(fileNameWithExtensionName + " Successfully Copied File to " + persistentPath);
return 1;
}
else
{
Debug.Log(fileNameWithExtensionName + " File already exist here. There is no need to copy it again");
return 0;
}
}
catch (Exception e)
{
Debug.Log(fileNameWithExtensionName + " Failed To Copy File. Reason: " + e.Message);
return -1;
}
}
If that does not work for you, use the method with WebClient below:
void copyFileToPersistentDataPath(string fileNameWithExtensionName)
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileNameWithExtensionName);
string persistentPath = Application.persistentDataPath + "/" + fileNameWithExtensionName;
Debug.Log("Dir: " + persistentPath);
WebClient webClient = new WebClient();
webClient.Proxy = null;
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadComplete);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnUpdateDownloadProgress);
Uri uri = new Uri(path);
webClient.DownloadFileAsync(uri, persistentPath);
}
void OnDownloadComplete(object sender, AsyncCompletedEventArgs e)
{
Debug.Log("Finished Downloading: " + e.Error.Message);
}
void OnUpdateDownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
Debug.Log("Uploading Progreess: " + e.ProgressPercentage);
}
File Copy Usage:
copyFileToPersistentDataPath("yourFileName.txt");
After copying the file, you can then read and convert it to Json like this:
string fileNameWithExtensionName = "questionfile.txt";
string tempPath = Path.Combine(Application.persistentDataPath, fileNameWithExtensionName);
string dataAsJson = File.ReadAllText(fileNameWithExtensionName);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
I want to upload image on Google Cloud Storage from my android app. For that I searched and found that GCS JSON Api provides this feature. I did a lot of research for Android sample which demonstrates its use. On the developer site they have provided code example that only support java. I don't know how to use that API in Android. I referred this and this links but couldn't get much idea. Please guide me on how i can use this api with android app.
Ok guys so I solved it and got my images being uploaded in Cloud Storage all good.
This is how:
Note: I used the XML API it is pretty much the same.
First, you will need to download a lot of libraries.
The easiest way to do this is create a maven project and let it download all the dependencies required. From this sample project :
Sample Project
The libraries should be:
Second, you must be familiar with Cloud Storage using the api console
You must create a project, create a bucket, give the bucket permissions, etc.
You can find more details about that here
Third, once you have all those things ready it is time to start coding.
Lets say we want to upload an image:
Cloud storage works with OAuth, that means you must be an authenticated user to use the API. For that the best way is to authorize using Service Accounts. Dont worry about it, the only thing you need to do is in the API console get a service account like this:
We will use this service account on our code.
Fourth, lets write some code, lets say upload an image to cloud storage.
For this code to work you must put your key generated in step 3 in assets folder, i named it "key.p12".
I don't recommend you to do this on your production version, since you will be giving out your key.
try{
httpTransport= new com.google.api.client.http.javanet.NetHttpTransport();
//agarro la key y la convierto en un file
AssetManager am = context.getAssets();
InputStream inputStream = am.open("key.p12"); //you should not put the key in assets in prod version.
//convert key into class File. from inputstream to file. in an aux class.
File file = UserProfileImageUploadHelper.createFileFromInputStream(inputStream,context);
//Google Credentianls
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton(STORAGE_SCOPE))
.setServiceAccountPrivateKeyFromP12File(file)
.build();
String URI = "https://storage.googleapis.com/" + BUCKET_NAME+"/"+imagename+".jpg";
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
//byte array holds the data, in this case the image i want to upload in bytes.
HttpContent contentsend = new ByteArrayContent("image/jpeg", byteArray );
HttpRequest putRequest = requestFactory.buildPutRequest(url, contentsend);
com.google.api.client.http.HttpResponse response = putRequest.execute();
String content = response.parseAsString();
Log.d("debug", "response is:"+response.getStatusCode());
Log.d("debug", "response content is:"+content);} catch (Exception e) Log.d("debug", "Error in user profile image uploading", e);}
This will upload the image to your cloud bucket.
For more info on the api check this link Cloud XML API
Firstly, You should get the below information by registering your application in the GCP console.
private final String pkcsFile = "xxx.json";//private key file
private final String bucketName = "your_gcp_bucket_name";
private final String projectId = "your_gcp_project_id";
Once you get the credentials, you should put the private key (.p12 or .json) in your assets folder. I'm using JSON format private key file. Also, you should update the image location to upload.
#RequiresApi(api = Build.VERSION_CODES.O)
public void uploadImageFile(String srcFileName, String newName) {
Storage storage = getStorage();
File file = new File(srcFileName);//Your image loaction
byte[] fileContent;
try {
fileContent = Files.readAllBytes(file.toPath());
} catch (IOException e) {
e.printStackTrace();
return;
}
if (fileContent == null || fileContent.length == 0)
return;
BlobInfo.Builder newBuilder = Blob.newBuilder(BucketInfo.of(bucketName), newName);
BlobInfo blobInfo = newBuilder.setContentType("image/png").build();
Blob blob = storage.create(blobInfo, fileContent);
String bucket = blob.getBucket();
String contentType = blob.getContentType();
Log.e("TAG", "Upload File: " + contentType);
Log.e("File ", srcFileName + " uploaded to bucket " + bucket + " as " + newName);
}
private Storage getStorage() {
InputStream credentialsStream;
Credentials credentials;
try {
credentialsStream = mContext.getAssets().open(pkcsFile);
credentials = GoogleCredentials.fromStream(credentialsStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return StorageOptions.newBuilder()
.setProjectId(projectId).setCredentials(credentials)
.build().getService();
}
I need to connect from my Android phone to a Windows PC share and access files. I saw some sample apps in Android market that access share folders using smb/samba. But I have no idea about how to create an app like that. Thanks a lot.
You need to get JCIFS and use SmbFile class to interact with files over the network,
http://lists.samba.org/archive/jcifs/2007-September/007465.html
that is a quick example of how to list files, of coarse you need internet permission on. So Far though everytime I try to call SmbFile.listFiles(); I get an UnknownHostException, However others seam to be able to do it with no problem, this might work for you, try it!
Google has released a simple, free Samba client. It is on github so you can have a look and use whatever you need out of that: https://github.com/google/samba-documents-provider
The other option is JCIFS: https://jcifs.samba.org/. There you can find the library and examples on how to use it.
I used JCIFS. Here is an example from my code which reads files from a folder in a windows share:
TreeMap<Date, String> filesInfo = new TreeMap<Date, String>();
NtlmPasswordAuthentication auth = null;
UniAddress dc = UniAddress.getByName(m_dataHostIp);
if(m_userName.length() > 0 && m_password.length() > 0)
auth = new NtlmPasswordAuthentication(m_domain + ";" + m_userName + ":" + m_password);
else
auth = new NtlmPasswordAuthentication(m_domain, null, null);
SmbSession.logon(dc, auth);
SmbFile file = new SmbFile(m_foldername, auth);
SmbFile[] files = file.listFiles();
for (int i = 0; i < files.length; i++)
{
String fileName = files[i].getName();
String extension=fileName.substring(fileName.lastIndexOf(".") + 1);
logInfo(TAG + " " + fileName + "\n");
Date fileTime = new Date(files[i].getDate());
if(m_fileExtension.contains(extension))
filesInfo.put(fileTime, fileName);
}
The code posted above works. It allows you to connect to the share, authenticate (username and password that you know) and get the list of the files. At the root of jcif file access is the SmbFile which has all the info you need to access files in the share. All you need is in your build.gradle for the app add:
dependencies {
implementation files('libs/jcifs-1.3.19.jar')
}
and in your implementation file:
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import static jcifs.smb.SmbFile.FILE_SHARE_DELETE;
import static jcifs.smb.SmbFile.FILE_SHARE_READ;
import static jcifs.smb.SmbFile.FILE_SHARE_WRITE;