I wrote the following method:
/**
* Downloads an arbitrary file to the cache asynchronously, if the current
* platform has a cache path, or to the app home; if the file was previously
* downloaded and if it's still available on the cache, it calls the
* onSuccess callback immediatly.More info:
* https://www.codenameone.com/blog/cache-sorted-properties-preferences-listener.html
*
* #param url The URL to download.
* #param extension you can leave it empty or null, however iOS cannot play
* videos without extension (https://stackoverflow.com/q/49919858)
* #param onSuccess Callback invoked on successful completion (on EDT by
* callSerially).
* #param onFail Callback invoked on failure (on EDT by callSerially).
*/
public static void downloadFileToCache(String url, String extension, SuccessCallback<String> onSuccess, Runnable onFail) {
FileSystemStorage fs = FileSystemStorage.getInstance();
if (extension == null) {
extension = "";
}
if (extension.startsWith(".")) {
extension = extension.substring(1);
}
String name = "cache_" + HashUtilities.sha256hash(url);
if (!extension.isEmpty()) {
name += "." + extension;
}
String filePath;
if (fs.hasCachesDir()) {
// this is supported by Android, iPhone and Javascript
filePath = fs.getCachesDir() + fs.getFileSystemSeparator() + name;
} else {
// The current platform doesn't have a cache path (for example the Simulator)
String homePath = fs.getAppHomePath();
filePath = homePath + fs.getFileSystemSeparator() + name;
}
// Was the file previously downloaded?
if (fs.exists(filePath)) {
CN.callSerially(() -> onSuccess.onSucess(filePath));
} else {
Util.downloadUrlToFileSystemInBackground(url, filePath, (evt) -> {
if (fs.exists(filePath)) {
CN.callSerially(() -> onSuccess.onSucess(filePath));
} else {
CN.callSerially(onFail);
}
});
}
}
It works. It's similar to some methods provided by the Util class, but with two main differences: the first is that the Util class provides methods only to download images to the cache, while I want to download arbitrary files; the second is that I can assume that the same url always returns the same file, so I don't need to download it again if it's still in the cache (while the Util methods always download the files when invoked).
However, I have some doubts.
My first question is about how caching works: currently I'm using this method to download images and videos to cache (in a chatting app), assuming that I don't need to care about when the files will be not more necessary, because the OS will delete them automatically. Is it so, right? Is it possible that the OS deletes files while I'm using them (for example immediately after storing them to the cache), or Android and iOS delete only older files?
I wrote this method to store arbitrary files. Is there any reasonable limit in MB to the file size that we can store in the cache?
Finally, I have a doubt about the callSerially that I used in the method. Previously I didn't use that, but I got odd results: my callbacks do UI manipulations and frequently (but not always) something went wrong. I solved all my callbacks problems adding the callSerially, so callSerially is the solution. But... why? The odd fact is that the ActionListener of Util.downloadUrlToFileSystemInBackground is called under the hood by the addResponseListener(callback) of a ConnectionRequest instance, so the callback is already invoked in the EDT (according to the javadoc). To be sure, I tested CN.isEdt() in the callbacks without adding the callSerially, and it returned true, so in theory callSerially is not necessary, but in practice it is. What's wrong in my reasoning?
Thank you for the explanations.
As far as I know the cache directory is just a directory that the OS is allowed to delete if it needs space. I don't think it will delete it for an active foreground application but that might vary.
There are no limits other than storage. But you still need to consider that the OS won't just clean that directory for you. It will only flush it when storage is very low and even then not always. So you still need to store data responsibly.
I think only the first callSeially has an impact. It defers the result to the next EDT loop instead of continuing in the existing thread.
Related
I have the two following methods and I am using them to store a special value locally and be able to access it on application restart:
(Store value locally:)
private void SaveSet(string key, string value)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
ISharedPreferencesEditor prefEditor = prefs.Edit();
prefEditor.PutString(key, value);
// editor.Commit(); // applies changes synchronously on older APIs
prefEditor.Apply(); // applies changes asynchronously on newer APIs
}
(Read it again:)
private string RetrieveSet(string key)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
return prefs.GetString(key, null);
}
This works perfectly. Now is it possible to access and edit this Shared Preferences externally? Unfortunately, I cannot find any file when searching in folder
Phone\Android\data\com.<company_name>.<application_name>\files
nor anywhere else. I want / try to edit this value from my computer, after connecting the phone to it. Is this possible?
Alternatively: Can anyone maybe show me how to create a new file in the given path above, write/read it programmatically and how it stays there, even if application is closed / started again? So I can then edit this file with my computer anyhow?
I tried it with the following code, but unfortunately it doesn't work / no file is created or at least i cannot see it in the given path above:
//"This code snippet is one example of writing an integer to a UTF-8 text file to the internal storage directory of an application:"
public void SaveValueIntoNewFile(int value)
{
var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "newFile.txt");
using (var writer = System.IO.File.CreateText(backingFile))
{
writer.WriteLine(value.ToString());
}
}
Would be very happy about every answer, thanks in advance and best regards
What you're looking for is where Android stores the Shared Preference file for applications that make use of it's default PreferenceManager.
I'd refer to this SO post which answers your question pretty well
SharedPreferences are stored in an xml file in the app data folder,
i.e.
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
or the default preferences at:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
SharedPreferences added during runtime are not stored in the Eclipse
project.
Note: Accessing /data/data/ requires superuser
privileges
A simple method is to use Android Device Monotor,you can open it by clicking Tools--> android-->Android Device Monotor...
For example:
The path in my device is as follows:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
And we notice three buttons in the upper right corner of the picture.
The first one is used toPull a file from the device,the second one is used to Push a file onto the device,and the last one is used to delete the preferences.xml file.
So we can pull the preferences.xml file from current device to our computer and edit it as we want, and then push the updated preferences.xml to the folder again.Then we will get the value of preferences.xml file .
My app features a button to record audio (and another to play it back when the recording is over). I send the recorded audio files on a server. On Android the files is recorded as .amr (mime type audio/amr) and can be played back.
On iOS however the file can neither be played back on the device (iPhone 4 or 4S) nor on a computer. ffmpeg -i reports
[mov,mp4,m4a,3gp,3g2,mj2 # 0x2fac120] moov atom not found
9gMjOnnmsj9JJZR3.m4a: Invalid data found when processing input
Please note that VLC cannot play it either.
I give the m4a extension because Voice recorder uses it (along with aac codec).
Here is the code I use (mostly based on https://github.com/codenameone/CodenameOne/blob/master/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java#L2768-L2794 ) :
audioMemoPath = ParametresGeneraux.getWRITABLE_DIR() + "AudioMemo-"
+ System.currentTimeMillis() +
(Display.getInstance().getPlatformName().equals("and")
? ".amr"
: ".m4a");
audioMemoMimeType = MediaManager.getAvailableRecordingMimeTypes()[0];
audioMemoRecorder = MediaManager.createMediaRecorder(audioMemoPath, audioMemoMimeType);
// If the permission audio has not been granted
// the audiomemorecoreder will be null
if (audioMemoRecorder != null) {
audioMemoRecorder.play();
boolean b = Dialog.show("Recording", "", "Save", "Cancel");
audioMemoRecorder.pause();
audioMemoRecorder.cleanup();
...
}
Moreover if I display the available mime types on iOS, it yields "audio/amr" which I doubt according to all the posts I could read that tell you amr is not supported on iOS. Looking at the source it appears amr is the by default mime type because it is always returned :
/**
* Gets the available recording MimeTypes
*/
public String [] getAvailableRecordingMimeTypes(){
return new String[]{"audio/amr"};
}
So my question : is it possible to record audio on iOS, and if it is, how can it be done ?
Any help appreciated,
Have you looked at the Capture class? That seems to be more straightforward.
https://www.codenameone.com/javadoc/index.html
Ok I got it working by overloading some methods of the MediaManager, namely getAvailableRecordingMimeTypes() and also createMediaRecorder() to prevent it from using its getAvailableRecordingMimeTypes method.
Here is the code for getAvailableRecordingMimeTypes():
/**
* Normally the method returns amr even for ios. So we overload the original
* method to return aac on ios.
* #return
*/
public static String[] getAvailableRecordingMimeTypes() {
if (Display.getInstance().getPlatformName().equals("ios")) {
return new String[]{"audio/aac"};
} else {
return new String[]{"audio/amr"};
}
}
createMediaRecorder() is left as is (copied without changes).
Now it is possible to record audio in iOS and play it back in both iOS and Android!
I need to serve files to authenticated users and recognise that using PHP will include a performance penalty, however what I've experienced so far seems to be unworkable.
I have a very simple controller action which sends the file:
public function view($id = null) {
$id = $id | $this->params->named['id'];
if (!$this->Attachment->exists($id)) {
throw new NotFoundException(__('Invalid attachment'));
}
$this->autoRender = false;
$this->Attachment->recursive = -1;
$file = $this->Attachment->findById($id);
$this->response->file(APP . DS . $file['Attachment']['dir']);
return $this->response;
}
A small (55 KB) PNG file takes 8 seconds to load using this method, where as if I move the file to the webroot directory and load it directly it takes less than 2.5 seconds. From looking at Chrome Dev Tools, the 'Receiving' part of the response is taking > 7s (compared with 1.5s direct).
A medium sized PDF file (2.5MB) takes over 2 minutes through CakeResponse, compared to ~4s directly. Surely I must be missing something in my controller action as this must be unworkable for anyone?
EDIT: CakePHP version is 2.4.1.
Thanks to the suggestion to use Xdebug I was able to quickly track down the problem.
In CakeResponse, there is the following function:
/**
* Flushes the contents of the output buffer
*
* #return void
*/
protected function _flushBuffer() {
//#codingStandardsIgnoreStart
#flush();
#ob_flush();
//#codingStandardsIgnoreEnd
}
Obviously with the error suppression operator the calls to flush and ob_flush would not normally cause a problem.
However, I also have Sentry as a remote debugging tool installed. This ignores the error suppression operator, reports that there is no buffer to flush (because ob_start has not been called) and in doing so outputs the contents of the file to the log file!
everyone!
I have a trouble. I tried to save excel file in jsf web application.
I generated file by my utils and trying to get "save" window, but I failed.
Here is my code:
<div>
<h:commandButton value="Apply" actionListener="#{hornPhonesBean.generateReport}"/>
</div>
and:
public void generateReport(ActionEvent event) {
System.out.println("GENERATE REPORT FROM = " + this.dateFrom + "; TO = " + this.dateTo);
try {
XSSFWorkbook workbook = (XSSFWorkbook) HornReportGenerator.getWorkbook(null, null);
String fileName = "1.xlsx";
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
// Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
ec.responseReset();
// Check http://www.w3schools.com/media/media_mimeref.asp for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
ec.setResponseContentType("application/vnd.ms-excel");
// Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
//ec.setResponseContentLength(contentLength);
// The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
OutputStream output = ec.getResponseOutputStream();
workbook.write(output);
output.flush();
output.close();
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
System.out.println("END");
} catch (Exception e) {
e.printStackTrace();
}
}
I read suggestions here and from another forums - everyone says I shouldnt use , but I didn't use it at all.
Then I thought that the problem could be in the
<ice:form>,
where I kept the
<h:commandButton>,
and I changed to
<h:form>,
but it didn't help.
Maybe the problem in the request - it has header Faces-Request partial/ajax. But I am not sure.
Please give me some ideas - I already spent 4 hours for this crazy jsf download issue)
Maybe the problem in the request - it has header Faces-Request partial/ajax. But I am not sure.
This suggests that the request is an ajax request. You can't download files by ajax. Ajax requests are processed by JavaScript which has for obvious security reasons no facilities to programmatically pop a Save As dialogue nor to access/manipulate client's disk file system.
Your code snippet does however not show that you're using ajax. Perhaps you oversimplified it too much or you're using ICEfaces which silently auto-enables ajax on all standard JSF command components.
In any case, you need to make sure that it's not sending an ajax request.
See also:
How to provide a file download from a JSF backing bean?
ICEfaces libary in classpath prevents Save As dialog from popping up on file download
Can GTK#'s FileChooserDialog be used as a unified file/URI dialog? I'd like it to accept http/https/ftp URIs without "rewriting" them (prepending local directory).
Even if I set LocalOnly=false and paste a http://.... uri into the text box inside the filechooser, I cannot get the original entry. Local directory is always prepended to the text.
I've done some research, and I don't think it's possible. At least not with the direct native C GTK+ API, which is what I tested.
In my testing, I always either got the local directory's path prepended to the http:// URI I had entered in the dialog, or I got back (null). I did call the get_uri() method, not just get_filename().
I also took a quick look, as a reference, at the GIMP application's File menu. As you probably know, GIMP provides the G in GTK+, so it can sometimes be used as a reference for ideas on how to use the toolkit. GIMP does not try to support URIs entered in the file chooser dialog, instead it has a dedicated Open Location command, that opens a simple dialog with just a GtkEntry.
I think you need to set local-only to FALSE and then use the GIO get_file ()/get_files () calls which return a GFile* accessible through the GIO File API and therefore through gvfs.
I found a solution / hack after all (in C#):
private string _extractUri(Widget wi) {
if (wi is Entry)
return ((wi as Entry).Text);
else if (wi is Container) {
foreach (Widget w in (wi as Container).Children) {
string x = _extractUri(w);
if (x!=null)
return x;
}
}
return null;
}
I'm not sure if that's always safe, but it worked for the standard FileChooserDialog. It will return the original string from the input field - even if standard Uri / File results are mangled.