I am calling a permissions request from a non-Activity based class inside of a Cordova plugin, and the main activity of my application cannot be extended so I cannot implement/override the onRequestPermissionsResult method of the activity. I need to run an event after the user has authorized permissions like the commented pseudocode lambda below, but I am not sure how. I have tried creating my own Activity subclass and implementing this method, but I get a NullPointerException.
class MyPlugin extends CordovaPlugin {
private void saveAndOpenFile() {
int permission =
ActivityCompat.checkSelfPermission(
cordova.getActivity(),
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
cordova.getActivity(),
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
); /*.then((int requestCode, String[] permissions, int[] grantedResults) -> {
// Do something crazy
});*/
}
}
}
I suggest following the documentation on this one.
In short, it appears you should use cordova.hasPermission to check if we have the permission, then use cordova.requestPermission, which "will call the activity and cause a prompt to appear, asking for the permission. Once the user has the permission, the result must be handled with the onRequestPermissionResult method, which every plugin should override." So, it sounds like CordovaPlugin offers an empty base implementation of onRequestPermissionResult that your plugin should override.
Granted, I imagine you can probably get away with calling ActivityCompat.checkSelfPermission and Acitivity.requestPermissions, but either way, your plugin should override onRequestPermissionResult.
class MyPlugin extends CordovaPlugin {
private void saveAndOpenFile() {
/* check permissions
...
*/
}
#Override
public void onRequestPermissionResult(int requestCode, String[] permissions,
int[] grantResults) throws JSONException {
/* check permission granted and proceed accordingly */
}
}
I m trying to get the Internal Storage path of a Android Device.
Since most of the devices late off return Internal Storage path using Environment.getExternalStorageDirectory().getPath(); so I am using this to get the path.
Every thing is working fine except that when I am calling Environment.getExternalStorageDirectory().getPath(); from an non Activity class it is returning null whereas if I call it from a Activity class it returns the correct path.
I Tried searching other posts but could not find anything useful.
Any help would be really grateful.
EDIT:
if(getExtSdCardPath(con)!=null)
{ path=getExtSdCardPath(con);
if(new File(path).getPath().equal(Environment.getExternalStorageDirectory().getPath())) // This line give null "Null Pointer exception"
{
return null;
}
return path;
}
I am checking if the SD Card path is same as the path which is returned by Environment.getExternalStorageDirectory().getPath()
Ideally, getExtSdCardPath() would be "idempotent", which is a fancy way of saying "does the same work and returns the same thing no matter how many times you call it".
In your case, it is not. The first call to getExtSdCardPath() is returning the value that you want, and the second call to getExtSdCardPath() is returning null.
In your case, there is no particular need to call getExtSdCardPath() twice, and so you can work around the idempotence issue by rewriting your code to be something like:
path=getExtSdCardPath(con);
if(path!=null)
{
if(new File(path).getPath().equal(Environment.getExternalStorageDirectory().getPath())) // This line give null "Null Pointer exception"
{
return null;
}
return path;
}
Sounds like you forgot to put the requested permission in the manifest OR/AND forgot to ask for such a permission in runtime (in case you run this on devices with android 6.0 and above).
try to addto your manifest : <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Here a quick and simple implementation example of how to request the permission in runtime:
public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback{
private static final int REQUEST_WRITE_PERMISSION = 111; //Number is not matter, just put what you want
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_WRITE_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Do your stuff with the file
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermission();
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
} else {
//Do your stuff with the file
}
}
}
Currently I am working on the Google's Saved Games integration into an Android app.
I am trying to create a new snapshot after the user requests new save. I implemented onActivityResult as i found here:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
// requestCode and resultCode checks happen here, of course...
if (intent != null) {
if (intent.hasExtra(Snapshots.EXTRA_SNAPSHOT_METADATA)) {
// Load a snapshot.
SnapshotMetadata snapshotMetadata = intent.getParcelableExtra(Snapshots.EXTRA_SNAPSHOT_METADATA);
currentSaveName = snapshotMetadata.getUniqueName();
loadFromSnapshot(snapshotMetadata);
} else if (intent.hasExtra(Snapshots.EXTRA_SNAPSHOT_NEW)) {
// Create a new snapshot named with a unique string
// TODO: check for existing snapshot, for now, add garbage text.
String unique = new BigInteger(281, new Random()).toString(13);
currentSaveName = "snapshotTemp-" + unique;
saveSnapshot(null);
}
}
}
Obviously it is a good idea to check if a snapshot with the generated name already exists. How should I actually do it?
The list of existing saved games can be retrieved by calling [Snapshots.load()](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshots#load(com.google.android.gms.common.api.GoogleApiClient, boolean)). This is an asynchrounous call, so one way to use it is to call it before saving and keep the names in a list which you can then compare to the new name.
The sample CollectAllTheStars (https://github.com/playgameservices/android-basic-samples) demonstrates how to use this API to display a custom view to select a saved game.
Games.Snapshots.load(mGoogleApiClient, false).setResultCallback(
new ResultCallback<Snapshots.LoadSnapshotsResult>() {
#Override
public void onResult(Snapshots.LoadSnapshotsResult loadSnapshotsResult) {
mSavedGamesNames = new ArrayList<String>();
for (SnapshotMetadata m :loadSnapshotsResult.getSnapshots()) {
mSavedGamesNames.add(m.getUniqueName());
}
}
});
I'm having a weird issue that is causing a conflict. I had to switch to native Fragments to fix it, but there are bugs with that.
My original problem: I have a navigation drawer setup with v4 Fragments. To ask for permission in one of my Fragments I call ActivityCompat.requestPermissions(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION, 1); The prompt shows up just fine, but when I accept or deny the permission, nothing happens. The callback onRequestPermissionsResult() is never called. Instead it gets called in the Activity that my Fragments are attached to. Useless to me, I need the callback to work in the Fragment.
With this in mind I was told that I need to use FragmentCompat, but that only works with native Fragments (v13+), so I changed navigation drawer to work from native Fragments instead of the v4 support library Fragments. However, because I'm using AppCompatActivity, certain things do not work, like addToBackStack() and going back to a previous fragment.
Long story short, does anyone know how I can use the v4.Fragment and still call for permission in the Fragment and get the callback to be in the Fragment? I feel like this is a bug in Android that hasn't been addressed but I'm not 100%.
Let me know if you need to see my code, it's just the standard methods that you need for runtime permissions, I would like to work with v4 Fragments though which doesn't work from my understanding.
Adding this to the parent activity works for me:
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
Source: https://code.google.com/p/android/issues/detail?id=189121#c5
If you need to get the permissionResult in fragment v4 make sure you use
Fragment.requestPermission(String[], int);
instead of
AppCompat.requestPermission(Activity, String[], int)
Check out this answer!
This behavior seems to be present in the v4 Fragment support class requestPermissions in Fragment. The Activity/FragmentCompat implementations exist for people who wish to use the native classes with the extended functionality on API levels between 11 and 23.
You can use this part of code
requestPermissions(new String[]{Manifest.permission.GET_ACCOUNTS}, PERMISSION_REQUEST_CODE);
I faced the same situation recently, when I needed to check for a permission inside the support fragment and get a callback there.
I was able to use ContextCompat to checkSelfPermission and then as #NasaGeek said called android.support.v4.app.Fragment's requestPermissions to request the permission and then got a call back to onRequestPermissionsResult in v4 Fragment.
Hope this helps.
Thanks.
v4.Fragment works well. I have an issue with nested fragment of v4.Fragment. Permission is asked, but the callback onRequestPermissionsResult() is never called in nested fragment!
Issue opened
At the moment the most stable solution is doing it by hand. I myself resolved it simply by notifying child fragments from the parent fragments.
if (fragment.getParentFragment() != null) {
Fragment parentFragment = fragment.getParentFragment();
try {
ChildFragmentPermissionRegistry registry = (ChildFragmentPermissionRegistry) parentFragment;
registry.registerPermissionListener((ChildFragmentPermissionCallback) fragment);
parentFragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_REQUEST_CODE);
} catch (ClassCastException e) {
Log.e(PermissionVerifier.class.getSimpleName(), e.getMessage());
}
} else {
fragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_REQUEST_CODE);
}
Where parent fragment implements interface ChildFragmentPermissionRegistry and registers child fragment,
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (callback != null) {
callback.onRequestPermissionsResult(requestCode, permissions, grantResults);
callback = null;
}
}
and child fragments implements ChildFragmentPermissionCallback
and interfaces are something like this:
public interface ChildFragmentPermissionCallback {
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
}
public interface ChildFragmentPermissionRegistry {
void registerPermissionListener(ChildFragmentPermissionCallback callback);
}
I don't know if it's recently fixed by google, but I can reach the expected result without doing any tricks.
The most important thing is to call super.onRequestPermissionsResult(requestCode, permissions, grantResults); in the activity, so it will pass the result to fragment if it's requested from fragment.
So, what I do is:
1) in fragment, ask permission using v4 fragment.requestPermissions(permissions, requestCode)
2) in activity's onRequestPermissionsResult, must call
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
3) in fragment's onRequestPermissionsResult, write the code I want to handle the result.
In my case I have requested the permission from the fragment and also need to get the response in fragment.
But my phone running on android 8.1
so I was need to add one more condition for check this
so eventually there is my solution
private void doOnWazeButtonClick()
{
int permissionStatus = PackageManager.PERMISSION_DENIED;
if (getContext() != null)
{
permissionStatus = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION);
}
if (permissionStatus == PackageManager.PERMISSION_GRANTED)
{
showContentWaze();
}
else
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
Objects.requireNonNull(getActivity()).requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_PERMISSION_ACCESS_FINE_LOCATION);
}
else
{
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_PERMISSION_ACCESS_FINE_LOCATION);
}
}
}
Check runtime permission from Fragment (the 2021 way)
I have answered this in a different question, here is the link: answer link.
In short, we can now use registerForActivityResult in a fragment. In your fragment's constructor (before onCreate), you initialize ActivityResultLauncher<String[]> activityResultLauncher resultLauncher, and when you need permission, simply invoke resultLauncher.launch(stringArrayOfPermissions). Please check the link on the first line for detailed example.
Just use requestPermission("your permission/s in string array",your request code) simply no need to use Fragment.requestPermissons(String[],int );
This method in your fragment calls requestPermissions of android.support.v4.app.Fragment class i.e
public final void requestPermissions(#NonNull String[] permissions, int requestCode) {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mHost.onRequestPermissionsFromFragment(this, permissions, requestCode);
}
I'm trying to create a custom module in Appcelerator for the new Square API for Android. I have everything the way I want it, but the main problem is that I want to be able to notify the caller that the payment was successful for if it failed. The Square API says this:
After Square finishes, Android invokes Activity.onActivityResult() on the activity passed to the constructor. The request code passed to this method will be passed to onActivityResult(). The result code is Activity.RESULT_CANCELED if the payment was canceled or Activity.RESULT_OK if the payment succeeded.
I've been passing the TiContext.currentActivity to the constructor:
public SquareModule(TiContext tiContext) {
super(tiContext);
ourSquare = new Square(tiContext.getActivity());
}
And then in the method that actually runs the payment, I have this that basically tries to set the passed in callback to the onResult handlers of the current activity using the registerResultHandler in the TiActivitySupportHelper class.
public void runPayment(KrollInvocation invocation, int price, String description, KrollCallback handler) {
Log.i(LCAT, "runPayment called");
// Register the passed in function as a handler on the onResult stack
this.resultCallback = handler;
Activity activity = invocation.getTiContext().getActivity();
TiActivitySupportHelper support = new TiActivitySupportHelper(activity);
int code = support.getUniqueResultCode();
support.registerResultHandler(code, this);
// Some of the payment work here
ourSquare.squareUp(Bill.containing(advice), code);
}
The main module class implements TiActivityResultHandler and implements onResult and onError. These methods are not being called at all. And of course the passed in method isn't being called either.
For completeness, see the implementation of the onResult and onError handlers:
#Override
public void onResult(Activity activity, int requestCode, int resultCode, Intent data)
{
Log.i(LCAT, "onResult Called");
if (resultCallback == null) return;
KrollDict event = new KrollDict();
event.put(TiC.EVENT_PROPERTY_REQUEST_CODE, requestCode);
event.put(TiC.EVENT_PROPERTY_RESULT_CODE, resultCode);
event.put(TiC.EVENT_PROPERTY_INTENT, new IntentProxy(getTiContext(), data));
event.put(TiC.EVENT_PROPERTY_SOURCE, this);
resultCallback.callAsync(event);
}
#Override
public void onError(Activity activity, int requestCode, Exception e)
{
Log.i(LCAT, "onError Called");
if (resultCallback == null) return;
KrollDict event = new KrollDict();
event.put(TiC.EVENT_PROPERTY_REQUEST_CODE, requestCode);
event.put(TiC.EVENT_PROPERTY_ERROR, e.getMessage());
event.put(TiC.EVENT_PROPERTY_SOURCE, this);
resultCallback.callAsync(event);
}
And also see the Appcelerator JS calling the method in the module:
square.runPayment(2, 'Testing123', function(e) {
label1.text = 'Payment Successful!';
});
For those that come upon this question. The answer can be found in the module here:
https://github.com/hidef/Appcelerator-Square-Module (see the LaunchSquare.java class)
Basically, I used an Activity object that I created to receive the Square API's onResult update. I then was able to pass that back cleanly to the module class and hand it back via callback to the calling application.