On Android 11, when trying to remove multiple tracks from a Playlist, all the tracks are removed instead of just the selected ones. This used to work fine till Android 10.
However, if only 1 track is removed, no other tracks are removed. Is this a bug or an expected behavior going forward from Android 11? Is this a side-effect of scoped storage?
Tested on : Pixel 3, Android 11 Official release build
public void removeTracksFromPlaylist(long playlistId, String[] ids) {
Log.d(TAG, "removeTracksFromPlaylist() called with: playlistId = [" + playlistId + "], ids = [" + Arrays.toString(ids) + "]");
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
StringBuilder stringBuilder = new StringBuilder();
for (int index = 0; index < ids.length; index++) {
if (index > 0) {
stringBuilder.append(",");
}
stringBuilder.append("?");
}
String selection = MediaStore.Audio.Playlists.Members._ID + " IN (" + stringBuilder.toString() + ")";
contentResolver.delete(uri, selection, ids); // Returns count of all files removed from playlist
}
Edit:
Some information from source : (https://android.googlesource.com/platform/packages/providers/MediaProvider/+blame/refs/tags/android-11.0.0_r3/src/com/android/providers/media/MediaProvider.java#5610)
From the source, I see that if the count of playlist items are more than 1, the whole playlist is cleared.
try (Cursor c = qb.query(helper,
new String[] { Playlists.Members.PLAY_ORDER }, queryArgs, null)) {
if ((c.getCount() == 1) && c.moveToFirst()) { // This condition fails if more than 1 item to delete
return c.getInt(0) - 1;
} else {
return -1;
}
}
The index is used here which clears the playlist:(https://android.googlesource.com/platform/packages/providers/MediaProvider/+blame/refs/tags/android-11.0.0_r3/src/com/android/providers/media/MediaProvider.java#5566)
private int removePlaylistMembers(#NonNull Uri playlistUri, #NonNull Bundle queryArgs)
throws FallbackException {
final int index = resolvePlaylistIndex(playlistUri, queryArgs);
try {
final File playlistFile = queryForDataFile(playlistUri, null);
final Playlist playlist = new Playlist();
playlist.read(playlistFile);
final int count;
if (index == -1) {
count = playlist.asList().size();
playlist.clear(); //Clears whole playlist
} else {
count = 1;
playlist.remove(index);
}
playlist.write(playlistFile);
resolvePlaylistMembers(playlistUri);
return count;
} catch (IOException e) {
throw new FallbackException("Failed to update playlist", e,
android.os.Build.VERSION_CODES.R);
}
}
Related
i try to join two Contacts, a default Contact from the Default Address-Store, and a Contact of my own Provider.
I have the following code-snippet of the com.android.Contact app:
private interface JoinContactQuery {
String[] PROJECTION = {
RawContacts._ID,
RawContacts.CONTACT_ID,
RawContacts.NAME_VERIFIED,
RawContacts.DISPLAY_NAME_SOURCE,
};
String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
int _ID = 0;
int CONTACT_ID = 1;
int NAME_VERIFIED = 2;
int DISPLAY_NAME_SOURCE = 3;
}
Cursor c = resolver.query(ContactsContract.RawContacts.CONTENT_URI,
JoinContactQuery.PROJECTION,
JoinContactQuery.SELECTION,
new String[]{String.valueOf(contactId1), String.valueOf(contactId2)}, null);
if (c == null) {
Log.e(TAG, "Unable to open Contacts DB cursor");
return;
}
long rawContactIds[];
long verifiedNameRawContactId = -1;
try {
if (c.getCount() == 0) {
return;
}
int maxDisplayNameSource = -1;
rawContactIds = new long[c.getCount()];
for (int i = 0; i < rawContactIds.length; i++) {
c.moveToPosition(i);
long rawContactId = c.getLong(JoinContactQuery._ID);
rawContactIds[i] = rawContactId;
int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
if (nameSource > maxDisplayNameSource) {
maxDisplayNameSource = nameSource;
}
}
// Find an appropriate display name for the joined contact:
// if should have a higher DisplayNameSource or be the name
// of the original contact that we are joining with another.
if (writable) {
for (int i = 0; i < rawContactIds.length; i++) {
c.moveToPosition(i);
if (c.getLong(JoinContactQuery.CONTACT_ID) == contactId1) {
int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
if (nameSource == maxDisplayNameSource
&& (verifiedNameRawContactId == -1
|| c.getInt(JoinContactQuery.NAME_VERIFIED) != 0)) {
verifiedNameRawContactId = c.getLong(JoinContactQuery._ID);
}
}
}
}
} finally {
c.close();
}
// For each pair of raw contacts, insert an aggregation exception
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
for (int i = 0; i < rawContactIds.length; i++) {
for (int j = 0; j < rawContactIds.length; j++) {
if (i != j) {
buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
}
}
}
// Mark the original contact as "name verified" to make sure that the contact
// display name does not change as a result of the join
if (verifiedNameRawContactId != -1) {
Builder builder = ContentProviderOperation.newUpdate(
ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
builder.withValue("name_verified", 1);
operations.add(builder.build());
}
My Problem: The "name_verified" Field is removed on Android M (Preview 2). Whats the "correct" way to join two Contacts (and do not change the Name of the Contact)?
* EDIT / Solution *
Remove name_verified and set "IS_SUPER_PRIMARY" to content uri
//mark as SUPER PRIMARY
if (verifiedNameRawContactId != -1) {
operations.add(
ContentProviderOperation.newUpdate(ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, verifiedNameRawContactId))
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.build());
}
The question is not quite clear to me but i think your are trying to join two contacts and one name is taking precedence over another but not the way you want it.
The correct way to join contacts is to add a row in this table and keep the type as TYPE_KEEP_TOGETHER. AggregationsExceptions table
If you want to keep the name of the first raw_contact mark the name record of the contact in Data table (StructuredName) as IS_SUPER_PRIMARY
IS_SUPER_PRIMARY
I hope you can help me.
I make a game like 4Pics1Word.
I want to load the Level Randomly, I want a loop which generate a Random number from 0 to 10, and then check if the generated number is the first time loaded.
If yes write it in an array and end the loop.
If the number is not the first time loaded, generate a new random number and check it again until it is not used.
For example this is my code (donĀ“t work right):
Boolean usedImageSet = false;
for (int t = 0; t <= usedImages.length; t++) {
if (usedImageSet == false) {
StringBuilder sb = new StringBuilder();
sb.append(currentQuestion);
String used = sb.toString();
if (usedImages[t] != null) {
System.out.println("usedImage" + t
+ " = not Null, it is" + usedImages[t]);
if (usedImages[t].equals(used)) {
System.out.println("String: "
+ used
+ " found it here: [" + t + "]");
currentQuestion = (int) (Math.random() * 10);
}else {
System.out.println("String: "
+ used + " not found");
}
}
if (usedImages[t] == null) {
usedImages[t] = used;
System.out.println("useddImage[" + t + "]: "
+ usedImages[t]);
System.out.println("usedImage" + t + " is Null, change to"
+ usedImages[t]);
usedImageSet = true;
}
}
}
PS:
Thank you all, I think the solution from Oren is the best
// naive implementation
ArrayList<Integer> list = new ArrayList();
for(int i=0; i<10; i++)
{
list.add(i);
}
Collections.shuffle(list);
// output the generated list
for(int i=0; i<10; i++)
{
System.out.print(list.get(i));
}
But how can I save the list if I close the game?
You would probably be much better off creating a List of the numbers you want and then calling Collections.shuffle() on that list.
// naive implementation
ArrayList<Integer> list = new ArrayList();
for(int i=0; i<10; i++)
{
list.add(i);
}
Collections.shuffle(list);
// output the generated list
for(int i=0; i<10; i++)
{
System.out.print(list.get(i));
}
int oldnumber = 5;
int newnumber = new Random().nextInt(10);
while (newnumber == oldnumber){
newnumber = new Random().nextInt(10);
}
public class MainActivity extends Activity {
FileOutputStream fos;
FileInputStream fOne, fTwo;
ArrayList<String> arr1 = new ArrayList<String>();
ArrayList<String> arr2 = new ArrayList<String>();
ArrayList<String> words = new ArrayList<String>();
ArrayList<String> wordsTwo = new ArrayList<String>();
int count = 0;
int countTwo = 0;
int countThree = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button fileOne = (Button)findViewById(R.id.file1);
Button fileTwo = (Button)findViewById(R.id.file2);
Button compare = (Button)findViewById(R.id.compare);
arr1.add("1");
arr1.add("2");
arr1.add("3");
arr1.add("4");
//arr1.add("3");
arr2.add("1");
arr2.add("2");
fileOne.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
try
{
fos = openFileOutput("File1", Context.MODE_PRIVATE);
for(int temp = 0; temp< arr1.size(); temp++)
{
fos.write((arr1.get(temp).getBytes()) );
fos.write(System.getProperty("line.separator").getBytes());
}
fos.close();
fos.flush();
}
catch(Exception e)
{
}
}
});
fileTwo.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
try
{
fos = openFileOutput("File2", Context.MODE_PRIVATE);
for(int temp = 0; temp< arr2.size(); temp++)
{
fos.write((arr2.get(temp).getBytes()) );
fos.write(System.getProperty("line.separator").getBytes());
}
fos.close();
fos.flush();
}
catch(Exception e)
{
}
}
});
compare.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
try
{
fOne = openFileInput("File1");
fTwo = openFileInput("File2");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Scanner scanFile = new Scanner(new DataInputStream(fOne));
Scanner scanFileT = new Scanner(new DataInputStream(fTwo));
words = new ArrayList<String>();
wordsTwo = new ArrayList<String>();
while (scanFile.hasNextLine())
{
if(scanFile.nextLine()!=null)
{
count++;
}
while(scanFileT.hasNextLine())
{
if(scanFileT.nextLine()!=null)
{
countTwo++;
}
}
}
try
{
fOne.close();
fTwo.close();
scanFile.close();
scanFileT.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Toast.makeText(getBaseContext(), "One : " + count, 1000).show();
Toast.makeText(getBaseContext(), "Two : " + countTwo, 1000).show();
Toast.makeText(getBaseContext(), "Three : " + countThree, 1000).show();
count = 0 ;
countTwo = 0;
countThree = 0;
}
});
}
}
Above is the code to write and read the file. What I did here, write two files and read the contents..Now I have to compare contents of files line by line. What needs to be done?
Try following code. This will give you desired output. I took files from asset directory. So you need to replace that line of code if you are taking files from other directory.
private void compareFiles() throws Exception {
String s1 = "";
String s2 = "", s3 = "", s4 = "";
String y = "", z = "";
// Reading the contents of the files
BufferedReader br = new BufferedReader(new InputStreamReader(
getAssets().open("first.txt")));
BufferedReader br1 = new BufferedReader(new InputStreamReader(
getAssets().open("second.txt")));
while ((z = br1.readLine()) != null) {
s3 += z;
s3 += System.getProperty("line.separator");
}
while ((y = br.readLine()) != null) {
s1 += y;
s1 += System.getProperty("line.separator");
}
// String tokenizing
StringTokenizer st = new StringTokenizer(s1);
String[] a = new String[10000];
for (int l = 0; l < 10000; l++) {
a[l] = "";
}
int i = 0;
while (st.hasMoreTokens()) {
s2 = st.nextToken();
a[i] = s2;
i++;
}
StringTokenizer st1 = new StringTokenizer(s3);
String[] b = new String[10000];
for (int k = 0; k < 10000; k++) {
b[k] = "";
}
int j = 0;
while (st1.hasMoreTokens()) {
s4 = st1.nextToken();
b[j] = s4;
j++;
}
// comparing the contents of the files and printing the differences, if
// any.
int x = 0;
for (int m = 0; m < a.length; m++) {
if (a[m].equals(b[m])) {
} else {
x++;
Log.d("Home", a[m] + " -- " + b[m]);
}
}
Log.d("Home", "No. of differences : " + x);
if (x > 0) {
Log.d("Home", "Files are not equal");
} else {
Log.d("Home", "Files are equal. No difference found");
}
}
Input File 1
Hi
Hello
Chintan
Rathod
Input File 2
Hi
HellO
Chintan
RathoD
Output
08-26 12:07:58.219: DEBUG/Home(2350): Hello3. -- HellO3.
08-26 12:07:58.219: DEBUG/Home(2350): Rathod -- RathoD
08-26 12:07:58.229: DEBUG/Home(2350): No. of differences : 2
08-26 12:07:58.229: DEBUG/Home(2350): Files are not equal
Edit
To get Difference between two files
Use StringUtils library which is provide by Apache and check this Documentation for more about that library.
And modify following lines of code.
int x = 0;
for (int m = 0; m < a.length; m++) {
if (a[m].equals(b[m])) {
} else {
x++;
Log.d("Home", a[m] + " -- " + b[m]);
//to print difference
if (a[m].length() < b[m].length())
Log.d("Home", "" + StringUtils.difference(a[m], b[m]));
else
Log.d("Home", "" + StringUtils.difference(b[m], a[m]));
}
}
Output
08-26 17:51:26.949: DEBUG/Home(17900): 12 -- 123
08-26 17:51:26.949: DEBUG/Home(17900): Difference String : 3
08-26 17:51:26.949: DEBUG/Home(17900): No. of differences : 1
08-26 17:51:26.949: DEBUG/Home(17900): Files are not equal
Try using java.util.Scanner
while (sc1.hasNext() && sc2.hasNext()) {
String str1 = sc1.next();
String str2 = sc2.next();
if (!str1.equals(str2))
System.out.println(str1 + " != " + str2);
}
Change your while loop to the following:
while (scanFile.hasNextLine() && scanFileT.hasNextLine())
{
if(scanFileT.nextLine().equals(scanFile.nextLine()))
{
// The lines are equal.
} else {
// The lines are not equal.
}
}
if(scanFile.hasNextLine() || scanFileT.hasNextLine())
{
// If more lines remain in one of the files, they are not equal.
} else {
// If no content remains in both files, they are equal.
}
Depending on the size of your file, I would recommend some optimisation like checking the file sizes before you go through them line by line.
The overall logic reads as follows; if both have another line, compare it to see if it is equal. If they don't have another line, check if one of them has lines remaining, if so, they are not equal.
Update
After clarifying the objective of the comparison in chat, see the comments to this question, I have come to the conclusion that another comparison would be more effective and, as a matter of fact, correct. The comparison algorithm above works great if comparing the structure of text but not if comparing a data vector which may or may not be sorted. After some discussion, we came to the conclusion that data needs to be sorted or the comparison will blow the complexity to at least O(n^2)which could be done in O(2n) if the data is sorted. Here the algorithm's skeleton:
if(! scanGroupFriends.hasNextLine())
{
//simple sanity check to see if we need to compare at all. In this case, add all friends.
} else {
String nextFriend = scanGroupFriends.nextLine();
while(scanAllFriends.hasNextLine())
{
if(scanAllFriends.nextLine().equals(nextFriend))
{
// Friend already figures, do not add him and advance the list of group friends.
if(scanGroupFriends.hasNextLine())
{
nextFriend = scanGroupFriends.nextLine();
} else {
// There are no more friends in the group, add all remaining friends to list to show.
break; // Terminate the `while` loop.
}
}
}
}
However, I personally think it is bad to make to many assumptions. What I would suggest is that the friends be saved in a Set, a TreeSet for example. Then, serialize the object rather than manually writing it to file. Sets are neat because they hold several interesting objects. For example, you could easily use the following code to remove all friends in a group from the set of all friends:
allFriends.removeAll(groupFriends);
However, be aware that this removes it from the set completely so you should make a copy beforehand.
i have an array of names which have multiple files and folders...now i want to sort the names according to files and folders.all folders first and then all files should display.i have the variable to check whether on particular index of array their is file or folder.but unable to think the logic..
i am attaching some of my codes.
////////////////////////////////
case 0://Sort By Name
{
if(m_sortType == SORT_BY_NAME && temp==false)
{
m_sortType = SORT_BY_NAME;
m_sortOrder=SORT_ORDER_DESCENDING;
temp= true;
//Log.d("SORTING", "SORT - NAME - DES");
}
else
{
m_sortType = SORT_BY_NAME;
m_sortOrder=SORT_ORDER_ASCENDING;
temp=false;
//Log.d("SORTING", "SORT - NAME - AES");
}
//Log.d("SORTING", "Data bfore sort");
for (int k=0; k<m_adapter.m_env.m_count; k++)
//Log.d("SORTING DATA", k + ": " + m_adapter.m_env.m_fs.get(m_SortArray[k]).m_name);
m_adapter.sortListing(m_sortType,m_sortOrder);
//Log.d("SORTING", "Data after sort");
for (int k=0; k<m_adapter.m_env.m_count; k++)
//Log.d("SORTING DATA", k + ": " + m_adapter.m_env.m_fs.get(m_SortArray[k]).m_name);
//Refresh();
break;
}
////////////////
private void sortListing(int sortType, int sortOrder)
{
m_sortType = sortType;
m_sortOrder = sortOrder;
Arrays.sort( m_SortArray , new Comparator() {
public int compare(Integer a1, Integer a2)
{
if(m_sortType == SORT_BY_NAME)
{
String s1 = null,s2 = null;
FileFolderEnum t2 = null;
FileFolderEnum t1 = null;
int i;
if(m_sortOrder==SORT_ORDER_ASCENDING)
{
s1 = m_env.m_fs.get(a1).m_name;
s2 = m_env.m_fs.get(a2).m_name;
t1 = m_env.m_fs.get(a1).m_type;
t2 = m_env.m_fs.get(a2).m_type;
}
else if(m_sortOrder==SORT_ORDER_DESCENDING)
{
s1 = m_env.m_fs.get(a2).m_name;
s2 = m_env.m_fs.get(a1).m_name;
t1 = m_env.m_fs.get(a2).m_type;
t2 = m_env.m_fs.get(a1).m_type;
}
//Log.d("SORTING COMPARE", "(" + Integer.toString(a1)+") s1: " + s1);
//Log.d("SORTING COMPARE", "(" + Integer.toString(a2)+") s2: " + s2);
if((t1.equals(CFileFolder.FileFolderEnum.FFE_FOLDER)&&(t2.equals(CFileFolder.FileFolderEnum.FFE_FOLDER))))
{
i=s1.compareToIgnoreCase (s2);
}
i=s1.compareToIgnoreCase (s2);
//Log.d("SORTING COMPARE", "s1.compareTo(s2): " + Integer.toString(i));
return i;
}
///////////////////
Code I pulled from my own file browser. Use as you wish. :
File[] directoryList = currentFolder.listFiles();
if (directoryList != null) {
List<File> directoryListing = new ArrayList<File>();
directoryListing.addAll(Arrays.asList(directoryList));
Collections.sort(directoryListing, new SortFileName());
Collections.sort(directoryListing, new SortFolder());
}
//sorts based on the files name
public class SortFileName implements Comparator<File> {
#Override
public int compare(File f1, File f2) {
return f1.getName().compareTo(f2.getName());
}
}
//sorts based on a file or folder. folders will be listed first
public class SortFolder implements Comparator<File> {
#Override
public int compare(File f1, File f2) {
if (f1.isDirectory() == f2.isDirectory())
return 0;
else if (f1.isDirectory() && !f2.isDirectory())
return -1;
else
return 1;
}
}
I know it's an old post but I needed to solve this issue and came across the post. dymmeh's solution was a good start but I wanted to sort in one pass and I wanted Windows-like sorting (case not considered). Here's what I came up with:
import java.io.File;
import java.util.Comparator;
public class FileComparator implements Comparator<File> {
#Override
public int compare(File lhs, File rhs) {
if (lhs.isDirectory() == rhs.isDirectory()) { // Both files are directory OR file, compare by name
return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase());
} else if (lhs.isDirectory()) { // Directories before files
return -1;
} else { // rhs must be a directory
return 1;
}
}
}
Usage is straight forward:
final File file = new File(_directory);
final File[] files = file.listFiles();
Arrays.sort(files, new FileComparator());
Well you have the variable to check the name is of file and folder, I can think of a way to sort them as per your requirement. Make two ArrayLists. Separate folders and files in these two ArrayList and then sort both according to the names, simple sort operation for string. Then simply append the files List at the end of Folder List. May be it will work. Seems simple.
I am using native Android camera and save file to my application data folder (/mnt/sdcard/Android/data/com.company.app/files/Pictures/). At the same time anther copy of photo is saved to DCIM folder.
This is my code:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String formattedImageName = getDateString() + ".jpg";
File image_file = new File(this.getExternalFilesDir(Environment.DIRECTORY_PICTURES), formattedImageName);
Uri imageUri = Uri.fromFile(image_file);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent, REQUEST_FROM_CAMERA);
How can I prevent saving additional copy of image to DCIM folder?
Many Thanks
You can use the following :
First we get the last saved image by checking which was the last modified image. Then check if last modified time is in the last few seconds. You may also have to check the exact location of where camera stores the image.
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
File[] images = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera").listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
// OR JUST Use success = latestSavedImage.delete();
success = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera/"
+ latestSavedImage.getAbsoluteFile()).delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
Unfortunately, some smart phones save images in another folder such as DCIM/100MEDIA. So can't rely to these solution. I prefer use this way:
String[] projection = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE};
final Cursor cursor = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,projection, null, null,
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if(cursor != null){
cursor.moveToFirst();
// you will find the last taken picture here and can delete that
}
I tried to find out if a second copy exists and delete the copy. I used the above code to find the last taken picture.
Notice: Don't use cursor.close(); after using managedQuery, Leave the cursor for the Android system to manage and don't call that. You can see managedQuery()
Notice2: The managedQuery method is deprecated and it should be avoided, implement CursorLoaders instead.
check this code..
private void FillPhotoList() {
// initialize the list!
GalleryList.clear();
String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","projection "+projection[0].toString());
// intialize the Uri and the Cursor, and the current expected size.
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","FillPhoto Uri u "+u.toString());
// Query the Uri to get the data path. Only if the Uri is valid.
if (u != null)
{
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the id).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Loop each and add to the list.
GalleryList.add(c.getString(0)); // adding all the images sotred in the mobile phone(Internal and SD card)
}
while (c.moveToNext());
}
Log.i(INFOLOG,"gallery size "+ GalleryList.size());
}
and this is where the method is doing all magic
/** Method will check all the photo is the gallery and delete last captured and move it to the required folder.
*/
public void movingCapturedImageFromDCIMtoMerchandising()
{
// This is ##### ridiculous. Some versions of Android save
// to the MediaStore as well. Not sure why! We don't know what
// name Android will give either, so we get to search for this
// manually and remove it.
String[] projection = { MediaStore.Images.ImageColumns.SIZE,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA,
BaseColumns._ID,};
// intialize the Uri and the Cursor, and the current expected size.
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","on activityresult projection "+projection[i]);
//+" "+projection[1]+" "+projection[2]+" "+projection[3] this will be needed if u remove the for loop
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","on activityresult Uri u "+u.toString());
if (CurrentFile != null)
{
// Query the Uri to get the data path. Only if the Uri is valid,
// and we had a valid size to be searching for.
if ((u != null) && (CurrentFile.length() > 0))
{
//****u is the place from data will come and projection is the specified data what we want
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the size).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Check each area in the gallery we built before.
boolean bFound = false;
for (String sGallery : GalleryList)
{
if (sGallery.equalsIgnoreCase(c.getString(1)))
{
bFound = true;
Log.i("InfoLog","c.getString(1) "+c.getString(1));
break;
}
}
// To here we looped the full gallery.
if (!bFound) //the file which is newly created and it has to be deleted from the gallery
{
// This is the NEW image. If the size is bigger, copy it.
// Then delete it!
File f = new File(c.getString(2));
// Ensure it's there, check size, and delete!
if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
{
// Finally we can stop the copy.
try
{
CurrentFile.createNewFile();
FileChannel source = null;
FileChannel destination = null;
try
{
source = new FileInputStream(f).getChannel();
destination = new FileOutputStream(CurrentFile).getChannel();
destination.transferFrom(source, 0, source.size());
}
finally
{
if (source != null)
{
source.close();
}
if (destination != null)
{
destination.close();
}
}
}
catch (IOException e)
{
// Could not copy the file over.
ToastMaker.makeToast(this, "Error Occured", 0);
}
}
//****deleting the file which is in the gallery
Log.i(INFOLOG,"imagePreORNext1 "+imagePreORNext);
Handler handler = new Handler();
//handler.postDelayed(runnable,300);
Log.i(INFOLOG,"imagePreORNext2 "+imagePreORNext);
ContentResolver cr = getContentResolver();
cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + c.getString(3), null);
break;
}
}
while (c.moveToNext());
}
}
}
A nice solution by Parth. But it's good for Samsungs that keep images in DCIM/Camera. Some phones - Sony Ericssons, HTCs keep them in folders like DCIM/100MEDIA, DCIM/100ANDRO so I have slightly modified the code:
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
//Samsungs:
File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM/Camera");
if(!folder.exists()){ //other phones:
File[] subfolders = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM").listFiles();
for(File subfolder : subfolders){
if(subfolder.getAbsolutePath().contains("100")){
folder = subfolder;
break;
}
}
if(!folder.exists())
return false;
}
File[] images = folder.listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
success = latestSavedImage.delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
I am encountering a similar problem with the Moto Z Force (7.1.1). I have the MediaStore.EXTRA_OUTPUT defined on the intent, but a duplicate file is still created in the camera directory.
I need to test on other devices, but here's an approach I took regarding this issue. Rather than trying to find the specific camera directory, I'm using the MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME location.
Here's my code snippet:
private void removeCameraDuplicate() {
String[] proj = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns._ID };
String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = ? ";
String[] selectionArgs = new String[] { "Camera" };
Cursor cursor = mActivity.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, selection, selectionArgs, MediaStore.Images.ImageColumns.DATE_TAKEN + " desc");
if (cursor != null) {
int idxPath = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (cursor.getCount() > 0 && idxPath > -1 && cursor.moveToFirst()) {
File original = new File(mMediaPath);
File cameraDupe = new File(cursor.getString(idxPath));
if (original.exists() && cameraDupe.exists()) {
LogUtils.LOGE("***> camera", "original " + original.length());
LogUtils.LOGE("***> camera", "original " + original.lastModified());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.length());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.lastModified());
if (original.length() == cameraDupe.length() && original.lastModified() == cameraDupe.lastModified()) {
if (cameraDupe.delete()) {
LogUtils.LOGE("***> camera", "duplicate deleted");
}
}
}
}
cursor.close();
}
}