Device - Nexus One
OS - Android 2.3.4
Class - CameraRoll
Method - addBitmapData()
Error - [ErrorEvent type="error" bubbles=false
cancelable=false eventPhase=2 text="Error #2038: File I/O Error."
errorID=2038]
I'm trying to develop a photo app but am having problems saving the full sized version of the image back to the CameraRoll. This is very frustrating as I've only seen examples saving the stage to CameraRoll (which I can get to work).
Is there a limitation to saving back to CameraRoll?? When I try to load an image (2592 x 1944) and save it directly back to CameraRoll using addBitmapData(), I get the following error.
[ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Error #2038: File I/O Error." errorID=2038]
Here's a code sample.
// class vars for CameraRoll and Loader
private var _cameraRoll:CameraRoll = new CameraRoll();
private var _loader:Loader = new Loader();
// launch _cameraRoll
private function launchCameraRoll(e:MouseEvent):void {
_cameraRoll.addEventListener(MediaEvent.SELECT, loadImg);
_cameraRoll.browseForImage();
}
// open selected image using _loader
private function loadImg(e:MediaEvent):void {
if (e.data.isAsync) {
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, saveImage);
_loader.loadFilePromise(e.data);
} else {
_loader.loadFilePromise(e.data);
saveImage();
}
}
// once loaded, save image immediately back to _cameraRoll
private function saveImage(e:Event = null):void {
_cameraRoll.addEventListener(ErrorEvent.ERROR, onError);
_cameraRoll.addEventListener(Event.COMPLETE, onComplete);
var bmd:BitmapData = new BitmapData(_loader.width, _loader.height);
bmd.draw(_loader);
_cameraRoll.addBitmapData(bmd);
}
// trace error
private function onError(e:ErrorEvent):void {
trace(e); // [ErrorEvent type="error" bubbles=false cancelable=false eventPhase=2 text="Error #2038: File I/O Error." errorID=2038]
}
// show complete status
private function onComplete(e:Event):void {
trace("complete");
}
I had the same problem (even with real small images).
I solved it by uncommenting this line in the <android><manifestAdditions> section of the [Application]-app.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
have you tried to save image in lower res? e.g. 1024x768? Maybe there is a limitation on the size that your device supports, I've found this or maybe the disk space is problem like mentioned here
best regards
Related
I'm just trying to resize an image after the user launches the Image Picker from my app and chooses an image file on the local device (handling a remote image from Dropbox or something will be another battle) and while this has worked for me previously, now I'm getting this exception:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1105296364, result=-1, data=Intent { dat=content://com.android.externalstorage.documents/document/primary:Download/20170307_223207_cropped.jpg flg=0x1 }} to activity {my.app/MainActivity}: java.io.FileNotFoundException: No content provider: /document/primary:Download/20170307_223207_cropped.jpg
This occurs after the image is chosen in the Picker, because I'm running my "processing" code to locate the image, resize it, and copy it to a subfolder in the app's folder.
Like I said, this worked, but I'm not sure what's wrong now. I've tried this on the emulator as well as on my Galaxy S10 via USB debugging and it's the same result. The image is in the local storage "Download" folder on the emulator as well as my own device.
The URI looks weird (I mean the picture is just in the local storage "Download" folder) but I'm no URI expert so I assume it's fine, because that's what the Image Picker returns.
Here's the immediate code that's throwing the exception (specifically, the ImageDecoder.decodeBitmap call):
private fun copyFileToAppDataFolder(
context: Context,
imageTempPath: String
): String {
// ensure we are sent at least a non-empty path
if (imageTempPath.isEmpty()) {
return ""
}
val appDataFolder = "${context.dataDir.absolutePath}/images/firearms"
var filename = imageTempPath.substringAfterLast("/", "")
if (filename.isNullOrBlank()) {
filename = imageTempPath.substringAfterLast("%2F", "")
}
// couldn't parse filename from Uri; exit
if (filename.isNullOrBlank()) {
return ""
}
// get a bitmap of the selected image so it can be saved in an outputstream
var selectedImage: Bitmap? = null
selectedImage = if (Build.VERSION.SDK_INT <= 28) {
MediaStore.Images.Media.getBitmap(context.contentResolver, Uri.parse(imageTempPath))
} else {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, Uri.parse(imageTempPath)))
}
if (selectedImage == null) {
return ""
}
val destinationImagePath: String = "$appDataFolder/$filename"
val destinationStream = FileOutputStream(destinationImagePath)
selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, destinationStream)
destinationStream.close()
return destinationImagePath
}
That above function is called from my ViewModel (that processFirearmImage function is just calling the one above), where I send the result URI from the image Picker as well as the Application Context:
// this event is fired when the Image Picker returns
is AddEditFirearmEvent.AssignedPicture -> {
val resizedImagePath = ShotTrackerUtility.processFirearmImage(
event.applicationContext, // this is from LocalContext.current in Composable
event.value // result uri from image picker
)
_firearmImageUrl.value = resizedImagePath
}
I don't know, lol. I can't believe this is such a difficult thing but information for this sure seems sparse (for Compose especially, but even so) but I don't really consider launching an Image Picker and resizing the resulting image to be that weird. Any help would be great from you smart people.
Taking a step away from programming problems and coming back seems about the best bet sometimes, lol.
I came back tonight and within a couple minutes noticed that I was sending an improper Uri to the ImageDecoder.createSource method that was causing the exception. Basically this was happening:
val imageTempPath = theUriReturnedFromImagePicker.path ?: ""
ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, Uri.parse(imageTempPath)))
And it should've been:
val imageUrl = theUriReturnedFromImagePicker
ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, imageUri))
As I mentioned in the OP, this originally worked but I must've changed code around a bit (arguments I'm sending to various methods/classes, mostly). I'm also using that Uri.path part to get the filename of the image chosen so I overlooked and/or got confused to what I was sending to ImageDecoder.createSource.
Doh. Maybe someone else will do something dumb like me and this can help.
I was looking for a way to display a phone's gallery in a GridView. Came across the local_image_provider library. It does it's job pretty well. Not a lot of problems except the fact that using the "findLatest" method on the LocalImageProvider does not return the newest images from the gallery (it is the only way to show take images from the gallery and put them into a list as far as I know). Instead, the list I create from using this method on for example 25 newest images from the gallery skips quite a lot of images. It shows returns mostly screenshots and some downloaded pictures, along with some images taken with my camera. (I am testing this app on my phone). I simply can not find any info on this library so I have decided to ask myself. Here is some relevant code:
import 'package:local_image_provider/local_image_provider.dart' as lip;
Future<List<ImageProvider>> getLocalImage() async {
lip.LocalImageProvider imageProvider = lip.LocalImageProvider();
bool hasPermission = await imageProvider.initialize();
if ( hasPermission) {
List<lip.LocalImage> images = await imageProvider.findLatest(20);
if ( !images.isEmpty ) {
lip.LocalImage image = images.first;
lip.DeviceImage deviceImg = lip.DeviceImage( image );
List<ImageProvider> list = [];
images.forEach((element) {
list.add(lip.DeviceImage(element));
});
return list;
}
else {
print("No images found on the device.");
throw Exception();
}}
else {
print("The user has denied access to images on their device.");
throw Exception();
}
}
I then use this function as a future for a future builder which builds a gridview.
I am trying to save images i take with the camera on my phonegap app to a more permanent destination. I run the app on iOS and Android so i need this to work on ios and android.
So far i can get the image file name and temporary storage path.
I can also get the base file system path for the device i am running (currently an Android Galaxy S7 edge).
However, i am unable to move/copy the file from the temporary storage to the new one as it fails.
Ive got the following code and ive got a try/catch around the code which moves the images. in the catch i have an alert which triggers always as there is something wrong with my code.
Ive tried changing the moveTo to CopyTo and changing the code from output.moveTo to things like cordova.file.moveTo without success.
If anyone could help i would be greatful.
Thanks Kind regards
const tempFilename = imageURI.substr(imageURI.lastIndexOf('/') + 1);
const tempBaseFilesystemPath = imageURI.substr(0, imageURI.lastIndexOf('/') + 1);
const newBaseFilesystemPath = cordova.file.dataDirectory;
var output=tempBaseFilesystemPath+tempFilename;
var saveOutput=newBaseFilesystemPath+tempFilename;
alert(tempFilename+","+tempBaseFilesystemPath+","+newBaseFilesystemPath+","+tempFilename);
try{
output.moveTo(tempBaseFilesystemPath, tempFilename, newBaseFilesystemPath,tempFilename)
.then(function (success) {
alert("done");
}, function (error) {
alert("not done");
});
}
catch{
alert("didnt run");
}
const storedPhoto = output;
localStorage.setItem('imageLoc',storedPhoto);
localStorage.setItem('goodPic','not');
},
function( message ) {
},
{
quality: 30,
destinationType: Camera.DestinationType.FILE_URI,
});
}
I am trying to build an augmented reality app and in that app i want some prefabs to load dynamically. So that with some 3d model prefabs on unity store i created myasset.unity3d . I am using WWW.LoadFromCacheOrDownload to download that unity3d file and using it.
When i am debugging in my laptop the file is downloading and i am using it as i want but when i add build in and try it on android phone download is not happening.
The code i am using to download is
IEnumerator DownloadAndCache() {
while (!Caching.ready)
yield return null;
string bundleURL = "http://s3-ap-southeast-1.amazonaws.com/cf-export/sofa.unity3d";
www = WWW.LoadFromCacheOrDownload (bundleURL, 1);
yield return www;
Debug.Log (www.assetBundle);
AssetBundle bundle = www.assetBundle;
}
So please help me .
i also tried other way of doing it but still i did not succeed
IEnumerator Start () {
WWW www = WWW.LoadFromCacheOrDownload (BundleURL, 1);
yield return www;
AssetBundle bundle = www.assetBundle;
AssetBundleRequest request = bundle.LoadAssetAsync (AssetName, typeof(GameObject));
yield return request;
GameObject obj = request.asset as GameObject;
Instantiate (obj);
bundle.Unload(false);
www.Dispose();
}
The above two methods are working in when testing in unity IDE but if i build using android and install it in android device its not working. I gave internet access to app while building
I printed the error using a text in mobile and please check the screen shot attached below
the first error text is www (variable for downloading) and second is www.error ( actual error). i did not get what that error meant. So please help me
the issue is asset bundle version is not sync with app version. So while making asset bundle i added small code which made my code working. To make Asset bundle my code is
using UnityEditor;
public class CreateAssetBundles
{
[MenuItem ("Assets/Build AssetBundles")]
static void BuildAllAssetBundles ()
{
BuildPipeline.BuildAssetBundles ("Assets/AssetBundles", BuildAssetBundleOptions.None, BuildTarget.Android);
}
}
To use AssetBuundle for Mobile platform:
While creating AssetBundle it must be platform specific. For Android use following Script to create AssetBundle. put it inside "Editor"
using UnityEngine;
using System.Collections;
using UnityEditor;
public class ExportAssetBundles : Editor {
[MenuItem("Assets/Build AssetBundle")]
static void ExportResource()
{
string path = "Assets/AssetBundle/myAssetBundle.unity3d";
Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path,
BuildAssetBundleOptions.CollectDependencies
| BuildAssetBundleOptions.CompleteAssets,BuildTarget.Android);
}
}
Then Create folder "AssetBundle" inside Asset
When you create Assetbundle the AssetBundle.unity3d file will store inside this folder
Now you need to use Created Assetbundle at runtime. For this,
Write a script AssetBundleAugmenter
public class AssetBundleAugmenter : MonoBehaviour {
public string AssetName;
public int Version;
private GameObject mBundleInstance = null;
private bool mAttached = false;
void Start() {
StartCoroutine(DownloadAndCache());
}
// Update is called once per frame
IEnumerator DownloadAndCache() {
while(!Caching.ready)
yield return null;
//you can use remote URL like: www.arprabhu.com/assetBundle OR Local URL
// example URL of file on PC filesystem (Windows)
// string bundleURL = "file:///D:/Unity/AssetBundles/MyAssetBundle.unity3d";
// example URL of file on Android device SD-card
string bundleURL = System.IO.File.ReadAllText("/mnt/sdcard/filepath.txt");
Debug.Log ("Asset Loaded");
using (WWW www = WWW .LoadFromCacheOrDownload(bundleURL, Version)) {
yield return www;
if (www .error != null)
throw new UnityException("WWW Download had an error: " + www .error);
AssetBundle bundle = www .assetBundle;
if (AssetName == "")
Instantiate(bundle.mainAsset);
else
Instantiate(bundle.LoadAsset(AssetName));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
}
}}
Attach this Script to Empty GameObject in your Scene
Congrats!, you are ready to go.
Try checking your androidManifest.xml for the permission to connect to external servers.
Then, if everything seems alright and still doesn't work, my advice is to download the Device Console plugin for the Unity Editor, which is a really nice way to access the internal Android Debugger.
https://www.assetstore.unity3d.com/en/#!/content/44935
Or if you're truly brave: use logcat within the terminal...
I have an app developed and want a link to open a gallery of images. The images are locally bundled and included in my assets/www/img directory.
I would like this to seamlessly resemble the native image gallery.
Is this possible?
I have tried to use the following but cant get a response.
document.addEventListener("deviceready", onDeviceReady, false);
// PhoneGap is ready
//
function onDeviceReady() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, fail);
}
function onFileSystemSuccess(fileSystem) {
fileSystem.root.getDirectory("img/china", {create: false, exclusive: false}, getDirSuccess, fail);
}
function getDirSuccess(dirEntry) {
// Get a directory reader
var directoryReader = dirEntry.createReader();
// Get a list of all the entries in the directory
directoryReader.readEntries(readerSuccess,fail);
}
var numDirs = 0;
var numFiles = 0;
var readerTimeout = null, millisecondsBetweenReadSuccess = 100;
function readerSuccess(entries) {
var i = 0, len = entries.length;
for (; i < len; i++) {
if (entries[i].isFile) {
numFiles++;
entries[i].file(fileSuccess,fail);
} else if (entries[i].isDirectory) {
numDirs++;
getDirSuccess(entries[i]);
}
if (readerTimeout) {
window.clearTimeout(readerTimeout);
}
}
if (readerTimeout) {
window.clearTimeout(readerTimeout);
}
readerTimeout = window.setTimeout(weAreDone, millisecondsBetweenReadSuccess);
}
// additional event to call when totally done
function weAreDone() {
// do something
}
The Camera docs cover this. Is this not working for you?
If Camera.sourceType = Camera.PictureSourceType.PHOTOLIBRARY or Camera.PictureSourceType.SAVEDPHOTOALBUM, then a photo chooser dialog is shown, from which a photo from the album can be selected.
http://docs.phonegap.com/phonegap_camera_camera.md.html
Also refer...
Browsing Image Gallery With PhoneGap & Android
I am porting an application I wrote for the iphone over to Android and I wanted to start with the Camera functionality. I looked around and Googled and I could not find where it was documented on how to browse the image gallery on the Android device. I decided to bang out some simple modifications to the existing Camera object to support that functionality for now. I the future, hopefully this is implemented better by the PhoneGap guys and I can put this code out to pasture.
This code needs to be added to the phoneGap.js file. It creates the prototype for the new method for browsing the gallery on the device
1 //
2 // add the new prototype to support the browse function
3 //
4 // i have created a new class 'PixCameraLauncher' so I did not have to modify
5 // the original class file provided by PhoneGap
6 //
7 com.phonegap.CameraLauncherProxy.prototype.browseImages = function(quality) {
8 return PhoneGap.exec("com.phonegap.gapTest.PixCameraLauncher", "browseImages", [quality]);
9 };
This the modification I made to the CameraLauncher class provided by PhoneGap. Add a new intent to launch the image gallery
01 /**
02 * Get a picture from the Image Gallery.
03 *
04 * in DroidGap.onActivityResult, which forwards the result to
05 * this.onActivityResult.
06 *
07 * #param quality
08 * Compression quality hint (0-100: 0=low quality & high
09 * compression, 100=compress of max quality)
10 */
11 public void browseImages(int quality) {
12 this.mQuality = quality;
13
14 // Display camera
15 Intent intent = new Intent(Intent.ACTION_PICK,
16 android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
17
18 this.ctx.startActivityForResult((Plugin) this, intent);
19 }
Also we need to handle the returned file. I left it simple, but this could be cleaned up a bit What I do is assume that if there is a URI in the data field, then an image has been picked and should be processed by the utility. In the beginning of the
1 void onActivityResult(int requestCode, int resultCode, Intent intent)
add the code below. This will handle re-assigning the URI to the selected file
1 // If image available
2 if (resultCode == Activity.RESULT_OK) {
3
4 //
5 // this means the user selected an image, so just pass it back!!
6 //
7 if (intent.getData() != null) {
8 imageUri = intent.getData();
9 }
After a little more testing and code cleanup, I will make the complete project for the android work and the iphone work available. If there are any questions, post it below.
The problem is you are trying to use the File API to read files inside of your applications assets directory. That directory is not actually part of the file system. When you do a window.requestFileSystem(LocalFileSystem.PERSISTENT, ...) you are actually getting the /sdcard (most probably) back. So when you try to load "img/china" that directory does not exist.
You would need to copy the files from your assets directory to the local file system during the onCreate method of your main Java class. Then you can use the DirectoryReader to get a list of all the files.
Alternatively, if you know all the images ahead of time you could create your own JSON file that lists them all. Then load that file and be able to loop through all the entries to create your own gallery.