I am getting this error, as if the directory was not being found. I had this code working before
and I simply just changed the path where I want to be created the file.
My app also has all the needed priviledge, as stated, it worked perfectly.
Before is was:
String path = STORAGE_PATH + "/DCMI" + "/Appdnp";
I simply changed to:
String path = STORAGE_PATH + "/.vilarica" + "/Appdnp";
And it started giving me java.io.FileNotFoundException: /storage/emulated/0/.vilarica/Appdnp/5WmK6658f9eKcp2h0QghGToCKOi2.jpg: open failed: ENOENT (No such file or directory)
I also tried changing the folder names, nothing worked,also making the first folder not hidden,etc.
I searched all around, found many resolutions of wrong paths, wrong names etc, but since it was working before I can't really see how that helps me here.
Storage path is:
public static final String STORAGE_PATH = Environment.getExternalStorageDirectory().toString();
But I don't think it's this since it was working before, and it gets the path correctly.
This is the part of the code from the class that represents these methods:
private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(final byte[] data, Camera camera) {
new Thread(new Runnable() {
#Override
public void run() {
String path = STORAGE_PATH + "/.vilarica" + "/Appdnp";
writeFile(path, data);
}
}, "captureThread").start();
startPreview();
}
};
public void writeFile(String path, byte[] data) {
Bitmap bitmap = null;
if (data != null){
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
if (bitmap != null){
Matrix matrix = new Matrix();
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
matrix.postRotate(90);
}else if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT){
matrix.postRotate(90);
matrix.postScale(1, -1);
}
Bitmap rotateBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix,false);
saveBmp2SD(path, rotateBmp);
rotateBmp.recycle();
}
}
private void saveBmp2SD(String path, Bitmap bitmap){
File file = new File(path);
if (!file.exists()){
file.mkdir();
}
String uid = user.getUid();
//String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = path + "/" + uid + ".jpg";
try {
FileOutputStream fos = new FileOutputStream(fileName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
Log.i("TAG", "Take picture success!");
Intent i = new Intent(this, DPCalc.class);
i.putExtra("filename", fileName);
i.putExtra("path", path);
startActivity(i);
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e("TAG", "The save file for take picture does not exists!");
} catch (IOException e) {
e.printStackTrace();
Log.e("TAG", "Take picture fail!");
}
}
.mkdir() only makes one directory, AND if it fails, you don't get an exception, it returns 'false'. Real java has a proper API but I don't think android gets the benefit of this nicer API, it's too far behind on libraries. So:
Don't call file.mkdir(); it's file.getParent().mkdirs().
Check the return value of mkdirs().
Note that 'false' on mkdirs() means either that the dir is already there and nothing was done, or that the operation failed; it is not possible to determine why it failed.
I mentioned this API sucks, right? I guess... you could in theory write your own mkdirs which walks up the tree 'manually'. Alternatively, use if (!path.isDirectory()) to check if it already exists, and fail (without being capable of telling the user why) if .mkdirs() returns false.
Related
I have written codes for adding sticker pack dynamically. But I want to know how to update the sticker pack after adding to whatsapp?
I can add sticker file to the pack which is listed on my app only but it is not reflecting in whatsapp. I tried adding files to the same location (file:///...) from where the pack was sent to whatsapp.
I want to try updating content provider. But how to do that? Can I add files to whatsapp's 'content://...' uri or should I update my app's content provider or anything else?
I am using react-native-whatsapp-stickers module for react-native.
react-native code
invoking after adding single sticker from UI
const addOne = (path, packName) =>{
// log('path ',path[0])
// log('packName ',packName)
RNWhatsAppStickers.addSticker(path[0],packName)
.then(res=>RNWhatsAppStickers.send(packName,packName))
.then(res=>console.log('response ',res))
}
Java code of module RNWhatsAppStickers
#ReactMethod
public void send(String identifier, String stickerPackName, Promise promise) {
Intent intent = new Intent();
intent.setAction("com.whatsapp.intent.action.ENABLE_STICKER_PACK");
intent.putExtra(EXTRA_STICKER_PACK_ID, identifier);
intent.putExtra(EXTRA_STICKER_PACK_AUTHORITY, getContentProviderAuthority(reactContext));
intent.putExtra(EXTRA_STICKER_PACK_NAME, stickerPackName);
try {
Activity activity = getCurrentActivity();
ResolveInfo should = activity.getPackageManager().resolveActivity(intent, 0);
if (should != null) {
activity.startActivityForResult(intent, ADD_PACK);
promise.resolve("OK");
} else {
promise.resolve("OK, but not opened");
}
} catch (ActivityNotFoundException e) {
promise.reject(ERROR_ADDING_STICKER_PACK, e);
} catch (Exception e) {
promise.reject(ERROR_ADDING_STICKER_PACK, e);
}
}
// saving image to same pack
public static void SaveImage(Bitmap finalBitmap, String name, String identifier) {
String root = path + "/" + identifier;
File myDir = new File(root);
myDir.mkdirs();
String fname = name;
File file = new File(myDir, fname);
if (file.exists()){
// Log.d("ReactNative","root "+root);
file.delete();
}
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.WEBP, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Please give any idea what to do? Thanks
I found the solution on official doc. You have to increase the image_data_Version for that pack after adding new images.
To track the image_data_Version you can query the ContentProvider of your app or Store the image_data_Version somewhere and make changes accordingly .
When I am using webp files for animated sticker packs it get rejected but if use same files for static stickers it get excepted. After looking all the codes I came to know that this is the last point where those files becomes problematic. But don not know how to identify if webp files stays as animated webp after saving. Please share your thought.
ps: I am using these webp files for whatsapp sticker packs. there is flag "animated_sticker_pack". we have to tell whatsapp that this pack contains only animated webp with proper fomrat. If I set it false then sticker pack get added (let it be static or animated webp). But if I set that flag true then those animated webp get rejected for pack showing error that There's problem with this pack.... So it might be that frames are lesser then it required. It get accepted as static means it might have single frame only.
To avoid issues regarding file type,format,size and all I am using the sample files from WhatsApp sample app
Code:
public static void SaveImage(Bitmap finalBitmap, String name, String identifier) {
String root = path + "/" + identifier;
File myDir = new File(root);
myDir.mkdirs();
String fname = name;
File file = new File(myDir, fname);
if (file.exists()){
file.delete();
}
try {
// FileOutputStream
FileOutputStream out = new FileOutputStream(file);
// Bitmap.compress
finalBitmap.compress(Bitmap.CompressFormat.WEBP, 100, out);
// close
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
////////////////////Other methods before saving images
private Bitmap downloadImageBitmap(String sUrl, String sIdentifier, String sName) {
imageFileName = getLastBitFromUrl(sUrl).replace(".png", ".webp");
identifier = sIdentifier;
name = sName;
Bitmap bitmap = null;
try {
InputStream inputStream = new URL(sUrl).openStream(); // Download Image from URL
bitmap = BitmapFactory.decodeStream(inputStream); // Decode Bitmap
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
#Override
protected Bitmap doInBackground(String... params) {
return downloadImageBitmap(params[0], params[1], params[2]);
}
protected void onPostExecute(Bitmap result) {
SaveImage(result, imageFileName, identifier);
}
You can download and save in doInBackground()
InputStream inputStream = new URL(sUrl).openStream(); // Download Image from URL
FileOutputStream out = new FileOutputStream(file);
Then make a loop where you read bytes in a buffer from input stream and write to output stream.
Don't forget to close all streams when done.
I'm developing image editor app.. so each time the user have to save the image.
So first i inserted
String savedImageURL = MediaStore.Images.Media.insertImage(
getContentResolver(),
bitmap,
"Bird",
"Image of bird"
);
this code, but it creating new file instead of overwriting.
So i use another method
public String saveImage(String folderName, String imageName) {
String selectedOutputPath = "";
if (isSDCARDMounted()) {
File mediaStorageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), folderName);
// Create a storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("PhotoEditorSDK", "Failed to create directory");
}
}
// Create a media file name
selectedOutputPath = mediaStorageDir.getPath() + File.separator + imageName;
Log.d("PhotoEditorSDK", "selected camera path " + selectedOutputPath);
File file = new File(selectedOutputPath);
try {
FileOutputStream out = new FileOutputStream(file,true);
if (parentView != null) {
parentView.setDrawingCacheEnabled(true);
parentView.getDrawingCache().compress(Bitmap.CompressFormat.JPEG, 80, out);
}
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return selectedOutputPath;
}
But it also didn't work.
Does anyone know about overwrite a bitmap in the same name?
Pass false as 2nd argument, to set append to false, so that you will overwrite the existing file:
FileOutputStream out = new FileOutputStream(file,false);
Check out the constructor documentation:
here is your code:
public String saveImage(String folderName, String imageName) {
String selectedOutputPath = "";
if (isSDCARDMounted()) {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), folderName);
// Create a storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("PhotoEditorSDK", "Failed to create directory");
}
}
// Create a media file name
selectedOutputPath = mediaStorageDir.getPath() + File.separator + imageName;
Log.d("PhotoEditorSDK", "selected camera path " + selectedOutputPath);
File file = new File(selectedOutputPath);
if (file.exists())
{
try {
file.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
file.createNewFile();
FileOutputStream out = new FileOutputStream(file,false);
if (parentView != null) {
parentView.setDrawingCacheEnabled(true);
parentView.getDrawingCache().compress(Bitmap.CompressFormat.JPEG, 80, out);
}
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return selectedOutputPath;
}
I also had this situation, but it turns out that this is not a problem with saving, but with displaying in ImageViev. I used Glide, and it turns out to be stored in the cache when outputting. And I did not change the name and path of the file. That is, I rewrote them. But Glide did not know this. He thought they were the same file. To fix this problem, I added the following
Glide.with(context)
.load(file)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(view)
If you also have this situation and these solutions helped you, I'm glad to this.
I am unable to figure out how to share a video from SnapChat to my app while WhatsApp can do it without problems so it is possible.
Since SnapChat works with a contentprovider I figured I should just query the uri using the android contentresolver method. Using the databaseUtils I dumped the cursor to logcat but all it gives me back is the displayname and the filesize. I see no way how to get the actual file.
Help me out guys. What am I missing?
A bit late but i managed to solve it. Here is the solution. I added comments in the code to explain what i am doing.
void handleSendVideo(Intent intent)
{
// get title
String title = intent.getExtras().getString(Intent.EXTRA_SUBJECT);
// get the uri for the video
mVideoUri = (Uri) intent.getExtras().get(Intent.EXTRA_STREAM);
// check if it originates from snapchat
if (mVideoUri.getAuthority().equals(UriHelper.SNAPCHAT_FILE_PROVIDER))
{
Intent mRequestFileIntent;
ParcelFileDescriptor mInputPFD;
mRequestFileIntent = new Intent(Intent.ACTION_PICK);
mRequestFileIntent.setType("video/*");
// Query the content resolver to get the name of the file. Beware that you will not find
// the actual file here. You must read it from the fileDescriptor.
Cursor fileDataCursor = getContentResolver().query(mVideoUri, null, null, null, null);
String fileName = "";
if (fileDataCursor != null)
{
fileDataCursor.moveToFirst();
fileName = fileDataCursor.getString(0);
fileDataCursor.close();
}
// something is wrong... return
if (fileName.isEmpty())
{
return;
}
try {
// open the file descriptor that belongs to the file given to us by snapchat.
mInputPFD = getContentResolver().openFileDescriptor(mVideoUri, "r");
// fetch the descriptor
FileDescriptor fd = mInputPFD.getFileDescriptor();
// create in input stream from descriptor
InputStream inputStream = new FileInputStream(fd);
// This is the file that will be created
File targetFile = new File(Environment.getExternalStorageDirectory(), fileName);
// Open a outputstream connected to the file
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
// Create a buffer with a size equal to what is available from the inputstream.
// Note: we dont have to loop here because the file is available on the storage.
byte[] buffer = new byte[inputStream.available()];
// Read all data into the buffer
inputStream.read(buffer);
// Write the buffer to the outputstream
fileOutputStream.write(buffer);
// close all streams, our file is ready.
inputStream.close();
fileOutputStream.close();
mInputPFD.close();
// check if the new file exists.
if (targetFile.exists())
{
// add the file to the android MediaProvider
mVideoUri = addVideo(targetFile);
}
else
{
DialogMaker.showAlertMessage(this, "Could not read video", "unable to read video from SnapChat.", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
finish();
}
});
return;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
logger.i("videoUri: " + mVideoUri.toString());
logger.i("videoPath: " + mVideoUri.getPath());
} catch (Exception e) {
e.printStackTrace();
}
}
public Uri addVideo(File videoFile) {
ContentValues values = new ContentValues(3);
values.put(MediaStore.Video.Media.TITLE, "My video title");
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.DATA, videoFile.getAbsolutePath());
return getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
}
I would like to load files directly from expansion OBB file by using AssetManager. I implemented my own FileHandleResolver
public class CustomFileHandleResolver implements FileHandleResolver
{
#Override
public FileHandle resolve(String fileName) {
return new CustomFileHandle(fileName);
}
}
I set it to my AssetManager. I created my own FileHandle and I override read() function
#Override
public InputStream read()
{
InputStream input = null;
try {
input = GameInfo.expansionFile.getInputStream(file.getPath().replace('\\', '/'));
} catch (IOException e) {
e.printStackTrace();
}
return input;
}
It loads all the files like .PNG, .PACK, .FNT, except .OGG files, so I guess that all sound files won't be loaded. I'm getting this error:
com.badlogic.gdx.utils.GdxRuntimeException: com.badlogic.gdx.utils.GdxRuntimeException: Couldn't load dependencies of asset: SFx/button_click.ogg
And this error:
com.badlogic.gdx.utils.GdxRuntimeException: java.lang.ClassCastException: com.solidgamesstudio.threedefendersn.framework.CustomFileHandle cannot be cast to com.badlogic.gdx.backends.android.AndroidFileHandle
I read that zip can not be compressed. In 7zip I selected compression to "Store" so that it's not compressed at all, but still this problem occurs.
I traversed what is happening when files are being loaded and I found that AssetManager calls my CustomFileHandleResolver which creates CustomFileHandle. For every file that is not .OGG it calls InputStream read(). In this function it loads the file from the zip and it's fine. But as I said when it comes to loading .OGG it doesn't call this function. So it's not even trying yet to get the file from the zip. Question is, why .OGG file doesn't call InputStream read() in CustomFileHandle()?
UPDATE
I traversed more and I found out that it won't call InputStream read() because it can't create a Sound from FileHandle somehow. Clue to this is
CustomFileHandle cannot be cast to AndroidFileHandle
While to create a sound you need to pass fileHandle.
public Sound newSound (FileHandle fileHandle);
This is called from SoundLoader
#Override
public void loadAsync (AssetManager manager, String fileName, FileHandle file, SoundParameter parameter) {
sound = Gdx.audio.newSound(file);
}
And that soundLoader uses my CustomFileHandleResolver. I don't know if Sounds are handled differently then other types of files. But by default AssetManager uses
public class InternalFileHandleResolver implements FileHandleResolver {
#Override
public FileHandle resolve (String fileName) {
return Gdx.files.internal(fileName);
}
}
I can't get into Gdx.files.internal to see if there are any special handling for Sounds.
UPDATE
Further analysis give me clue that the main problem is this as mentioned before.
CustomFileHandle cannot be cast to AndroidFileHandle
I don't know why it's casting my FileHandle to AndroidFileHandle while loading OGG file. If it loads fine other type of files, that probably means it doesn't do casting for them. That means that OGG is special and it needs casting. Any clues?
I have not found a way to load sound files from the zip file. Problem is that AssetManager loads sound files differently than other file types. Problem was that it is casting FileHandle to AndroidFileHandle, and since CustomFileHandle extends FileHandle it's impossible to cast it to AndroidFileHandle. I found no way to go around this, because it's deeply rooted.
CustomFileHandle cannot be cast to AndroidFileHandle
In this situation I had to take out all sound files from the OBB file and put them together with the app. I created another instance of AssetManager just for loading sounds. So, sounds are loaded normally as you would with AssetManager and for any other type of file I used AssetManager that uses my own FileHandlerResolver which uses my own FileHandle class that returns a file from the zip. The only problem with this approach is that you are limited to having sounds files only up to 50 MB.
I solved this problem extracting the zip to a specific folder,
and then reading from that external folder.
The extraction of the zip is done by these methods:
public void extract(){
String packageName = getPackageName();
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + "/Android/obb/" + packageName);
if (expPath.exists()) {
String strMainPath = null;
try {
strMainPath = expPath + File.separator + "main."
+ getPackageManager().getPackageInfo(
getPackageName(), 0).versionCode + "."
+ packageName + ".obb";
Log.e("Extract File path", "===>"+strMainPath);
File f=new File(strMainPath);
if(f.exists()){
Log.e("Extract From File path", "===> not exist");
}
else
{
Log.e("Extract From File path", "===> exist");
}
String pathToExtract = Environment.getExternalStorageDirectory()+"/"+Cons.FOLDERNAME;
Log.e("Extract to path", "===>"+pathToExtract);
flag = extractZip(strMainPath,pathToExtract);
Log.e("After Extract Zip", "===>"+flag);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
private boolean extractZip(String pathOfZip,String pathToExtract)
{
int BUFFER_SIZE = 1024;
int size;
byte[] buffer = new byte[BUFFER_SIZE];
try {
File f = new File(pathToExtract);
if(!f.isDirectory()) {
f.mkdirs();
}
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(new FileInputStream(pathOfZip), BUFFER_SIZE));
fileNum=0;
try {
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
String path = pathToExtract +"/"+ ze.getName();
if (ze.isDirectory()) {
File unzipFile = new File(path);
if(!unzipFile.isDirectory()) {
unzipFile.mkdirs();
}
}
else {
updateFileNum();
FileOutputStream out = new FileOutputStream(path, false);
BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);
try {
while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
fout.write(buffer, 0, size);
}
zin.closeEntry();
}catch (Exception e) {
Log.e("Exception", "Unzip exception 1:" + e.toString());
}
finally {
fout.flush();
fout.close();
}
}
}
}catch (Exception e) {
Log.e("Exception", "Unzip exception2 :" + e.toString());
}
finally {
zin.close();
}
return true;
}
catch (Exception e) {
Log.e("Exception", "Unzip exception :" + e.toString());
}
return false;
}
Note: Extract it to .Android folder, otherwhise users will have direct acces to the assets. For example they will see the images in the Gallery app.
Well, I'm doing this currently. Whenever you need to get a real FileHandle in order for the sound loading mechanism to work (or in any other case were the casting to AndroidFileHandle is bothering you), unzip that file to a local directory and reuse it if needed:
public static FileHandle getRealFileHandle(String zipEntryPath, ZipFile zipFile) {
if (Gdx.files.local(zipEntryPath).exists()) {
return Gdx.files.local(zipEntryPath);
} else {
Gdx.app.log(TAG, "Unzipping file '" + zipEntryPath + "'...");
try {
FileHandle unzippedFile;
ZipEntry entry = zipFile.getEntry(zipEntryPath);
if (entry != null) {
unzippedFile = Gdx.files.local(zipEntryPath);
InputStream is = zipFile.getInputStream(entry);
byte[] buffer = new byte[65536];
int readLength;
while ((readLength = is.read(buffer)) >= 0) {
unzippedFile.writeBytes(buffer, 0, readLength, true);
}
return unzippedFile;
} else {
Gdx.app.error(TAG, "Entry '" + zipEntryPath + "' not found inside zip file.");
}
} catch (IOException ioe) {
Gdx.app.error(TAG, "A problem occurred while writing to the local file.");
}
}
return null;
}