For a content: URI, I used ContentProvider/Contentresolver to retrieve information about the modified date, size and data. I would like to do the same for file: URI but when I try the following code:
CursorLoader loader = new CursorLoader(this, uri, proj, null, null, null);
Cursor cursor = loader.loadInBackground();
Cursor is returned as null. How to get the file size, data and date added/modified?
I might be misunderstanding your question, but you use java's normal file IO to find that information.
Here is an example of me finding out that information for a picture called cat.jpg:
File f = new File("/data/cat.jpg");
//Size of file in bytes
Long size = f.length();
//Time of when the file was last modified in microseconds
Long lastModified = f.lastModified();
//The bytes of the file. This gets populated below
byte[] fileData = new byte[(int)f.length()];
try {
FileInputStream fis = new FileInputStream(f);
int offset = 0;
int bytesActuallyRead;
while( (bytesActuallyRead = fis.read(fileData, offset, 1024)) > 0) {
offset += bytesActuallyRead;
}
} catch(Exception e) {
}
Related
How to read filenames from the download directory on android 10 and higher?
After filtering the names I want to let users select from this list to open
the file.
On Android versions 8 (API 28) the FILE api is quite simple to use.
Filenames are simple to read with the method DirListOld. With these names
I can read the content of the files.
I tried to make a method to do the same on Android 10 (API 30) and higher.
But documentation is not very clear. I did some experimenting with
MediaStore methods, but I could not get the filenames only got directory
names on external storage.
How to filter the results is not very well documented and examples of the
MediaStore.Downlaods are totally absent.
My experiment is shown in method DirListNew.
Also I had to ask for a permission for MANAGE_EXTERNAL_STORAGE. Without this
permission even DirListNew results in an empty string. As I read in several
comments Google-Play is not generous in giving this permission. Why not
special permission for only downloaded files. I don't have to read all external
files.
I don't understand why Google-Android developers made such a mess for retrieving
simple downloaded files.
public String DirListOld()
{
String sName;
File oDownloadDir;
String sDownloadDir;
StringBuilder dirContent = new StringBuilder();
oDownloadDir = this.getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
try {
sDownloadDir = oDownloadDir.getName();
if (!sDownloadDir.equals("") )
{
for (File f : Objects.requireNonNull(oDownloadDir.listFiles()))
{
if (f.isFile())
{
sName = f.getName();
dirContent.append(sName);
dirContent.append("\n");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return dirContent.toString();
} // DirListOld
#RequiresApi(api = Build.VERSION_CODES.Q)
public String DirListNew()
{
EditText editText = findViewById(R.id.editTextMultiLine2);
StringBuilder dirContent = new StringBuilder();
String[] projection = new String[] {
MediaStore.Downloads.DATA
};
String selection = null;
String[] selectionArgs = null;
String sortOrder = null;
Cursor cursor = getApplicationContext().getContentResolver().query(
MediaStore.Files.getContentUri("external"),
projection,
selection,
selectionArgs,
sortOrder
);
if (cursor != null) {
cursor.moveToFirst();
//iterate over rows
for (int i = 0; i < cursor.getCount(); i++) {
//iterate over the columns
for(int j = 0; j < cursor.getColumnNames().length; j++){
//append the column value to the string builder and delimit by \n
dirContent.append(cursor.getString(j));
dirContent.append("\n");
}
//add a new line carriage return
dirContent.append("\n");
//move to the next row
cursor.moveToNext();
}
//close the cursor
cursor.close();
}
return dirContent.toString();
} // DirListNew
I am developing an android app which backs up and restores the messages/conversations from device. It backup the messages, export file in the form of xml, and then later restore it. The only problem I am facing is the date/times of conversations. It is changed to current time at the time of restoration, but when I open any conversation, there time is correct. Have a look at photos.
Before backup:
After backup:
Code I am using for backup:
Uri uri = Uri.parse("content://sms/inbox");
//Uri uri = Uri.parse("content://mms-sms/conversations/");
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"*"};
Cursor SMSL = contentResolver.query(Telephony.Sms.Inbox.CONTENT_URI, projection, null, null, null);
int msgscount = SMSL.getCount();
if (msgscount>0) {
msgs = new String[SMSL.getCount()][5];
int i = 0;
while (SMSL.moveToNext()) {
address = SMSL.getString(SMSL.getColumnIndex("address"));
body = SMSL.getString(SMSL.getColumnIndex("body"));
read = SMSL.getString(SMSL.getColumnIndex("read"));
date = SMSL.getString(SMSL.getColumnIndex("date"));
type = SMSL.getString(SMSL.getColumnIndex("type"));
msgs[i][0] = address;
msgs[i][1] = body;
msgs[i][2] = date;
msgs[i][3] = read;
msgs[i][4] = type;
Log.i("Date: ", String.valueOf(SMSL.getLong(SMSL.getColumnIndex("date"))));
i++;
}
SMSL.close();
}else{
msgs = new String[0][0];
Toast.makeText(getApplicationContext(),"No messages found!",Toast.LENGTH_LONG).show();
}
Code for restoring:
ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://sms/inbox");
//Uri uri = Uri.parse("content://mms-sms/conversations/");
ContentValues values = new ContentValues();
for (int i = 0; i < readMsgsFromFile.length; i++) {
values.put("address",readMsgsFromFile[i][0]);
values.put("body",readMsgsFromFile[i][1]);
values.put("date",readMsgsFromFile[i][2]);
values.put("read",readMsgsFromFile[i][3]);
values.put("type",readMsgsFromFile[i][4]);
contentResolver.insert(Telephony.Sms.Inbox.CONTENT_URI, values);
Log.i("Restoring: ",readMsgsFromFile[i][2]);
}
Thanks Mike M. I did find a solution and you are right, the conversation table is updated whenever a new message is received or sent by a user and the time of conversation is same as that message's (whether received or sent) time. But in case of writing messages through contentresolver query it does not work and the conversation time is current time at the time of writing. So what I did is add a temporary message in all of the conversations, right after messages are restored. And after that delete all the temporary messages, this will update the conversations time to last message time.
HashSet hs = new HashSet();
hs.addAll(list);
//list is the ArrayList<String> which contains the addressess of all the messages and
//through hashset we remove all the duplicates to get only the addressess once and hence we know the number of conversations and their addressess.
list.clear();
list.addAll(hs);
//Add some dummy message to each conversation
ContentValues values2 = new ContentValues();
for (int i = 0; i < list.size(); i++) {
values2.put("address",list.get(i));
values2.put("date_sent",readMsgsFromFile[0][1]);
values2.put("date",readMsgsFromFile[0][2]);
values2.put("type",readMsgsFromFile[0][3]);
values2.put("body","temp"); //this should be more unique
values2.put("read",readMsgsFromFile[0][5]);
values2.put("service_center","01010101");
contentResolver.insert(Telephony.Sms.CONTENT_URI, values2);
}
//Now deleting that message with body 'temp' from each conversation
Cursor c = contentResolver.query(Telephony.Sms.CONTENT_URI,null,null,null,null);
while (c.moveToNext()){
String body = c.getString(c.getColumnIndex("body"));
String mid = c.getString(0);
if (body.equals("temp")){
Log.i("Deleting ",mid);
getContentResolver().delete(Uri.parse(Telephony.Sms.CONTENT_URI+"/"+mid),null,null);
}
}
c.close();
This word 'temp' could be and should be more unique so that it is not mixed with actual message.
Here's what happen:
When I convert a bitmap into a bytearray, the bytearray stores more than 100,000 index, but when I use the SELECT function to get the bytearray, the bytearray have only 20 index left. What happen?
My field is using BLOB to store the bytearray, and the following code is what I'm trying to get the bytearray:
private void insertAttachmentRecord(int parentid) {
for(int k = 0; k < attachmentarray.size(); k++) {
byte[] claimAttachmentByteArray;
newAttachmentRecord = new ContentValues();
claimAttachmentByteArray = attachmentarray.get(k).getImageByteArray();
newAttachmentRecord.put("claimattachmentimage", claimAttachmentByteArray);
newAttachmentRecord.put("parent_id", parentid);
claiminfoDB.insert(DBATTACHMENT_TABLE, null, newAttachmentRecord);
}
}
And the query to get the bytearray:
private ArrayList<ClaimViewResult> GetClaimViewResult() {
ArrayList<ClaimViewResult> results = new ArrayList<>();
Cursor c2 = claiminfoDB.rawQuery("SELECT 'claimattachmentimage' FROM 'claimattachment' WHERE parent_id = "+bundletableId, null);
if (c2.getCount() != 0) {
c2.moveToFirst();
ClaimViewResult cvr = new ClaimViewResult();
byte[] imagebytearray = c2.getBlob(0);
Bitmap bitmap = BitmapFactory.decodeByteArray(imagebytearray, 0, imagebytearray.length);
cvr.setBitmap(bitmap);
cvr.setFilename(getFilename());
results.add(cvr);
while (c2.moveToNext()) {
cvr = new ClaimViewResult();
imagebytearray = c2.getBlob(0);
bitmap = BitmapFactory.decodeByteArray(imagebytearray, 0, imagebytearray.length);
cvr.setBitmap(bitmap);
cvr.setFilename(getFilename());
results.add(cvr);
}
}
return results;
}
Please help.
May be you need to change scheme for saving your bitmaps? For example, you can store bitmaps in inner app's directory and saving path to this image in database.
Also, you can try to change your select query like this:
SELECT * FROM your_table;
I think, you can receive only 20 bytes because you select word, not a column (I mean 'claimattachmentimage' in your query)
I have this piece of code that allows me to read the contents of the gallery of the phone and scroll randomly its contents.
public static Uri getRandomImage(ContentResolver resolver) {
String[] projection = new String[] {
BaseColumns._ID
};
Random rand = new Random();
int p = 2 + rand.nextInt(8-2+1);
Uri uri = p == 0 ? Media.EXTERNAL_CONTENT_URI : Media.EXTERNAL_CONTENT_URI;
Cursor cursor = Media.query(resolver, uri, projection, null, MediaColumns._ID);
if (cursor == null || cursor.getCount() <= 0) {
return null;
}
cursor.moveToPosition(new Random().nextInt(cursor.getCount()));
return Uri.withAppendedPath(uri, cursor.getString(0));
}
But I would like to modify this code to read the contents of a folder set by me. How can I do this?
thank you very much
Probably one of the easiest way to do: name each file in your folder numerically i.e. (supposing they are all jpg) name them 1.jpg, 2.jpg, 3.jpg and so on. Now all you need to do is generate an array of distinct random numbers between 1 and number of files in your folder and generate a uri by concatenating this random numbers with .jpg and then display them.
Maybe you should set the URI to a different value?
Random rand = new Random();
int p = 2 + rand.nextInt(8-2+1);
Uri uri = p == 0 ? Media.EXTERNAL_CONTENT_URI : Media.EXTERNAL_CONTENT_URI;
This code always sets the URI to Media.EXTERNAL_CONTENT_URI ... no matter what the value of the integer p is...
Personally I actually can't even see what you are trying to do there...
Getting random files out or selected directory is quite easy.
You can use this snippet:
private File getRandomFile(File root) {
File[] files = root.listFiles();
Random randomizer = new Random();
return files[randomizer.nextInt(files.length - 1)];
}
If you need the method to return Uri, you can change the return and method signature:
private Uri getRandomFile(File root) {
File[] files = root.listFiles();
Random randomizer = new Random();
File f = files[randomizer.nextInt(files.length - 1)];
return Uri.fromFile(f);
}
As for cursor requirement, you should be able to wrap this method to another method and bridge request accordingly.
What I am trying to accomplish here is to make a Cursor that could be used in an android ListView. I'm reading values directly from multiple files and have to feed them to the cursor. I tried to use MatrixCursor but I can't get it to work with arrays. I have posted my attempt at it so far below and I'm open to all new suggestions. Is there a simpler way to do this?
static MatrixCursor getnameList() {
ArrayList<String> fsitem = getfsiList();
MatrixCursor cursor;
cursor = null;
for (int i = 0; i < fsitem.size(); i++) {
try {
File root = new File(Environment.getExternalStorageDirectory()
.getName() + "/" + fsitem.get(i));
if (root.canRead()) {
File namefile = new File(root, ".name");
FileReader namereader = new FileReader(namefile);
BufferedReader in = new BufferedReader(namereader);
String name = in.readLine();
String id = in.readLine();
String info = in.readLine();
String[] fsii = new String[3];
fsii[0]= name;
fsii[1]= id;
fsii[2]= info;
cursor.addRow(fsii); //crashes here on running.
}
} catch (IOException e) {
Log.e("NameManager.java : ", ("Error!! Not Writable!!"
+ Environment.getExternalStorageDirectory().getName()
+ "/" + fsitem.get(i)));
}
}
This code compiles but crashes at cursor.addRow(fsii);:
with 02-24 21:16:49.589: E/AndroidRuntime(3895): at com.manager.abcd.r1223.NameManager.getnameList(NameManager.java:81).
I'm thinking this is a problem with MartixCursor not supporting arrays, but I might be wrong. Any ideas?
If this is all the code then it is normal because you try to add a row on a null cursor(you never initialize cursor) and probably get a NullPointerException.
Initialize the MatrixCursor before you enter in the for loop:
String[] columnNames = {"col1", "col2", "col3"};
MatrixCursor cursor = new MatrixCursor(columnNames);
Check the docs.