Categories
discuss

BitmapFactory: Unable to decode stream: java.io.FileNotFoundException:open failed: EACCES (Permission denied) on Android Q

I granted permissions but BitmapFactory.decodeFile() returns this error:

E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20200130_165131.jpg: open failed: EACCES (Permission denied)

I want to take the last image from the gallery and save it to another file. I tested it on the Android P test device it was running on. But it returns this error in android emulator with android version Q (API level 29). What’s wrong with that?

These are my code snippets:

writeStoragePermissionGranted();

BitmapFactory.Options options = null;
Bitmap bm = null;

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,
            MediaStore.Images.ImageColumns.DISPLAY_NAME,
    };

    final Cursor cursor = this.getContentResolver()
            .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
                    null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
      if (cursor.moveToFirst()) {
        final ImageView imageView = (ImageView) findViewById(R.id.pictureView);
        String imageLocation = cursor.getString(1);
        File imageFile = new File(imageLocation);

        if (imageFile.exists()) {
            options = new BitmapFactory.Options();
            options.inSampleSize = 2;

            try {
                    bm = BitmapFactory.decodeFile(imageLocation, options);
            } catch(Exception e) {
                    e.printStackTrace();
            }
        }

        imageView.setImageBitmap(bm);



        try {
            saveImage(bm,"NAME" );
        } catch (IOException e) {
                e.printStackTrace();
        }
    }

writeStoragePermissionGranted() function:

 public void writeStoragePermissionGranted() {
       if (Build.VERSION.SDK_INT >= 23) {
           if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                   == PackageManager.PERMISSION_GRANTED) {
            startPeriodicRequest();
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION);
        }
    } else {
        return;
    }
} 

and manifest permissions:

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

I know there are many question about that. But no one worked on the android emulator version Q. On the other hand, this code works on android pie (API level 28)

Answer

On Android 10, they introduced the concept of “Scoped Storage” and this way, you no longer can OPEN a image using its path. You can get more info HERE.

So now you have to decode it using its ParcelFileDescriptor and its Uri.

You can:

final Cursor cursor = this.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor.moveToFirst()) {
    final ImageView imageView = (ImageView) findViewById(R.id.pictureView);


    if (Build.VERSION.SDK_INT >= 29) {
        // You can replace '0' by 'cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)'
        // Note that now, you read the column '_ID' and not the column 'DATA'
        Uri imageUri= ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getInt(0));

        // now that you have the media URI, you can decode it to a bitmap
        try (ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(mediaUri, "r")) {
            if (pfd != null) {
                bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
            }
        } catch (IOException ex) {

        }
    } else {
        // Repeat the code you already are using
    }
}
Categories
discuss

MutableLiveData setValue: Unexpected behavior in Activity’s onCreate

I’m toying with MutableLiveData’s methods to figure out what triggers the observer and what doesn’t.

Right now, I have this Activity:

class ActivityA : AppCompatActivity() {
    private val liveData = MutableLiveData<Int>().apply { this.value = 10 }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        liveData.observe(this, Observer {
            Log.v("ActivityA", "liveData = $it")
        })
        Log.v("ActivityA", "liveData = ${liveData.value}")
        liveData.value = 11
        liveData.postValue(12)
        liveData.value = 13
    }
}

The output is the following:

liveData = 10
liveData = 13
liveData = 12

Shouldn’t it be this?

liveData = 10
liveData = 11
liveData = 13
liveData = 12

Answer

Your problem is that livedata observer is not active. because of this when you do liveData.value = 11, this value doesn’t get posted to the observer. and subsequently when you do liveData.value = 13 it overrides the value 11.

To check if your live data has any active observers, you can do liveData.hasActiveObservers()

Docs clearly state that setValue only dispatches if there are any active observers

void setValue (T value)

Sets the value. If there are active observers, the value will be dispatched to them.

But why your observer is not active?

When you set a value, LiveData internally uses ObserverWrapper‘s shouldBeActive method to check if a specific observer is active .

and when you use observe method to register your observer, the observer is wrapped in a LifecycleBoundObserver (subclass of ObserverWrapper) which defines its shouldBeActive as follows.

@Override
boolean shouldBeActive() {
   return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

The part that matters is .isAtLeast(STARTED), here STARTED is of type Lifecycle.State, and its documentation has following to say about it.

Started state for a LifecycleOwner. For an Activity, this state is reached in two cases:

after onStart call;

right before onPause call.

and because you register observer in onCreate, it doesn’t become active right away and hence the problem.

For verification you can also use the observeForever (please read its documentation, it differs greatly from observe) method to register your observer. because this method makes the observer active instantly, you will see the output that you expect.

liveData.observeForever {
    Log.v("ActivityA", "liveData = $it")
}
Categories
discuss

How to Ignore view component(s) that I do not want to create bindings in Android view Binding library?

I am exploring new Android View Binding library.

In my layout XML file, some views that I don’t want to include in my binding class.

Does there any attribute or mechanism exist that exclude views into generated Binding class?

Answer

We can use tools:viewBindingIgnore=”true/false” to include and exclude view in generated view binding class.

Usage:

<LinearLayout
    ...
    tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

Check more about View Binding on below link:

https://developer.android.com/topic/libraries/view-binding#setup

Categories
discuss

Publishing app to google play – where is the demo account data?

As of late, google play is “reviewing” new apps that were published to google play.

My app requires the reviewer to have a demo account, otherwise the user cannot go past the initial screen.

Where is the place that I provide the reviewer with the demo account?

Answer

Google play does not open the app at all, they run automated tests on it, that does not seem to include opening the app.

It took 3 days and change but it was approved.

Categories
discuss

What are the compatibility risks of replacing METHOD/FIELD/etc. targets with TYPE_USE

I have a Java package that contains annotations used by external clients. The package appeared before Java 8, so historically these annotations have targets ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE. Now the package requires at least Java 8 version. Semantically the annotations in the package are applicable to the types, so e.g. when the method is annotated, the annotation effectively applies to the method return type. Now clients want to annotate generic type arguments as well (e.g. List<@MyAnnotation String>). Since we dropped the support of Java 7 and below, it seems quite natural to set the annotation target to ElementType.TYPE_USE and, to reduce ambiguity, remove the existing targets.

Here’s the question: are there any compatibility risks for existing clients when replacing ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE targets with TYPE_USE? Is it possible that the existing code will stop compiling? What about binary compatibility? May it cause any runtime problems if the class-files compiled before the change are used together with newer annotations pack at runtime?

Annotations’ retention policy is CLASS if this matters.

Answer

There are a number of source compatibility issues that may arise during this change:

  • Methods with void return type cannot be annotated anymore. @MyAnnotation void method() {} is compilable with ElementType.METHOD target but not compilable with TYPE_USE target.
  • When a qualified type is used in the source code, the TYPE_USE annotation must appear after the qualifier. E.g. void method(@MyAnnotation OuterClass.InnerClass param) {} is a valid code with ElementType.PARAMETER target, but should be updated to void method(OuterClass.@MyAnnotation InnerClass param) {} after changing to TYPE_USE.
  • Annotation applied to arrays will have a different meaning. E.g. before the migration @MyAnnotation String[] getData(); annotates the method getData, so the annotation clients likely assume that annotation is applied to the return type (an array of strings). After the migration, the same code means that the annotation is applied to the array component (string). This might cause a behavioral change, depending on the annotation semantics and how it’s processed by clients. To preserve the meaning, clients should update such a code to String @MyAnnotation [] getData();.
  • Such a change makes all usages of the annotations in Kotlin code invalid. In Kotlin, there’s strict syntactical difference between TYPE_USE annotations and others. E.g. a PARAMETER annotation must be used as fun(@MyAnnotation param : String) {...}. This is incorrect for TYPE_USE annotation, which must be used as fun(param : @MyAnnotation String) {...}. If your clients use Kotlin they will have to fix every single annotation use.
  • Such a change disallows using your annotations in Groovy code. Currently, as Groovy documentation says, Groovy does not support the TYPE_PARAMETER and TYPE_USE element types which were introduced in Java 8. If your clients use Groovy they won’t be able to use your annotation package anymore.

No problems should arise at runtime though. As annotations’ retention policy is CLASS (not RUNTIME), while the annotations present in class files they are ignored by the runtime. It’s not necessary to add your annotation pack to the classpath at all.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..