[Android][scoped storage] open failed: EACCES (Permission denied)


<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

AndroidManifest.xml

 

FileInputStream inputStream = new FileInputStream(originalFile);

/storage/emulated/0/Download/photo-1579165466741-7f35e4755660.jpeg


 10.0(Q, API29)

https://stackoverflow.com/a/57804657/7225691

<manifest ... >
    <!-- This attribute is "false" by default on apps targeting Android Q. -->
    <application android:requestLegacyExternalStorage="true" ... >
     ...
    </application>
</manifest>

Add android:requestLegacyExternalStorage="true"  in application label

 

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);

	Uri imgUri = data.getData();
	String imgPath = getPath(getApplicationContext(), imgUri);
	
	if(imgUri != null && imgPath != null){

		InputStream in = getContentResolver().openInputStream(imgUri);//src

		String extension = imgPath.substring(imgPath.lastIndexOf("."));
		File localImgFile = new File(getApplicationContext().getFilesDir(), "localImgFile"+extension);

		if(in != null) {
			try {
				OutputStream out = new FileOutputStream(localImgFile);//dst
				try {
					// Transfer bytes from in to out
					byte[] buf = new byte[1024];
					int len;
					while ((len = in.read(buf)) > 0) {
						out.write(buf, 0, len);
					}
				} finally {
					out.close();
				}
			} finally {
				in.close();
			}
		}

		//InternalStorage
	}
}
  /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The activity.
     * @param uri The Uri to query.
     * @author paulburke
     */
    public static String getPath(final Context context, final Uri uri) {

        // DocumentProvider
        if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }else{
                    Toast.makeText(context, "Could not get file path. Please try again", Toast.LENGTH_SHORT).show();
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                } else {
                    contentUri = MediaStore.Files.getContentUri("external");
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null; 

    } 

Crash Reporting with ACRA

 Using crash reporting tools is a great way to unearth the hidden bugs that are causing crashes. This tutorial will demonstrate an easy to use crash reporting tool ACRA (Application Crash Reports for Android) to send crash logs when crash occurs.

At first step we shall include the dependency of ACRA in our build.gradle file. The version 4.9.2 was the latest at the time of writing this tutorial.

compile 'ch.acra:acra:4.9.2'

At next step we have to create our Application class integrate ACRA there.

@ReportsCrashes(
        formUri = "",
        mailTo = "yourmail@yourdomain.com",
        customReportContent = {
                ReportField.APP_VERSION_CODE,
                ReportField.APP_VERSION_NAME,
                ReportField.ANDROID_VERSION,
                ReportField.PHONE_MODEL,
                ReportField.BRAND,
                ReportField.CUSTOM_DATA,
                ReportField.INITIAL_CONFIGURATION,
                ReportField.CRASH_CONFIGURATION,
                ReportField.USER_CRASH_DATE,
                ReportField.STACK_TRACE,
                ReportField.LOGCAT},
        resToastText = R.string.crash_toast_text,
        mode = ReportingInteractionMode.TOAST)
public class MyApplication extends Application {
 
    @Override
    public void onCreate() {
        super.onCreate();
        ACRA.init(this);
    }
}

The code is very much self explanatory, we need to set the email address to whom crash report will be send, set some content and select how will the app interact with the user when it is crashed.

We have to set the MyApplication as the application class and add the following internet, read logs and read phone state permission. The AndroidManifest.xml file will look like the following.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.acrademo">
 
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_LOGS" />
 
    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

Finally in our MainActivity, we shall add a button and add code that will force the app crash when button is clicked to test our app.

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    public void onCrash(View view){
        throw new RuntimeException("App Crashed");
    }
}

Playing audio stream using ExoPlayer 2

 Exoplayer is an Android application level media player developed by google for playing audio, video both locally and over the internet. Unlike the MediaPlayer API it is highly customizable. Minimum supported version for Exoplayer is Android 4.1 (API level 16). In this we are going to see a simple example of playing an audio stream URL using ‘Exoplayer 2’.

At first step we need to add the library in dependency section of the the build.gradle file by adding the following line

dependencies {
...
compile 'com.google.android.exoplayer:exoplayer:r2.4.2'
}

In this tutorial we are going to use a SimpleExoPlayer instance that requires three objects RendersFactory, TracSelector and LoadControl. Let’s create the objects one by one with appropriate parameters.

RendersFactory interface is used to build Renderer instances for use by a SimpleExoPlayer. Renderers are used to render individual components of the media. Let’s implement the RendersFactory by using the DefaultRendersFactory like below

renderersFactory = new DefaultRenderersFactory(getApplicationContext());

TrackSelector is used to select tracks provided by the MediaSource to be consumed by each of the available Renderers. We shall have look at how to build MediaSource object later on. Let’s create the TrackSelector by adding the following lines

bandwidthMeter = new DefaultBandwidthMeter(); //Provides estimates of the currently available bandwidth.
trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
trackSelector = new DefaultTrackSelector(trackSelectionFactory);

LoadControl is used to control when the MediaSource buffers more media, and how much media is buffered. In this example, we are going to use the default buffer settings.

loadControl = new DefaultLoadControl();

At this point we are ready to create the SimpleExoPlayer instance. We could also implement the ExoPlayer.EventListener in our Activity class and add this listener to our SimpleExoPlayer instance.

player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
player.addListener(this);

Now its time to create MediaSource. MediaSource defines the media to be played, loads the media, and from which the loaded media can be read. Let’s create the MediaSource in the following way

dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(), "ExoplayerDemo");
extractorsFactory = new DefaultExtractorsFactory();
mainHandler = new Handler();
mediaSource = new ExtractorMediaSource(Uri.parse(streamUrl),
                dataSourceFactory,
                extractorsFactory,
                mainHandler,
                null);

In the above we used DefaultDataSource instance that delegates to DefaultHttpDataSources for non-file/asset/content URIs and DefaultExtractorsFactory that provides an array of extractors for the formats MP4, fMP4, Matroska and WebM, Ogg Vorbis/FLAC, MP3, AAC, MPEG TS, MPEG PS, FLV, WAV, AC3, FLAC.
We have defined our streamUrl at the top like the following

private final String streamUrl = "http://bbcwssc.ic.llnwd.net/stream/bbcwssc_mp1_ws-einws"; //bbc world service url

Finally we need to prepare the player with the MediaSource in the following way.

player.prepare(mediaSource);

To keep the example simple no user interface was created to play or pause the media stream. We are going to play the stream when the activity resumes by including the following line inside the Activity lifecycle method onResume()

player.setPlayWhenReady(true);

and going to pause the stream when the Activity is on pause state by adding the following line inside Activity life cycle method onPause()

player.setPlayWhenReady(true);

Our MainActivity class now looks like the following.

public class MainActivity extends AppCompatActivity implements ExoPlayer.EventListener {
 
 
    private Handler mainHandler;
    private RenderersFactory renderersFactory;
    private BandwidthMeter bandwidthMeter;
    private LoadControl loadControl;
    private DataSource.Factory dataSourceFactory;
    private ExtractorsFactory extractorsFactory;
    private MediaSource mediaSource;
    private TrackSelection.Factory trackSelectionFactory;
    private SimpleExoPlayer player;
    private final String streamUrl = "http://bbcwssc.ic.llnwd.net/stream/bbcwssc_mp1_ws-einws"; //bbc world service url
    private TrackSelector trackSelector;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        renderersFactory = new DefaultRenderersFactory(getApplicationContext());
        bandwidthMeter = new DefaultBandwidthMeter();
        trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
        trackSelector = new DefaultTrackSelector(trackSelectionFactory);
        loadControl = new DefaultLoadControl();
 
        player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
        player.addListener(this);
 
        dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(), "ExoplayerDemo");
        extractorsFactory = new DefaultExtractorsFactory();
        mainHandler = new Handler();
        mediaSource = new ExtractorMediaSource(Uri.parse(streamUrl),
                dataSourceFactory,
                extractorsFactory,
                mainHandler,
                null);
 
        player.prepare(mediaSource);
 
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        player.setPlayWhenReady(true);
        Toast.makeText(MainActivity.this, "Exoplayer is playing.", Toast.LENGTH_SHORT).show();
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        player.setPlayWhenReady(false);
        Toast.makeText(MainActivity.this, "Exoplayer is on pause.", Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void onTimelineChanged(Timeline timeline, Object manifest) {
 
    }
 
    @Override
    public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
 
    }
 
    @Override
    public void onLoadingChanged(boolean isLoading) {
 
    }
 
    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
 
    }
 
    @Override
    public void onPlayerError(ExoPlaybackException error) {
 
    }
 
    @Override
    public void onPositionDiscontinuity() {
 
    }
 
    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
 
    }
}

How to extract filename from Uri?

Now, we can extract filename with and without extension :) You will convert your bitmap to uri and get the real path of your file. Now w...