On broadcast receiver, check for write permission

2019-09-06 06:23发布

I use broadcastreceiver of media folder android.hardware.action.NEW_PICTURE service to rename and move image using this code and it was running:

CameraReciver package perim.ebrahimi.ir.perim;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;

public class CameraReciver extends BroadcastReceiver {

    private States states;
    private SessionManager session;
    private WriteService main;

    @Override
    public void onReceive(Context context, Intent intent) {
        getStates(context);
        if(states.getAPP()){

            Cursor cursor = context.getContentResolver().query(intent.getData(), null, null, null, null);
            cursor.moveToFirst();
            if(cursor!=null){
                String image_path = cursor.getString(cursor.getColumnIndex("_data"));
                main = new WriteService();
                main.writeFolder(image_path, states, context);
            }
            cursor.close();
        } else abortBroadcast();
    }

    // OK
    private void getStates(Context context){
        session = new SessionManager(context.getApplicationContext());
        states = new States();
        states = session.getSession();
    }

}

and here is WriteService:

package perim.ebrahimi.ir.perim;

import android.app.Activity;
import android.app.Application;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class WriteService extends Activity {

    private States states;
    private Context context;
    private static CalendarJalalian cal;
    private static int day   ;
    private static int month ;
    private static int year  ;
    private static int saat  ;
    private static int minut ;
    private static int secnd ;
    private int orientation = -1;
    private static final int REQUEST_EXTERNAL_STORAGE = 100;
    private static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS=200;
    private boolean mExternalStorageAvailable = false;
    private boolean mExternalStorageWriteable = false;

//    @Override
//    protected void onCreate(Bundle savedInstanceState) {
//        super.onCreate(savedInstanceState);
//        //setContentView(R.layout.activity_main);
//
//        // A simple check of whether runtime permissions need to be managed
//        if (Build.VERSION.SDK_INT >= 23) {
//            checkMultiplePermissions();
//        }
//    }

    public void writeFolder(String image_path, States _states, Context _context){
        states = _states;
        context=  _context;
        cal = new CalendarJalalian();
        long fileSize = new File(image_path).length();
        Cursor mediaCursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[] {MediaStore.Images.ImageColumns.ORIENTATION,
                        MediaStore.MediaColumns.SIZE },
                MediaStore.MediaColumns.DATE_ADDED + ">=?",
                new String[]{String.valueOf(cal.getTimeInMillis()/1000 - 1)},
                MediaStore.MediaColumns.DATE_ADDED + " desc");
        if (mediaCursor != null && mediaCursor.getCount() !=0 ) {
            while(mediaCursor.moveToNext()){
                long size = mediaCursor.getLong(1);
                if(size == fileSize){
                    orientation = mediaCursor.getInt(0);
                    break;
                }
            }
        }
        orientation = (orientation<0)?0:orientation;
        image_path = changeName(image_path);
        if(image_path.length()==0) return;
        String image_jadid = copyFile(image_path);
        if(states.getMain() && image_jadid.length()>0){
            removeMain(image_path);
            removeMedia(context, new File(image_path));
        }
        if(states.getPayam()) Toast.makeText(this, getText(R.string.imageSaved)+": "+states.getKhas(), 500).show();

    }

    private void checkExternalStorage(){
        String[] aaa = new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE};
        ActivityCompat.requestPermissions(this, aaa , REQUEST_EXTERNAL_STORAGE);
        if(ContextCompat.checkSelfPermission(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE);
        } else {
            String state = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(state)) {
                mExternalStorageAvailable = mExternalStorageWriteable = true;
            } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                mExternalStorageAvailable = true;
                mExternalStorageWriteable = false;
            } else {
                mExternalStorageAvailable = mExternalStorageWriteable = false;
            }
        }
    }

    private static void removeMedia(Context context, File f) {
        ContentResolver resolver = context.getContentResolver();
        resolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.DATA + "=?", new String[] { f.getAbsolutePath() });
    }

    // OK
    private String changeName(String image_path){
        String result = "";
        day   = cal.getDay();
        month = cal.getMonth();
        year  = cal.getYear();
        saat  = Integer.parseInt(cal.getHHour());
        minut = Integer.parseInt(cal.getMinute());
        secnd = Integer.parseInt(cal.getSecond());

        String persianDateName = String.format("%1$s_%2$s_%3$s__%4$s_%5$s_%6$s.jpg",
                year,
                ((month<10)?"0"+month:month),
                ((day<10)?"0"+day:day),
                ((saat<10)?"0"+saat:saat),
                ((minut<10)?"0"+minut:minut),
                ((secnd<10)?"0"+secnd:secnd));

        File from = new File(image_path);
        if(from.exists()){
            if(states.getOnimage()){
                addTextToImage(image_path, persianDateName);
            }
            File to = new File(from.getParentFile(),persianDateName);
            try
            {
                boolean b = from.renameTo(to);
                if (!b) {
                    copy(from, to);
                    from.delete();
                }
                removeMedia(context, from);
                sendToMedia(to, persianDateName);
                result = to.getAbsolutePath();
            }
            catch(Exception ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

    private void addTextToImage(String from, String persianDateName) {
        Log.i("info","Draw "+persianDateName+" on image");
        try
        {
            Log.i("info","1");
            FileOutputStream fos = new FileOutputStream(from);
            if(fos==null) Log.i("info","fos = null");

            Log.i("info","2 = "+from);
            Bitmap bitmap = BitmapFactory.decodeFile(from);
            if(bitmap==null) Log.i("info","bitmap = null");

//          Log.i("info","3");
            android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
            if(bitmapConfig==null) Log.i("info","bitmapConfig = null");

//          Log.i("info","4");
//          if (bitmapConfig == null) {
//              bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
//          }

            Log.i("info","5");
            bitmap = bitmap.copy(bitmapConfig, true);

            Log.i("info","6");
            Canvas canvas = new Canvas(bitmap);

            Log.i("info","7");
            Resources resources = this.getResources();
            float scale = resources.getDisplayMetrics().density;

            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.rgb(61, 61, 61));
            paint.setTextSize((int) (14 * scale));
            paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
            Rect qab = new Rect();
            paint.getTextBounds(persianDateName, 0, persianDateName.length(), qab);
            int x = (bitmap.getWidth() - qab.width()) / 2;
            int y = (bitmap.getHeight() + qab.height()) / 2;
            canvas.drawText(persianDateName, x, y, paint);

            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        Log.i("info","Text added on image");
    }

    private void copy(final File f1, final File f2) throws IOException {
        if(f2.exists()) f2.delete();
        //checkExternalStorage();
        checkMultiplePermissions();
        if(mExternalStorageAvailable && mExternalStorageWriteable) {
            f2.createNewFile();
            final RandomAccessFile file1 = new RandomAccessFile(f1, "r");
            final RandomAccessFile file2 = new RandomAccessFile(f2, "rw");
            file2.getChannel().write(file1.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f1.length()));
            file1.close();
            file2.close();
        }
    }

    private boolean canRename(final File f1, final File f2) {
        final String p1 = f1.getAbsolutePath().replaceAll("^(/mnt/|/)", "");
        final String p2 = f2.getAbsolutePath().replaceAll("^(/mnt/|/)", "");
        return p1.replaceAll("\\/\\w+", "").equals(p2.replaceAll("\\/\\w+", ""));
    }

    // OK
    private void removeMain(String image_path){
        File f = new File(image_path);
        if(f.exists()) f.delete();
    }


    // OK
    private File createFileName(){
        day   = cal.getDay();
        month = cal.getMonth();
        year  = cal.getYear();
        saat  = Integer.parseInt(cal.getHHour());
        minut = Integer.parseInt(cal.getMinute());
        secnd = Integer.parseInt(cal.getSecond());

        String persianDateName = String.format("%1$s_%2$s_%3$s__%4$s_%5$s_%6$s.jpg",
                year,
                ((month<10)?"0"+month:month),
                ((day<10)?"0"+day:day),
                ((saat<10)?"0"+saat:saat),
                ((minut<10)?"0"+minut:minut),
                ((secnd<10)?"0"+secnd:secnd));

        String masirFolder = "";

        if(states.getSal())   masirFolder += year;
        if(states.getMah())   masirFolder += File.separator+ year+"_"+((month<10)?"0"+month:month);
        if(states.getRuz())   masirFolder += File.separator+ year+"_"+((month<10)?"0"+month:month)+"_"+((day<10)?"0"+day:day);

        if(states.getSaat())  masirFolder += File.separator+ year+"_"+((month<10)?"0"+month:month)+"_"+((day<10)?"0"+day:day)+"_"+((saat<10)?"0"+saat:saat);
        if(states.getMinut()) masirFolder += File.separator+ year+"_"+((month<10)?"0"+month:month)+"_"+((day<10)?"0"+day:day)+"_"+((saat<10)?"0"+saat:saat)+"_"+((minut<10)?"0"+minut:minut);

        if(states.getKhas().length()>0) masirFolder += File.separator+states.getKhas();

        File directTime = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), masirFolder);

        if (!directTime.mkdir()) makeDir(directTime);

        directTime = new File(directTime, persianDateName);

        return directTime;

    }

    // OK
    private void makeDir(File direct){
        direct.mkdirs();
    }

    // OK
    private String copyFile(String image_path){
        String masir = "";
        File sourceFile = new File(image_path);
        if (!sourceFile.exists()) {return masir;}
        File destinationFile = createFileName();
        FileChannel source = null;
        FileChannel destination = null;

        try {
            source = new FileInputStream(sourceFile).getChannel();
            destination = new FileOutputStream(destinationFile).getChannel();
            if (destination != null && source != null) {
                destination.transferFrom(source, 0, source.size());
            }
            if (source != null) {
                source.close();
            }
            if (destination != null) {
                destination.close();
            }
            masir = destinationFile.getName();
            sendToMedia(destinationFile, masir);
            masir = destinationFile.getAbsolutePath();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return masir;
    }

    // OK
    private void sendToMedia(File imageFile, String imageTitle){
        ContentValues image = new ContentValues();
        Date dateTaken = new Date();

        File parent = imageFile.getParentFile();
        String path = parent.toString().toLowerCase();
        String name = parent.getName().toLowerCase();

        image.put(MediaStore.Images.Media.TITLE, imageTitle);
        image.put(MediaStore.Images.Media.DISPLAY_NAME, String.format(this.getText(R.string.imageDisplayName).toString(),states.getKhas()));
        image.put(MediaStore.Images.Media.DESCRIPTION, String.format(this.getText(R.string.imageDescription).toString(),name));
        image.put(MediaStore.Images.Media.DATE_ADDED, dateTaken.toString());
        image.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken.toString());
        image.put(MediaStore.Images.Media.DATE_MODIFIED, dateTaken.toString());
        image.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
        image.put(MediaStore.Images.Media.ORIENTATION, orientation);//getImageOrientation(imageFile.getAbsolutePath()));

        image.put(MediaStore.Images.ImageColumns.BUCKET_ID, path.hashCode());
        image.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, name);
        image.put(MediaStore.Images.Media.SIZE, imageFile.length());
        image.put(MediaStore.Images.Media.DATA, imageFile.getAbsolutePath());

        this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, image);
    }

    private void checkMultiplePermissions() {

        if (Build.VERSION.SDK_INT >= 23) {
            List<String> permissionsNeeded = new ArrayList<String>();
            List<String> permissionsList = new ArrayList<String>();

            if (!addPermission(permissionsList, android.Manifest.permission.ACCESS_FINE_LOCATION)) {
                permissionsNeeded.add("GPS");
            }

            if (!addPermission(permissionsList, android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
                permissionsNeeded.add("Read Storage");
            }

            if (permissionsList.size() > 0) {
                requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                return;
            }
        }
    }

    private boolean addPermission(List<String> permissionsList, String permission) {
        try {
            if (Build.VERSION.SDK_INT >= 23)

                if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionsList.add(permission);

                    if (!shouldShowRequestPermissionRationale(permission))
                        return false;
                }
        } catch(Exception ex){
            ex.printStackTrace();
        }
        return true;
    }
}

But class addPermission raise error:

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.content.Context.checkSelfPermission(java.lang.String)' on a null object reference

I think the way I call activity is not correct, but I have no idea how to do it, please help.

Edit: I have found this difference in returned path:

String image_path = cursor.getString(cursor.getColumnIndex("_data"));

The changed part is obvious:

old but correct path: /storage/emulated/0/DCIM/Camera/IMG_20161215_173334.jpg
new but incorrect path: /storage/3466-033/DCIM/Camera/IMG_20161215_173334.jpg

I think since new devices permit user to select where to save Images, then returning address is changed accordingly. How to get correct address out of cursor?

1条回答
放荡不羁爱自由
2楼-- · 2019-09-06 07:25

You can't do that with a broadcast receiver. You can check if you have a permission via ContextCompat.checkSelfPermission, but in order to request the permission, you need to call ActivityCompat.requestPermissions. It needs an activity, and the result will also arrive to that activity.

Given that, the best solution for you is to implement some activity which will explain the user what is going on and request the permission. If the broadcast receiver detects that it doesn't have the necessary permission, it would launch this activity. When the permission is granted, the normal operation would be resumed.

I have to say that as a user it would be very weird to me if a dialog with a permission request suddenly popped up out of the blue, so I think it's for the best that you need to have an activity to request permissions.

Judging by your path, however, it is definitely somewhere on the SD card. This means that the SD card write restrictions apply. That means that requesting permissions for writing won't help. Take a look at this question: How to avoid the “EACCES permission denied” on SD card?

查看更多
登录 后发表回答