codenameone / CodenameOne

Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.
https://www.codenameone.com/
Other
1.72k stars 408 forks source link

Provide support to hard code permission dialog in android in your source code #3474

Open DurankGts opened 3 years ago

DurankGts commented 3 years ago

I had provided a solution to your hard code dialog in the android side when the user request any runtime permission:

https://github.com/codenameone/CodenameOne/pull/2182

you must implement other way to the developer to show a custom dialog.

this your source code

public static boolean checkForPermission(String permission, String description, boolean forceAsk){ //before sdk 23 no need to ask for permission if(android.os.Build.VERSION.SDK_INT < 23){ return true; }

    String prompt = Display.getInstance().getProperty(permission, description);

    if (android.support.v4.content.ContextCompat.checkSelfPermission(getContext(),
            permission)
            != PackageManager.PERMISSION_GRANTED) {

        if (getActivity() == null) {
            return false;
        }

        // Should we show an explanation?
        if (!forceAsk && android.support.v4.app.ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                permission)) {

            // Show an expanation to the user *asynchronously* -- don't block
            if(Dialog.show("Requires permission", prompt, "Ask again", "Don't Ask")){
                return checkForPermission(permission, description, true);
            }else {
                return false;
            }
        } else {

            // No explanation needed, we can request the permission.
            ((CodenameOneActivity)getActivity()).setRequestForPermission(true);
            ((CodenameOneActivity)getActivity()).setWaitingForPermissionResult(true);
            android.support.v4.app.ActivityCompat.requestPermissions(getActivity(),
                    new String[]{permission},
                    1);
            //wait for a response
            Display.getInstance().invokeAndBlock(new Runnable() {
                @Override
                public void run() {
                    while(((CodenameOneActivity)getActivity()).isRequestForPermission()) {
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            //check again if the permission is given after the dialog was displayed
            return android.support.v4.content.ContextCompat.checkSelfPermission(getActivity(),
                    permission) == PackageManager.PERMISSION_GRANTED;

        }
    }
    return true;
}

your hardcode dialog don't have the possibility to adapt to the content of the text. This cause that your dialog waste a lot of unnecessary space.

image

Please provide and interface to listen when the user select an option in the runtime permission dialog.

DurankGts commented 3 years ago

public boolean isLocationPermissionGranted() { String methodName = "isLocationPermissionGranted"; try { int api = android.os.Build.VERSION.SDK_INT; Log.i(TAG, methodName + "()->api:" + api); if (api >= 23) { final Activity activity = AndroidNativeUtil.getActivity(); int p = ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION); Log.i(TAG, methodName + "()->permission:Manifest.permission.ACCESS_FINE_LOCATION:" + p+":"+(p == PackageManager.PERMISSION_GRANTED));

            return p == PackageManager.PERMISSION_GRANTED;

        } else {
            return true;
        }
    } catch (Exception e) {
        Log.e(TAG, methodName + "()->Error:" + e.toString());
        return false;
    }
}

I was using my own custom dialog asking first(see up method) to call your api location to avoid your source code dialog. But with the new option added in android 10:

image

your method

if (android.support.v4.content.ContextCompat.checkSelfPermission(getContext(), permission) != PackageManager.PERMISSION_GRANTED)

is returning false althought I check before to call your api location and your dialog if showing again.

shai-almog commented 3 years ago

I don't understand this issue. if (android.support.v4.content.ContextCompat.checkSelfPermission(getContext(), permission) != PackageManager.PERMISSION_GRANTED) is an Android API not our method.

DurankGts commented 3 years ago

you must to create a way that the developer user a custom dialog.

// Show an expanation to the user asynchronously -- don't block if(Dialog.show("Requires permission", prompt, "Ask again", "Don't Ask")){ return checkForPermission(permission, description, true); }else { return false; }

your dialog don't adapt to its content

DurankGts commented 3 years ago

Hello... any response about this?

shai-almog commented 3 years ago

You can write native code that does that and executes before the invocation of the applicable Codename One API occurs.

DurankGts commented 3 years ago

This what I don't to avoid your hard code dialog in the Location Api. Your read very well this issue?

DurankGts commented 3 years ago

The developers are tied Hands with your Hard code dialog. From Target 29 this error are occuring. I avoid this dialog with my native Request permission Implemented, but with this specific option Launched by google from api 28 your method is returning false when the user select this option.

DurankGts commented 3 years ago

this issue is occurring from android api 29,30. Google separate the way that you use the Location in foreground and background

https://developer.android.com/training/location/permissions?hl=fr

Now google decline our release App by this policies() of background use. I think that when I request ACCESS_FINE_LOCATION in your source code All time you are requesting ACCESS_BACKGROUND_LOCATION. This the cause why your hard code dialog appear.

You must implement in the LocationManager Api the way to separe foreground location from Background location.

DurankGts commented 3 years ago

I have check your source code and I found that you have and option to avoid request background location.

image

I think that you must annunciate any change to your developers that impact the flow of our apps. this issue I reported on july 12 I you don't tell me the cause of this flow.

shannah commented 3 years ago

Notice that option is "false" by default. Meaning that it does NOT request background permissions in this block unless you explicitly set that display property.

This option had/has no impact on the flow of your app because it did not change the flow from the previous default unless you explicitly set that flag.

DurankGts commented 3 years ago

Please provide other solution to this hard code dialog. Create and interface or callback in the Codenameone side to notify permission required and the developer can create its own dialog. this dialog occupies much unnecessary space.

image

DurankGts commented 3 years ago

Notice that option is "false" by default. Meaning that it does NOT request background permissions in this block unless you explicitly set that display property.

This option had/has no impact on the flow of your app because it did not change the flow from the previous default unless you explicitly set that flag.

I never have seen this option, I check now in your source code because this issue was impacting the flow of my app. How can you explain me that your hard code dialog it appear although I request fine location?

DurankGts commented 3 years ago

your are hard coding in your generated build server code the background location. this the main cause why this impact my app. Google now decline all app that don't justify the access to background. I think that you must documéntate and launch a blog message to all your developers. image

shannah commented 3 years ago

Are you using the Geofence API? This can trigger this flag to be added as it requires background permissions.

DurankGts commented 3 years ago

Not I'm not using. I just use LocationManager api.

shannah commented 3 years ago

Do you call setBackgroundLocationListener() anywhere? That would also trigger it.

DurankGts commented 3 years ago

no I just call this method in the iphone side when goes to background. I just call this method in android side to delete this location by error when I stop my Service class Gps

lm.setBackgroundLocationListener(null);

@Override public void appStart() { String methodName = "appStart"; Log.i(TAG, "------------"); Log.i(TAG, "appStart()"); Log.i(TAG, "------------"); try { int os = getOsInt(); switch (os) { case OsInt.SIMULATOR: break; case OsInt.ANDROID: break; case OsInt.IPHONE: Log.i(TAG, methodName+"()->setBackgroundLocationListener:null"); LocationManager.getLocationManager().setBackgroundLocationListener(null); break; }

    } catch (Exception e) {
        Log.e(TAG, methodName + "()->E0:" + LogCodes.ECatch.E0 + e.toString());
        logEvent = clientCode + "," + currEventIdOnServer + "," + eventType + "," + LogCodes.ECodes.E_DB + "," + LogCodes.ECatch.E0 + e.toString() + "," + TAG + "," + methodName;
        ForceSOS.insertLogEvent(logEvent);
    }
}

private void appStop(){ String methodName = "appStop"; try { Log.i(TAG, "--------------------------------------------"); Log.i(TAG, "startBackgroundLocation()"); Log.i(TAG, "--------------------------------------------"); boolean isMinimized = Display.getInstance().isMinimized(); Log.i(TAG, methodName+"()->isMinimized :" + isMinimized);

        int os = getOsInt();
        switch (os) {
            case OsInt.SIMULATOR:
                break;
            case OsInt.ANDROID:
                break;
            case OsInt.IPHONE:
                Log.i(TAG, methodName+"()->setBackgroundLocationListener:SvcGpsBgn.class");
                LocationManager.getLocationManager().setBackgroundLocationListener(SvcGpsBgn.class);
                break;
        }

    } catch (Exception e) {
        Log.e(TAG, methodName + "()->" + LogCodes.ECatch.E0 + e.toString());
    }

}
shannah commented 3 years ago

That is what is triggering it. The build server detects the use of that API via static class analysis.

You could set that display property to "false" before calling any location APIs. That would prevent this permission. request.

DurankGts commented 3 years ago

don't worry I fixed this issue when I check your built code in the init() method to avoid your hard code dialog

public static void requiredBackgroundLocationPermissionStatus(boolean status){ Display.getInstance().setProperty("android.requiresBackgroundLocationPermissionForAPI29", ""+status); }

DurankGts commented 3 years ago

But I think that you must implement a way that the developer it show its own custom dialog. By Example implementing a call bak in the codenameone side like android the receive a call back in the activity. To avoid your hard code native dialog. I implemented my own Native interface to request permission in android side an ios side.