Thursday, January 29, 2026

Android java: write and delete file into folder creeated by application in Download pattern

Target device Android 7 (API 24) to Android 16 (API 36)

AndroidManifest.xml permission 

     <uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />
    <!-- targeting api 13++ use media store -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

To write contact (vcard) to folder com.dedetok.wredacontactbackup in folder Download

    final static String subFolder = "com.dedetok.wredacontactbackup"; // folder ref
    private static File backupFolder, backupFile; //


    private static boolean writeSingleVcf(Context context, Collection<VCard> vCards) {
        try {

            String date = new SimpleDateFormat("yyyyMMddHHmmSS", Locale.US).format(new Date());
            String fileName = date + ".vcf";


            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                // ✅ API 29-36 (Android 10+): Use MediaStore (Scoped Storage)
                return MyWorker.writeMediaStore(context, fileName, vCards);
            } else {
                // ✅ API 24-28 (Android 7-9): Use traditional File API
                // ✅ API 29-36 (Android 10+): Use MediaStore (Scoped Storage)
                return MyWorker.writeLegacy(context, fileName, vCards);

            }
        } catch (Exception e) {
            //Log.e("dedetok",e.getMessage()); // debug
            return false;
        }
    }


    /*
     * write backup MediaStore
     */
    @RequiresApi(api = Build.VERSION_CODES.Q)
    private static boolean writeMediaStore (Context context, String fileName, Collection<VCard> vCards) throws IOException{
        ContentValues values = new ContentValues();
        //Log.e("dedetok", "writeMediaStore"); // debug
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
        values.put(MediaStore.MediaColumns.MIME_TYPE, "text/x-vcard");
        // Creates 'Download/YourSubFolder/' automatically
        values.put(MediaStore.MediaColumns.RELATIVE_PATH,
                Environment.DIRECTORY_DOWNLOADS + File.separator + subFolder);

        Uri uri = context.getContentResolver().insert(
                MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
        if (uri == null) return false;

        OutputStream os = context.getContentResolver().openOutputStream(uri);
        Ezvcard.write(vCards).version(VCardVersion.V3_0).go(os);
        return true;
    }

    /*
     * write backup in legacy mode
     */
    private static boolean writeLegacy(Context context, String fileName, Collection<VCard> vCards) throws IOException{
        // Setup Directory
        File downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        backupFolder = new File(downloads, MySharePreferences.PREF_NAME);
        // Create folder if missing
        if (!backupFolder.exists()) {
            boolean success = backupFolder.mkdirs();
            if (!success) {
                //Log.e("dedetok", "Failed to create folder. Check permissions!"); // debug
                return false;
            }
        }

        backupFile = new File(backupFolder, fileName);
        try (FileOutputStream fos = new FileOutputStream(backupFile)) {
            Ezvcard.write(vCards).version(VCardVersion.V3_0).go(fos);
        }

        //Log.d("dedetok", "File saved at: " + backupFolder.getName()); // debug

        // Tell Android to scan the file so it shows up in Windows/File Manager
        MediaScannerConnection.scanFile(
                context,
                new String[]{ backupFile.getAbsolutePath() },
                null,
                new MediaScannerConnection.OnScanCompletedListener() {
                    @Override
                    public void onScanCompleted(String path, Uri uri) {
                        //Log.e("dedetok", "Scanned: " + path);
                    }
                }
        );

        //Log.e("dedetok", "File created successfully "+backupFile.getName()); // debug
        return true;

    }

To delete file created by application from folder com.dedetok.wredacontactbackup in folder Download

    /*
     * delete File helper for android 7 yo 16
     */
    static private boolean deleteFile(Context context, File fileToDelete) {
        boolean returnValue = false;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            // Android 7–9
            if (fileToDelete.exists()) {
                returnValue = fileToDelete.delete();
                if (returnValue) {
                    // IMPORTANT: Tell the system the file is gone so Google Files updates
                    MediaScannerConnection.scanFile(context, new String[]{fileToDelete.getAbsolutePath()}, null, null);
                }
            }
        } else {
            // Android 10 to 16
            ContentResolver resolver = context.getContentResolver();
            Uri collection = MediaStore.Downloads.EXTERNAL_CONTENT_URI;

            // IMPORTANT: RELATIVE_PATH must end with a slash /
            String relativePath = Environment.DIRECTORY_DOWNLOADS + "/" + subFolder + "/";

            String selection = MediaStore.MediaColumns.DISPLAY_NAME + "=? AND " +
                    MediaStore.MediaColumns.RELATIVE_PATH + "=?";

            String[] selectionArgs = new String[]{
                    fileToDelete.getName(),
                    relativePath
            };

            int rows = 0;
            try {
                // Directly delete from MediaStore - this usually deletes the physical file too
                rows = resolver.delete(collection, selection, selectionArgs);
            } catch (Exception e) {
                //Log.e("dedetok", "MediaStore delete failed", e);
            }

            if (rows > 0) {
                returnValue = true;
            } else {
                // FALLBACK: Physical delete if MediaStore didn't find it
                if (fileToDelete.exists()) {
                    returnValue = fileToDelete.delete();
                    if (returnValue) {
                        // Sync the index
                        MediaScannerConnection.scanFile(context, new String[]{fileToDelete.getAbsolutePath()}, null, null);
                    }
                }
            }
        }
        return returnValue;
    }