React Native: Crop image on Android - android

The following function works on iOS, but doesn't work on Android. I want to achieve the same result on both platforms.
Based on this official documentation, the reason is missing ImageStore in react-native package for Android.
import {
ImageEditor,
ImageStore
} from 'react-native';
What would be an alternative way to crop images (as square) in react-native for Android?
cropImage(base64ImageData) {
return new Promise((resolve, reject)=>{
ImageStore.addImageFromBase64(
base64ImageData,
(photoURI)=>{
ImageEditor.cropImage(
photoURI,
{offset:{x:0,y:(1920-1080)/2},size:{width:1080, height:1080}},
(croppedURI)=>{
ImageStore.getBase64ForTag(
croppedURI,
(base64CroppedData)=>{
ImageStore.removeImageForTag(croppedURI);
resolve(base64CroppedData);
},
(err)=>{
reject(err);
}
);
ImageStore.removeImageForTag(photoURI);
},
(err)=>{
reject(err);
}
);
},
(err)=>{
reject(err);
}
);
});
}
Update:
There is a project https://github.com/seancunningham/react-native-image-store-ext (last commit Oct 28, 2016), but it only implements:
Boolean removeImageForTag(String file_uri)
The rest is missing:
static addImageFromBase64(base64ImageData, success, failure)
My preliminary solution:
private static final String IMAGE_STORAGE_URL_SCHEME = "rct-image-store";
#ReactMethod
public void addImageFromBase64(String base64_image_data, Callback successCallback, Callback failureCallback){
String imageStorageDir = this.reactContext.getApplicationContext().getFilesDir()+"/"+IMAGE_STORAGE_URL_SCHEME+"/";
byte[] buffer = new byte[BUFFER_SIZE];
String file_uri = imageStorageDir+"1";
try {
File f = new File(imageStorageDir);
if(!f.exists()) {
f.mkdir();
}
FileOutputStream fos = new FileOutputStream(file_uri, false);
byte[] decodedImage = Base64.decode(base64_image_data, Base64.DEFAULT);
fos.write(decodedImage);
fos.close();
successCallback.invoke("file://"+file_uri);
} catch (IOException ioe) {
failureCallback.invoke("Failed to add image from base64String"+ioe.getMessage());
} catch (Exception e) {
failureCallback.invoke("Failed to add image from base64String"+e.getMessage());
}
}
static getBase64ForTag(uri, success, failure)
Implemented in this file ImageStorageManager.java
static hasImageForTag(uri, callback)

The issue must be the sum of the offset + size be more then the height accepted, try it:
{offset:{x:0,y:(1920-1080)/2},size:{width:1080, height:1080 - (1920-1080)/2)}},
See if it works, if it does, just try to resize your size.height and see what works for you! :)

Related

new BlobStoreManager read write on Android 11

I previously used external storage to store specific data that I would like to share between my applications (without having any contentprovider "host")
File folder = new File(Environment.getExternalStorageDirectory(), "FOLDER_NAME");
File file = new File(folder, "FILE_NAME.dat");
FileOutputStream outputStream = new FileOutputStream(file);
That is why I am trying to use BlobStoreManager, as suggested in google's recommendation for targeting 30 (https://developer.android.com/training/data-storage/shared/datasets)
The read & write are based on a BlobHandle with 4 parameters, one being MessageDigest based on a "content". BlobHandle must use the same 4 parameters, or read will fail (SecurityException).
I managed to write data, and to read it, but it makes no sense:
It seems that in order to write, I need to use the data I want to write to generate the BlobHandle.
Then, to read, as BlobHandle must use the same 4 parameters, I also need the data I wrote to be able to read.
Totally illogic, as I wanted to read this data, I don't have it!
I must miss something or just do not understand how it work. If someone can help :)
Here are my sample:
If I set the following:
createBlobHandle: content = "mydata"
write: data = "mydata"
Then write will success, and read will success too. But it I can not know the value before reading it in a normal usecase :(
If I set the following (which would be logic, at least to me):
createBlobHandle: content = "somekey"
write: data = "mydata"
Then write will fail :(
#RequiresApi(api = Build.VERSION_CODES.R)
private BlobHandle createBlobHandle() {
//Transfer object
String content = "SomeContentToWrite";
String label = "label123";
String tag = "test";
//Sha256 summary of the transmission object
try {
byte[] contentByte = content.getBytes("utf-8");
MessageDigest md = MessageDigest.getInstance("sha256");
byte[] contentHash = md.digest(contentByte);
return BlobHandle.createWithSha256(contentHash, label,0, tag);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
private void write() {
String data = "SomeContentToWrite";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
//Generate the session of this operation
try {
BlobHandle blobHandle = createBlobHandle();
if (blobHandle == null)
return;
long sessionId = blobStoreManager.createSession(blobHandle);
try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
try (OutputStream pfd = new ParcelFileDescriptor.AutoCloseOutputStream(session.openWrite(0, data.getBytes().length))) {
//The abstract of the written object must be consistent with the above, otherwise it will report SecurityException
Log.d(TAG, "writeFile: >>>>>>>>>>text = " + data);
pfd.write(data.getBytes());
pfd.flush();
//Allow public access
session.allowPublicAccess();
session.commit(applicationContext.getMainExecutor(), new Consumer<Integer>() {
#Override
public void accept(Integer integer) {
//0 success 1 failure
Log.d(TAG, "accept: >>>>>>>>" + integer);
}
});
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String read() {
String data = "";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
BlobHandle blobHandle = createBlobHandle();
if (blobHandle != null) {
try (InputStream pfd = new ParcelFileDescriptor.AutoCloseInputStream(blobStoreManager.openBlob(createBlobHandle()))) {
//Read data
byte[] buffer = new byte[pfd.available()];
pfd.read(buffer);
String text = new String(buffer, Charset.forName("UTF-8"));
Log.d(TAG, "readFile: >>>>>>>>>>>>>>>>>>>>" + text);
} catch (IOException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
return data;
}
According to the official training documentation linked in the question, the missing piece of information, at the time of the question having been asked, is that the four pieces of data contained in the BlobHandler need to be uploaded to a server owned by the client application then subsequently downloaded by which ever other application wants to access the blob via the BlobStorageManager.
So it would seem that on-device blob discovery is not supported. There could also be a solution possible using a Content Provider which could offer up the four required pieces of data, thus circumventing the need for the server infrastructure.

Android OS - How to track Azure upload progress

I've been working with Azure on the Android OS and I managed to upload my video file (.mp4) to a Container I had already prepared for it.
I did this by getting a Shared Access Signature (SAS) first, which provided me with:
a temporary key
the name of the container to where I want to send the files
the server URI
Then, I started an AsyncTask to send the file to the container using the "upload".
I checked the container, and the file gets uploaded perfectly, no problems on that end.
My question is regarding the progress of the upload. Is it possible to track it? I would like to have an upload bar to give a better UX.
P.S - I'm using the Azure Mobile SDK
Here's my code:
private void uploadFile(String filename){
mFileTransferInProgress = true;
try {
Log.d("Funky Stuff", "Blob Azure Config");
final String gFilename = filename;
File file = new File(filename); // File path
String blobUri = blobServerURL + sharedAccessSignature.replaceAll("\"", "");
StorageUri storage = new StorageUri(URI.create(blobUri));
CloudBlobClient blobCLient = new CloudBlobClient(storage);
//Container name here
CloudBlobContainer container = blobCLient.getContainerReference(blobContainer);
blob = container.getBlockBlobReference(file.getName());
//fileToByteConverter is a method to convert files to a byte[]
byte[] buffer = fileToByteConverter(file);
ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer);
if (blob != null) {
new UploadFileToAzure().execute(inputStream);
}
} catch (StorageException e) {
Log.d("Funky Stuff", "StorageException: " + e.toString());
e.printStackTrace();
} catch (IOException e) {
Log.d("Funky Stuff", "IOException: " + e.toString());
e.printStackTrace();
} catch (Exception e) {
Log.d("Funky Stuff", "Exception: " + e.toString());
e.printStackTrace();
}
mFileTransferInProgress = false;
//TODO: Missing ProgressChanged method from AWS
}
private class UploadFileToAzure extends
AsyncTask <ByteArrayInputStream, Void, Void>
{
#Override
protected Void doInBackground(ByteArrayInputStream... params) {
try {
Log.d("Funky Stuff", "Entered UploadFileToAzure Async" + uploadEvent.mFilename);
//Method to upload, takes an InputStream and a size
blob.upload(params[0], params[0].available());
params[0].close();
} catch (StorageException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Thanks!
You can split your file and send its part using Block, there is a good example of your case in this link but it used C# so you should find the corresponding function in the android library reference.
Basically instead of sending you file as one big file, you split it to multiple files (bytes) and send it to azure so you can track the progress on how many bytes that already sent to azure

Write File in Java Android, Read the File with Phonegap

I'm trying to write a json file using android like this:
String jsonString = broadcastDataArray.toString();
Writer output = null;
File fileAnnouncement = new File("announcement.json");
try {
output = new BufferedWriter(new FileWriter(fileAnnouncement));
output.write(jsonString);
output.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
and now i'm trying to load/read the file using phonegap. Using this method shown here, i am able to read a file that was included in my application source. But i want to read some file that was generated by the android code.
Is there anyway to do this? Is there any specific directory where the file could be accessed from Java android and Phonegap/Sencha ?
Any kind of help or pointer is appreciated. Thanks.
You can do this in following way.
Write this function in your Android activity to write data in text file.
public void WriteDataToFile(String sFileName, String sBody){
try
{
FileWriter f = new FileWriter("/sdcard/data.txt");
f.append(sBody);
f.flush();
f.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
And, now you can read that file from your Sencha/Phonegap app by calling this function.
var user_data = function(){
var request = new XMLHttpRequest();
request.open("GET", "/sdcard/data.txt");
request.onreadystatechange = function() {
if (request.readyState == 4) {
// alert("*" + request.responseText + "*");
data = request.responseText;
}
}
request.send();
}

Need help debugging my code

I need some input about my code.
Basically, I have a method to load music from Class A
public void onListItemClick(ListView parent, View v, int position, long id){
musicIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
cursor.moveToPosition(position);
filePath = cursor.getString(musicIndex);
fileName = new File(filePath).getName();
playMusic();//Play the selected music
}
public void playMusic(){
if(mPlayer.isPlaying()){
mPlayer.reset();
}
try{
mPlayer.setDataSource(filePath);
mPlayer.prepare();
mPlayer.start();
BeatDetection beatDetect = new BeatDetection();
beatDetect.init();
}catch (Exception e){
}
}
That method will call the init() method in Class B
public void init() throws Exception{
energy = 0;
variance = 0;
constant = 0;
isBeat = false;
sensitivity = 0;
dBuffer = new float[sampleRate / bufferSize];
eBuffer = new float[sampleRate / bufferSize];
timer = System.currentTimeMillis();
MusicLoad msc = new MusicLoad();
totalMs = 0;
seeking = true;
//msc.printText();
decode(msc.fileName, 25, 40);
}
In that method, it initializes everything and call the decode() method
public void decode(String path, int startMs, int maxMs)
throws IOException, javazoom.jl.decoder.DecoderException {
debug();
File in = new File(path);
InputStream inStream = new BufferedInputStream(new FileInputStream(in), 8 * 1024);
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
try {
Bitstream bitstream = new Bitstream(inStream);
Decoder decoder = new Decoder();
boolean done = false;
while (! done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
totalMs += frameHeader.ms_per_frame();
if (totalMs >= startMs) {
seeking = false;
}
if (! seeking) {
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
if (output.getSampleFrequency() != 44100 || output.getChannelCount() != 2) {
throw new javazoom.jl.decoder.DecoderException("mono or non-44100 MP3 not supported", null);
}
short[] pcm = output.getBuffer();
for (short s : pcm) {
outStream.write(s & 0xff);
outStream.write((s >> 8 ) & 0xff);
}
}
if (totalMs >= (startMs + maxMs)) {
done = true;
}
}
bitstream.closeFrame();
}
byte[] abAudioData = outStream.toByteArray();
calculation(abAudioData);
} catch (BitstreamException e) {
throw new IOException("Bitstream error: " + e);
} catch (DecoderException e) {
Log.w("Decoder error", e);
throw new javazoom.jl.decoder.DecoderException("Error",e);
} finally {
inStream.close();
}
}
Don't mind reading all the code lines. If you guys notice I put debug() in the beginning to see whether the method is called or not. At this point, the debug() is properly called. However, if I put the debug() after the line File in = new File(path);, the debug() will not be called anymore. It seems like the code is stop running at that point.
The ultimate result is, I can load and play the song without any problem. However, the decode() is not called and there is no error whatsoever. I'm stuck at pointing out the problem at this point. So if there's any input please help me.
EDIT: After I tried tracing the "path" variable, it returns NULL so the error is NullPointerException. Seems like the "fileName" variable from Class A is not passed to Class B. Any suggestion?
If you are using Eclipse with ADT then it's very easy to debug your Android apps, just add a breakpoint (probably in the new File(...) line) and see what happens.
My guess here is that File in = new File(path); probably is throwing a IOException in your decode method, that exception is bubbling first to init() and then to playMusic(), where it is caught by try catch block. Your catch is empty so you are not seeing anything. Try debugging as I said or add some logging info in the catch block.
This is just something to look at, but from the doc page
http://developer.android.com/reference/java/io/File.html#File%28java.lang.String%29
"The actual file referenced by a File may or may not exist. It may also, despite the name File, be a directory or other non-regular file."
If you had the path wrong, it may be trying to create the file and you may not have the correct permission to do so. Perhaps: WRITE_EXTERNAL_STORAGE.
I know this post is old, but I just wanted to show how to get the file path to read/write files for others that come across this post as I have:
String filePath = myContext.getFilesDir().getPath().toString() + "/sysout.log";
File file = new File(filePath);
These two lines will create (open if it exists, and overwrite) a file named "sysout.log" in the folder /data/data/com.app.name/files/; myContext is just the current context. Using this technique alleviates problems with defining your own path name. Hope this helps someone.

Simultaneous downloads result in corrupted files

I'm downloading sets of images in separated threads and saving them to the SD card. The problem is that when I run two or more downloading threads the saved images are corrupted. If just one thread is running the images are ok. I'm downloading it from the same domain but different url, e.g. www.test.com/set1/img1.jpg, www.test.com/set2/img1.jpg etc.
I'm saving them to different folders by the name of the set. I noticed that mostly larger images are corrupted (over 500 KB), smaller are usually ok, but not always.
Do you have any clue why the files get corrupted when multiple threads are running?
Here's a part of the code I'm using:
protected class DownloadTask extends DownloadRunnable {
#Override
public void run() {
InputStream is = null;
OutputStream os = null;
File bitmapFile = null;
/** some more declarations and preparations are here */
for (int pg=getDownloadedPages(); pg < numPages; ++pg) {
for (char ch='a'; ch <= 'e'; ++ch) {
/* check for pause */
synchronized (pauseLock) {
while (paused && !aborted) {
try {
pauseLock.wait();
} catch (InterruptedException e) {
}
}
}
fileName = "page-" + df.format(pg) + "-" + ch;
url = MainApp.getRestrictedUrl(MainApp.tstcode, urlFile + fileName+ ".jpg");
is = new BufferedInputStream(new URL(url).openStream());
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
bitmapFile = new File(pathToSave, fileName + MagazinePage.FILE_EXT);
MainApp.encryptToFile(bitmapFile, is);
dwnlSize += bitmapFile.length();
}
is.close();
}
}
}
public static void encryptToFile(File file, InputStream is) throws IOException {
BufferedOutputStream os = null;
try {
if (file.exists()) {
file.delete();
} else {
file.getParentFile().mkdirs();
}
file.createNewFile();
os = new BufferedOutputStream(new FileOutputStream(file));
IkioskContentProvider.getInstance().encrypt(is, os);
} finally {
os.close();
}
}
}
DownloadRunnable is custom abstract class implementing Runnable. And I'm using it in thread the regular way:
protected void downloadIssuePages() {
dwnlTask = new DownloadTask();
new Thread(dwnlTask).start();
}
I'm calling downloadIssuePages() on two different objects to download two sets for example.
Using SDK version 11 (Android 3.0), device Acer Iconia Tab A500 with Android 3.1
I've tried to disable writing for the second thread, only the first thread was saving files to find out if there's a problem in reading from the stream or writing. Apparently writing was the problem as data was correct in this case.
So I decided to use lock around writing to the file and looks like it's working fine:
synchronized (MainApp.fileWritingLockObj) {
while (MainApp.fileWritingLocked) {
try {
MainApp.fileWritingLockObj.wait();
} catch (InterruptedException e) {
}
}
MainApp.fileWritingLocked = true;
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
bitmapFile = new File(pathToSave, fileName + MagazinePage.FILE_EXT);
MainApp.encryptToFile(bitmapFile, is);
dwnlSize += bitmapFile.length();
}
is.close();
MainApp.fileWritingLocked = false;
MainApp.fileWritingLockObj.notifyAll();
}

Categories

Resources