Determine if 1MB limit is exceeded when sharing data - android

I have an app similar to an explorer and allow the user to share folders, multiple folders and multiple files. The share intent will hold a list of all shared files in the end.
If the user shares a folder with 1000 files, this is just to much. But how can I determine if it's possible to share the amount of files the user selected?
I want to check if it's possible to share the amount of files the user selected and if not, I want to tell him that he selected to much files and reached an android limit. In the best case I would like to tell him that his share intent is let's say 2.5MB and that the limit is 1MB, so he can estimate by himself how many files he should select...
Question
Is it possible to determine if a share intent is < 1MB and therefore is possible to be shared?
Is it possible to determine the size of a share intent?
Edit
To eliminat confusion:
I don't share files directly in the intent, but only the URI or relative path to the files. So the length of the URI or the length of the relative path is important for the size of the intent, but not if it points to big or small files.
The 1MB limit is valid for the intent (parceled intend I think) and if the intent holds to much data, it will fail SILENTLY and that's the problem
I'm looking for a reusable solution, that works with ANY intent! I am sometimes adding share targets and sometimes not.

For Intent:
You can write that intent object to a Parcel and use dataSize() method to get serialized data size contained in that intent. Make sure to recycle() the parcel afterwards.
final Intent testIntent = getIntent();
final Parcel testParcel = Parcel.obtain();
try {
testParcel.writeParcelable(testIntent,0);
Log.i("TEST", String.format("Intent size: %s", testParcel.dataSize()));
}finally {
testParcel.recycle();
}
For files:
If you can retrieve list of file names from intent, then you can query each file size and sum it up:
sizeBytes += new File(filePath).length();
For a directory though you'll need to walk and add up the size of all files under it. You can use DirectoryWalker class for this.

Why don't call a shell command to get a size of files?
It won't determine intent size exactly but gives you a nice prediction.
try {
Process process = Runtime.getRuntime().exec("ls -l");
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
} catch (InterruptedException e) {
e.printStackTrace();
}
Ensure you don't use awk and other which can be unavailable on some devices.
For directories sizes you will need to use something different than ls.

what you could do is that you keep reference to the file names length that are being selected to avoid looping through them later.
1 character = 1 byte. so, about 1000 characters equal 1 kilobyte.
so base on this you could limit the user selection and be able to actually tell them while they are selecting the files, that they reached the maximum number of files to select(a general message). example code:
private int maxLength = 0;
onSelectFile(File file){
maxLength += file.getName().length()
if(maxLength < 1mb ){//display massage and dont add the new selected file}
}

I believe there's no direct way of checking it, but you can do a manual work on it to get the size of your intent.
Considering that Intent implements Parcelable you should be able to get a Parcel, write to it, and check it's size before sending/launching the intent. Something like that:
Intent intent = ....
... do your intent and fill with the URI data.
Parcel parcel = Parcel.obtain();
intent.writeToParcel(parcel, 0);
int size = parcel.dataSize(); // HERE IS THE NUMBER YOU WANT TO CHECK
parcel.recycle(); // don't forget to return the parcel to the system
parcel = null;
I'm not sure if that size is in bytes, but it should be. If you want the official docs are here https://developer.android.com/reference/android/os/Parcel.html
On a user experience note, you might want to keep checking the size as the user selects stuff, so that once the limit is reached, you can block the user from selecting any extra itens and thou don't waste user time.

It might make more sense to put a sensible worse case number on it. "You have shared too many files (1005/1000 files)" would be a useful message. Telling a user they've shared too many bytes will be confusing when you're referring to the length of file names, not size of the files.
The worst case file name is 127 characters or 255 bytes.
1MB/256 = 4096
So, I would think 4000 would be risky, but 2000-3000 should be fine, depending on the size of the rest of your payload.
If you think that's not enough for your users, then you need a different solution, not a warning.

Related

How to send a list of items to the watch from PebbleKit for Android

I have an android app that is essentially a list of timers. Each timer can have the following fields:
title (string, can be up to 255 characters)
id (integer)
seconds (integer)
time_started (integer)
seconds_left (integer)
running (boolean)
order (integer)
There can be an unlimited number of these timers, though for the pebble watch app, it'd be ok if I only send the first n (10, 15, 20).
Currently I've been sending the items to the watch one at a time, and creating the PebbleDictionary like this:
private PebbleDictionary buildTimerDictionary(Timer timer) {
PebbleDictionary data = new PebbleDictionary();
data.addUint32(C.KEY_ID, timer.getId());
data.addUint32(C.KEY_SECONDS, (int)timer.getSeconds());
data.addString(C.KEY_DESCRIPTION, timer.getDescriptionFormatted());
data.addUint32(C.KEY_TIME_STARTED, (int)timer.getTimeStarted());
data.addUint32(C.KEY_TIME_LEFT, (int)timer.getSecondsLeft());
data.addUint8(C.KEY_RUNNING, (byte)(timer.isRunning() ? 1 : 0));
data.addUint32(C.KEY_ORDER, (int)timer.getOrder());
data.addString(C.KEY_TIME_DISPLAY, timer.getSecondsFormatted());
return data;
}
And sending it via a queue that sends the next PebbleDictionary to the watch after the previous is acked.
This works, but it's pretty slow. I feel like I could save a lot of time by packing more than one timer into each message. However, I'm not sure how to do that, considering the inbox size on the pebble watch itself, and the fact that you have to hard define the keys for the dictionary in appinfo.json (it doesn't seem like you can use arbitrary keys).
How is this sort of thing usually done?
There are two ways to do this:
One message for each item (what you are doing now)
Multiple items per message.
In this case you can pack everything into one value by using byte arrays and concatenating all your fields into the byte array.
You can then use the key to send the index of the element in the list.
A few comments:
Defining the keys in appinfo.json is optional and only useful if you are using PebbleKit JavaScript. It has absolutely no use for apps that talk with PebbleKit Android.
You can query the available buffer size on Pebble with app_message_inbox_size_maximum(). Get it when you start your app and send it from Pebble to the Android app.
The best strategy depends on the average size of your messages. Right now your items will be about 25bytes + the strings + the small overhead of a dictionary (7 bytes + 1 byte per key). If the strings are very small you might fit three items per messages, if the description is very long, you might not be able to send the item at all (you should probably truncate it).

How to add some data to .jpg or .mp4 file in android

Business Purpose :
1) Want to add large string(data) of length 1200 to the .jpg / .mp4 file in android mobile
2) Later the file can be uploaded to server from mobile
3) In server we retrieve the added data from the file
What i have tried in .jpg file :
Used the below code for adding data
ExifInterface exif = new ExifInterface(photoPath);
exif.setAttribute("UserComment", "String having length of 1000");
exif.saveAttributes();
This code is working. After i set the attribute, i can able to read it by
String userComment=exif.getAttribute("UserComment");
In low end mobile it showed error "stack corruption detected: aborted" while saving attribute.Later i found it taken up to 663 characters alone.
In high end mobile the string of length saved up to 1999 after saveAttribute().
Is there any other way to add some tag/meta data/string to .jpg,.mp4 and .mp3 file ?
So that the added data can be retrieved later.
please share your views. Is it possible ?
It sounds as if it's certainly is possible using your approach, but you're running into various implementation limits in how long attribute values are supported.
One solution to at least investigate is of course to split your 1200-byte string into multiple shorter strings, say four 300-byte ones, and add those as UserComment0, UserComment1 and so on. That should be trivial to extract and concatenate to get back your original longer string, and might work around the limitations.
Praveen,
take a look at Steganography project
https://github.com/johnkil/Steganography
Thanks,
Jey.

Android Loading Strings into Array

I have a list of 1000 words. I need to load an array with n randomly chosen words from that list (no repeats allowed). What is the best way of going about doing that?
My ideas:
1) Load the words into R.arrays to create a String array. Use collections.shuffle to shuffle the array, then pull the first n entries from it. Right now, I am having memory issues loading the initial array with the 1000 words using this method.
2) Load the words into a text file, read each word into a String array. Use same method to get first n entries.
3) Hard code the input of the words into a String array (I'd use a script to get that output of course). Use same method to get first n entries.
Is there a better way?
If you're mainly worried about memory usage and you're willing to give up computation speed, here's an algorithm that will get you there.
Keep your words in a text file, one word per line, with a fixed amount of characters per word, padding each word with spaces at the end to ensure a fixed word char size, call it s.
Create an array of max size n, call it w
Open a stream reader to the file containing the 1000 words
Get a random number between 1 and 1000, call it k
Seek to position k*s in the file stream and grab the next s characters
Add the word to w if it does not exist in the array yet
If the w array is full (ie. size=n), we're done, otherwise go back to step 3
Let us know how it goes. Happy coding!

Android: Most efficient way to save a list of int

I'm trying to create a system to save the list of levels that have already been played in my application.
I've been using Editor and SharedPreferences to store information on the phone.
But now I have a list of int to save
I've been trying to figure out something with
DataOutputStream out;
//Size
int resultSize = results.size();
out.writeInt(resultSize);
//All the levels ID
for (int idx = 0; idx < resultSize; idx++){
//l().debug("Lvl ID: "+results.get(idx));
out.writeInt(results.get(idx));
}
But then I don't know how to store this in the shared preferences.
I'm also concerned about size, this list of ID might get really big so I want to optimize the amount stored.(hence the DataStream)
any ideas
Jason
You could iterate through your list and construct a comma delimitted String from the ints. E.g. "1,2,15..etc", then just write this string to SharedPreferences. You would obviously have to tokenize it when you read it back.
How big is "really big" exactly? Considering it's a list of levels played by a human I am going to assert that it is "small" for what a smartphone can handle. Though, personally I would recommend this to go in a SQLite table of "completed" levels for ease of querying the information.
I don't think that you need the most efficient way. This data is small, go with what you know. Pick something simple and understandable, something easy to debug.
A flat file with every level int on a new line. You can't get much simpler than that. Sure, it's bigger than the most efficient way, but it's much easier to recover from corrupted files, much easier to debug.
Binary stream is the most efficient way how to save integers. If you have really a lot of numbers (megabytes), use DataOutputStream combined with ZipOutputStream.

Determine the content length of an Intent.STREAM_EXTRA (Android)

Suppose an app gets a content stream by being called with the android.intent.action.SEND filter. Also suppose the Uri and the stream is received like this:
Uri contentUri = (Uri) extras.get(Intent.EXTRA_STREAM);
InputStream in = getContentResolver().openInputStream(contentUri);
Now, how can I determine the total length of the content without reading the whole stream? Is it even possible? in.available() isn't necessarily accurate, so I guess I do have to read the stream until the end, first?
The content might be very large, like about 50MB or more. So, I'd rather only read it once.
You can't determine the amount of data in a stream without reading it; you can, however pass the length via some other extra if you are the source of input.
I would read into a ByteArrayOutputStream and then call toByteArray() to get the resultant byte array. You don't need to define the size in advance (although it's possibly an optimisation if you know it. In many cases you won't)

Categories

Resources