I'm having a problem for a few days now and I can't find the solution. I've been looking on the internet for hours but I can't find any answer that works for me.
I'm making an app where I have to send pictures to a webservice. I'm using Fishbun to access to the phone gallery and pick some pictures. When the pictures are picked, I use Bitmapfactory.decodeFile() with the returned path from Fishbun to display the pictures on the screen. It works good. The path at this moment is:
/storage/emulated/0/DCIM/Camera/IMG_20160321_044346.jpg
Then, I save the path in a ArrayList which will be send to the webservice.
When I use BitmapFactory.decodeFile() on the path from the ArrayList, it returns null. The path is exactly the same as the first time, but it seems like I can't decode it twice.
If I restart the app and take back the same picture as the first time in fishbun, the decodeFile() also returns null. Here is the code ....
Fishbun activity result:
protected void onActivityResult(int requestCode, int resultCode,
Intent imageData) {
super.onActivityResult(requestCode, resultCode, imageData);
switch (requestCode) {
case Define.ALBUM_REQUEST_CODE:
if (resultCode == RESULT_OK) {
path = imageData.getStringArrayListExtra(Define.INTENT_PATH);
int i = 0;
int error=0;
while(i<path.size()){
Bitmap bmp = BitmapFactory.decodeFile(path.get(i));
if(bmp.getWidth()<bmp.getHeight()) // bmp is not null the first time, null the second time
{
error++;
path.remove(i);
i--;
}
i++;
}
if(error>=1){
Toast.makeText(newStep7.this,R.string.rerrors, Toast.LENGTH_LONG).show();
}
if(path.size()>0) {
mainAdapter.changePath(path); //display the picture on the screen without changing the path, the method name is kinda wrong
}
break;
}
}
}
This is the decodeFile() before sending my request:
String imgPath = rep.getImgList().get(0);
File file = new File(imgPath);
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
Log.d("startedfromthebottom", file.getAbsolutePath()); //show the same path as in the activityresult above
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); //bitmap is always null
bitmap.compress(Bitmap.CompressFormat.JPEG,100,os);
Could you help me find why my bitmap is null when I decode the same file twice ?
EDIT:
I found the image file in my Android Device Monitor. The image size is 0 and can't be used anymore after the Outputstream
UPDATE: Little code change after applying bwt answer:
String imgPath = rep.getImgList().get(0);
File file = new File(imgPath);
AtomicFile atomicFile =
new AtomicFile(file);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = atomicFile.startWrite();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
Log.d("showmethebitmap", bitmap.toString()); //Error: bitmap is null !
oos = new ObjectOutputStream(fos);
bitmap.compress(Bitmap.CompressFormat.JPEG,0,oos);
oos.writeObject(bitmap);
oos.flush();
atomicFile.finishWrite(fos);
...
} catch (IOException e) {
atomicFile.failWrite(fos);
throw e;
} finally {
if (oos != null) oos.close();
}
I know the question has been answered ,however if someone still getting null make sure that the application has permission to read user's files
You should load the image first, then open the output stream, as this erases the existing content.
Log.d("startedfromthebottom", file.getAbsolutePath()); //show the same path as in the activityresult above
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); //bitmap is always null
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG,100,os);
This is a bit dangerous because if something goes wrong the image is lost. It could be a good idea to use an AtomicFile.
Edit (based on your updated code) :
String imgPath = rep.getImgList().get(0);
File file = new File(imgPath);
AtomicFile atomicFile = new AtomicFile(file);
FileOutputStream fos = null;
try {
// read the current image
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
// open the stream (backup the current content)
// from now on (and until finishWrite/failWrite) we cannot read the file directly
fos = atomicFile.startWrite();
Log.d("showmethebitmap", bitmap.toString()); //Error: bitmap is null !
OutputStream oos = new BufferedOutputStream(fos);
bitmap.compress(Bitmap.CompressFormat.JPEG,0,oos);
// flush but do not close the stream (#see AtomicFile doc)
oos.flush();
// close the stream, remove the backup
atomicFile.finishWrite(fos);
...
} catch (IOException e) {
// recover the content from the backup
atomicFile.failWrite(fos);
throw e;
}
Related
In my application, I have to store a bitmap as a PNG file in shared memory, to make it visible for Gallery app. First, I tried to store the image in /Android/data/package.name/files/Pictures. I got this path from context.getExternalFilesDir(Environment.DIRECTORY_PICTURES). Images stored in this directory are not detected by Gallery. Then I read a few articles and SO posts about MediaStore and I tried to save my image with it.
This is a function that I use to store bitmap. It does not throw any exception, returns true, bitmap.compress() also returns true but I can't find any PNG image in device's memory. I tried to look for it using Gallery and file manager. I also tried to change it to save JPEG instead of PNG but it also does not work.
Could you help me to figure out why this function does not save image to device's store?
I tested it on Samsung A52s 5G, Android 12, OneUI 4.0.
private boolean saveImageToStorageAndroidQ(Bitmap bitmap, String filename) {
filename = filename + ".png";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
final ContentResolver resolver = getActivity().getContentResolver();
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri uri = resolver.insert(contentUri, values);
try {
OutputStream imageOutStream = resolver.openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.PNG, 95, imageOutStream);
imageOutStream.flush();
imageOutStream.close();
return true;
} catch (Exception e) {
return false;
} finally {
if (uri != null)
resolver.delete(uri, null, null);
}
}
You can also use this method. Thought it's long, it's better as the other one has been deprecated. Use this code:
String filename = "name.png";
File sd = Environment.getExternalStorageDirectory();
File dest = new File(sd, filename);
try {
FileOutputStream out = new FileOutputStream(dest);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
You can use a very easy method:
MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, filename, "Description");
This is deprecated. Try this answer
I'm working on an android application and I want to save a screenshot of the application. I can save a single screenshot but it keeps over writing the previous screenshot.
I followed a tutorial and modified it but it does not take more than a single screenshot
Attached here is the code in the button action
case R.id.btn_save:
View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap bitmap = getScreenShot(rootView);
int i = 0;
File file = new File("ScreenShot"+ i +".PNG");
if(!file.exists()){
store(bitmap, "ScreenShot"+ i +".PNG");
}
else {
store(bitmap, "ScreenShot"+ i++ +".PNG");
}
and the screenshot storing function
public void store(Bitmap bm, String fileName){
String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Screenshots";
File dir = new File(dirPath);
if (!dir.exists()){
dir.mkdirs();
}
File file = new File(dirPath,fileName);
try{
FileOutputStream fos = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 100,fos);
fos.flush();
fos.close();
}catch (Exception e){
e.printStackTrace();
Toast.makeText(this, "Error saving File", Toast.LENGTH_SHORT).show();
}
}
You are declaring i variable inside the button save so you will always start with 0 when button is clicked. To use the way you are trying you should declare that variable outside that scope, but it will restart when you kill and reopen the app.
You can use Shared Preferences to save the following number to use (or the last you used) if you want to use that approach. If not you can use simply
"Screenshot" + System.currentTimeInMillis().toString().
You will also have the time when the screenshot was taken (although in millis). If you want you can format it to be like "user readable" 20191110 for example
Because in that code the file name is always the same- i is always 0. To make it work for one use of the app, i should be a member variable and incremented every screenshot. To make it work more generally, you should generate a random name using File.createTempFile()
case R.id.btn_save:
View rootView getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap bitmap = getScreenShot(rootView);
File dir = new File(Environment.getExternalStorageDirectory(), "Screenshots");
if (!dir.exists())
if ( !dir.mkdirs())
{
Toast ( could not create dir...);
return;
}
int i = 0;
while (++i > 0 )
{
String fileName = "ScreenShot"+ i +".png";
File file = new File(dir, fileName);
if(!file.exists())
{
store(bitmap, file);
break;
}
}
break;
Change the parameter of store(Bitmap bm, String fileName) to store(Bitmap bm, File file)
There you can remove all code before the try block.
I am writing a camera application for the android platform. I am using the CameraKitView Library for producing my camera view. Everything else including accessing the camera is working as expected except for actually capturing and saving the image. minimum sdk is 15 and target sdk and compile sdk is 28. The code for saving the image is as shown below
cameraKitView.captureImage(new CameraKitView.ImageCallback() {
#Override
public void onImage(CameraKitView cameraKitView, byte[] photo) {
File savedPhoto = new File(Environment.getExternalStorageDirectory(), "pchk.jpg");
try{
FileOutputStream outputStream = new FileOutputStream(savedPhoto.getPath());
outputStream.write(photo);
outputStream.close();
}catch (java.io.IOException e){
e.printStackTrace();
}
}
});
Thank you in advance for your assistance
First of all verify that your file or folder where you want to save a file is exists or not, if not create them
capturedFolderPath is a path of the folder where you want to create the file
File folderFile = new File(capturedFolderPath)
if (!folderFile.exists()) {
folderFile.mkdirs();
}
String imageNameToSave = "xyz.jpg";
String imagePath = capturedFolderPath
+ File.separator + imageNameToSave + ".jpg";
File photoFile = new File(imagePath);
if (!photoFile.exists()) {
photoFile.createNewFile();
}
after creating write the bytes on the file like this
FileOutputStream outputStream = new FileOutputStream(photoFile);
outputStream.write(bytes);
outputStream.close();
I am calling a file glassShader.vert from the following method and it gives me FileNotFoundException error
The complicated issue is that the class GLGridRenderer that contains this method lies in the directory GridLogin which is in turn inside the package com.jasfiddle.AmazingInterface
So to address the directory, it would be com.jasfiddle.AmazingInterface.GridLogin
But I don't know how to call shader.vert which is inside GridLogin
public static String readShaderFile(String filepath) throws IOException {
FileInputStream stream = new FileInputStream(new File(filepath));
try{
FileChannel fc = stream.getChannel();
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
return Charset.defaultCharset().decode(bb).toString();
}
finally{
stream.close();
}
}
other than raw you can also use asset folder see link ....
try {
// get input stream for text
InputStream is = getAssets().open("text.txt");
// check size
int size = is.available();
// create buffer for IO
byte[] buffer = new byte[size];
// get data to buffer
is.read(buffer);
// close stream
is.close();
}
catch (IOException ex) {
return;
}
Files that you want to read should not be put in a package. They should be packaged as resources or assets. For instance, with a data file, put it in the res/raw folder and give it a legal resource name. Then you can open an input stream if you have a Context (such as your Activity class or a View class).
InputStream stream = context.getResources().openRawResource(R.raw.filepath);
(This would be if you named the file res/raw/filepath.dat. You'd probably want a more meaningful name. If you want the name to be a variable, then you can obtain the resource ID using:
int resId = context.getResources.getIdentifier(filepath, "raw", context.getPackageName());
I've created an app that records audio, saves the sample to the sd card then plays it back, using the record and play buttons. I need to reverse this sample. I can do all this and the the reversed sample is saved on the SD card under a different name. The original sample is test.wav and the same sample reversed is save as revFile.wav. when i try play revFile.wav android says it can't play this format.
I've litterally put the sample in an array then reversed the contents, something is telling me that there could be header info at the start of the sample that needs striping first, any ideas. thanks.
Here's what i have so far.
public class recorder extends Activity {
MediaRecorder myRecorder = null;
DataInputStream dis = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onClickPlay(View v){
Log.v("onClickplay", "play clicked");
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/test.wav");
mp.prepare();
mp.start();
} catch(Exception e3) {
e3.printStackTrace();
}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("playing");
}
public void onClickRecord(View v){
Log.v("onClickRecord", "record clicked");
File path = Environment.getExternalStorageDirectory();
Log.v("file path", ""+path.getAbsolutePath());
File file = new File(path, "test.wav");
if(file.exists()){
file.delete();
}
path.mkdirs();
Log.v("file path", ""+file.getAbsolutePath());
myRecorder = new MediaRecorder();
myRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
myRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
myRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
myRecorder.setOutputFile(file.getAbsolutePath());
Log.i("myrecorder", "about to prepare recording");
try{
myRecorder.prepare();
} catch(Exception e) {
e.printStackTrace();
}
Log.i("myrecorder", "prepared");
myRecorder.start(); // Recording is now started
Log.i("myrecorder", "recording");
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("recording");
}
public void onClickStop(View v){
Log.v("onClickStop", "stop clicked");
try{
myRecorder.stop();
myRecorder.reset(); // You can reuse the object by going back to setAudioSource() step
myRecorder.release(); // Now the object cannot be reused
}catch(Exception e){}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("recording stopped");
}
public void onClickReverse(View v){
Log.v("onClickReverse", "reverse clicked");
File f = Environment.getExternalStorageDirectory();
String path = f.getAbsolutePath();
path = path + "/test.wav";
Log.v("path = ", ""+path);
Log.v("dir = ", ""+f.getAbsolutePath());
Log.v("test file exists? = ", ""+f.getAbsolutePath()+"/test.wav");
File f2 = new File(path);
Log.v("f2 = ", ""+f2.getAbsolutePath());
try {
InputStream is = new FileInputStream(f2);
BufferedInputStream bis = new BufferedInputStream(is);
dis = new DataInputStream(bis);
} catch (Exception e) {
e.printStackTrace();
}
int fileLength = (int)f2.length();
byte[] buffer = new byte[fileLength];
/*File reversedFile = Environment.getExternalStorageDirectory();
File revFile = new File(reversedFile, "reversedFile.wav");
Log.v("reversedfile path", ""+ revFile.getAbsolutePath());
if(revFile.exists()){
revFile.delete();
}
reversedFile.mkdirs();
*/
byte[] byteArray = new byte[fileLength +1];
Log.v("bytearray size = ", ""+byteArray.length);
try {
while(dis.read(buffer) != -1 ) {
dis.read(buffer);
Log.v("about to read buffer", "buffer");
byteArray = buffer;
}
Log.v(" buffer size = ", ""+ buffer.length);
} catch (IOException e) {
e.printStackTrace();
}
byte[] tempArray = new byte[fileLength];
int j=0;
for (int i=byteArray.length-1; i >=0; i--) {
tempArray[ j++ ] = byteArray[i];
}
File revPath = Environment.getExternalStorageDirectory();
Log.v("revpath path", ""+revPath.getAbsolutePath());
File revFile = new File(revPath, "revFile.wav");
Log.v("revfile path ", ""+revFile.getAbsolutePath());
if(revFile.exists()){
revFile.delete();
}
revPath.mkdirs();
try {
OutputStream os = new FileOutputStream(revFile);
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream dos = new DataOutputStream(bos);
Log.v("temparray size = ", ""+ tempArray.length);
dos.write(tempArray);
dos.flush();
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(Environment.getExternalStorageDirectory().getPath()
+"/revFile.wav");
mp.prepare();
mp.start();
} catch(Exception e3) {
e3.printStackTrace();
}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("playing reversed file");
}
}// end of onclickrev
The WAV file format includes a 44-byte header chunk. Most WAV files consist of this 44 byte header, followed by the actual sample data. So, to reverse a WAV file, you should first copy the 44 byte header from the original file, and then copy the inverted sample data from the original after the header. If you just reverse the byte order of the original entire file, it definitely won't work. It also won't work if you copy the header and then reverse the byte order of the remainder of the file (actually it will sort of work, except what you get will be just noise). You actually need to reverse the frames, where the frame size is dependent on the bytes-per-sample and whether the file is stereo or mono (for example, if the file is stereo and 2 bytes per sample, then each frame is 4 bytes).
Note that not all WAV files are "canonical" like this. WAV files are actually a variant of RIFF files, so technically you need much more complicated code to find the various parts of the header and sample data within the original file. However, most WAV files are just the header followed by the samples (and this will certainly be true if you're recording the audio yourself), in which case you can save yourself a lot of work.
Joe Cullity's link is a good description of the WAV file format.
There is a good description of the .WAV header at link text
Also note that all data in the file is stored as Little Endian order (low order byte of 1 multi byte number is stored at the lowest address....) so you can't just reverse the bytes. you need to see how many bytes wide each sample is, (usually 16, but check the header) and reverse them in chunks of that size
I'm pretty sure that you're right and that the problem is that you can't just reverse the bytes in the file to reverse the waveform because you're destroying header information. You should try to see if there's a good library out there to do this, since I have little experience working with .wav files.