From 0a2e4a674f3d2f70e5f485b26151b089e1f5f82a Mon Sep 17 00:00:00 2001 From: Monte Goulding Date: Fri, 20 Jul 2018 10:12:28 +1000 Subject: [PATCH 01/19] [[ Bug 21434 ]] Force bitmap view visible for visual effects This patch fixes a regresion from a previous patch that changed the behavior of the `showBitmapView` method to only set the visibility of `m_bitmap_view` to visible if the OpenGL view is not visible. This interfered with visual effects as they uset the bitmap view rather than OpenGL view. --- docs/notes/bugfix-21434.md | 1 + engine/src/java/com/runrev/android/Engine.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 docs/notes/bugfix-21434.md diff --git a/docs/notes/bugfix-21434.md b/docs/notes/bugfix-21434.md new file mode 100644 index 00000000000..e41919685a4 --- /dev/null +++ b/docs/notes/bugfix-21434.md @@ -0,0 +1 @@ +# Fix visual effects not working when acceleratedRendering is true on Android \ No newline at end of file diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index bc22d9fe676..af01d4297d1 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -2251,7 +2251,8 @@ public void run() { public void showBitmapView() { - ensureBitmapViewVisibility(); + // force visible for visual effects + m_bitmap_view.setVisibility(View.VISIBLE); } //////////////////////////////////////////////////////////////////////////////// From 5264d8696a397db869c2f368a02d90e8a65714e0 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Thu, 26 Jul 2018 12:15:57 +0100 Subject: [PATCH 02/19] [Android API 23+][mobilePickPhoto] Request for camera permissions on runtime --- .../src/java/com/runrev/android/Engine.java | 68 +++++++++++++++---- .../com/runrev/android/LiveCodeActivity.java | 10 +++ 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index af01d4297d1..49728804e9b 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -53,6 +53,8 @@ import android.provider.MediaStore.Images.Media; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.Manifest; +import java.lang.Object; import java.net.*; import java.io.*; @@ -1859,22 +1861,62 @@ else if (t_caminfo.facing == CameraCompat.CameraInfo.CAMERA_FACING_FRONT) return new String(t_directions); } + private String m_source; + public static final int CAMERA_PERMISSION_REQUEST_CODE = 1; public void showPhotoPicker(String p_source, int p_width, int p_height) { - m_photo_width = p_width; - m_photo_height = p_height; - - if (p_source.equals("camera")) - showCamera(); - else if (p_source.equals("album")) - showLibrary(); - else if (p_source.equals("library")) - showLibrary(); - else - { - doPhotoPickerError("source not available"); - } + m_photo_width = p_width; + m_photo_height = p_height; + m_source = p_source; + + // Camera permission not granted, so ask for it + if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) + { + Activity t_activity = (LiveCodeActivity)getContext(); + t_activity.requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); + } + // Camera permission already granted, or we are in a device running Android API < 23 + else + { + onCameraPermissionGranted(m_source); + } } + + private void onCameraPermissionGranted(String p_source) + { + if (p_source.equals("camera")) + showCamera(); + else if (p_source.equals("album")) + showLibrary(); + else if (p_source.equals("library")) + showLibrary(); + else + { + doPhotoPickerError("source not available"); + } + } + + private void onCameraRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) + { + if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) + { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) + { + onCameraPermissionGranted(m_source); + } + else + { + doPhotoPickerError("Permission denied. You can change this in the Settings app"); + } + } + } + + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + onCameraRequestPermissionResult(requestCode, permissions, grantResults); + } + public void showCamera() { diff --git a/engine/src/java/com/runrev/android/LiveCodeActivity.java b/engine/src/java/com/runrev/android/LiveCodeActivity.java index f0ba460584a..49279915e72 100644 --- a/engine/src/java/com/runrev/android/LiveCodeActivity.java +++ b/engine/src/java/com/runrev/android/LiveCodeActivity.java @@ -23,6 +23,7 @@ import android.content.res.*; import android.widget.*; import android.util.*; +import android.content.pm.PackageManager; // This is the main activity exported by the application. This is // split into two parts, a customizable sub-class that gets dynamically @@ -198,4 +199,13 @@ protected void onActivityResult (int requestCode, int resultCode, Intent data) { s_main_view.onActivityResult(requestCode, resultCode, data); } + + // Callback sent when the app requests permissions on runtime (Android API 23+) + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + s_main_view.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } From 9a8c15bfe0d774b9bb6d79e53ef197ebe3d893f1 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Thu, 26 Jul 2018 22:23:11 +0100 Subject: [PATCH 03/19] [Android API 23+] Request for location permissions on runtime --- .../src/java/com/runrev/android/Engine.java | 20 +++++++++++++-- .../java/com/runrev/android/SensorModule.java | 25 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index 49728804e9b..3629eab7192 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -1863,6 +1863,7 @@ else if (t_caminfo.facing == CameraCompat.CameraInfo.CAMERA_FACING_FRONT) private String m_source; public static final int CAMERA_PERMISSION_REQUEST_CODE = 1; + public static final int LOCATION_PERMISSION_REQUEST_CODE = 3; public void showPhotoPicker(String p_source, int p_width, int p_height) { m_photo_width = p_width; @@ -1912,12 +1913,27 @@ private void onCameraRequestPermissionResult(int requestCode, String[] permissio } } + private void onLocationRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) + { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) + { + m_sensor_module.createLocationTracker(); + } + else + { + Log.e(TAG,"Permission denied. You can change this in the Settings app"); + } + } + + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - onCameraRequestPermissionResult(requestCode, permissions, grantResults); + if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) + onCameraRequestPermissionResult(requestCode, permissions, grantResults); + else if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) + onLocationRequestPermissionResult(requestCode, permissions, grantResults); } - public void showCamera() { // 2012-01-18-IM temp file may be created in app cache folder, in which case diff --git a/engine/src/java/com/runrev/android/SensorModule.java b/engine/src/java/com/runrev/android/SensorModule.java index 29fabd62746..40ef8f493a5 100644 --- a/engine/src/java/com/runrev/android/SensorModule.java +++ b/engine/src/java/com/runrev/android/SensorModule.java @@ -19,6 +19,9 @@ import android.content.*; import android.os.*; import android.util.*; +import android.Manifest; +import java.lang.Object.*; +import android.app.Activity; import java.util.*; @@ -44,6 +47,8 @@ abstract class Tracker public static final int COARSE_TRACKING = 1; public static final int FINE_TRACKING = 2; + public static final int LOCATION_PERMISSION_REQUEST_CODE = 3; + protected boolean m_paused; protected int m_tracking_requested; protected int m_tracking_internal; @@ -230,6 +235,21 @@ class LocationTracker extends Tracker LocationListener m_gps_location_listener; public LocationTracker() + { + if (Build.VERSION.SDK_INT >= 23 && ((m_engine.getContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + != m_engine.getContext().getPackageManager().PERMISSION_GRANTED || m_engine.getContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) + != m_engine.getContext().getPackageManager().PERMISSION_GRANTED))) + { + Activity t_activity = (LiveCodeActivity)m_engine.getContext(); + t_activity.requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); + } + else + { + createLocationTracker(); + } + } + + private void createLocationTracker() { // Get the number of seconds since the device was booted double t_seconds_since_boot; @@ -689,4 +709,9 @@ public void finish() m_accel_tracker.stopTracking(); m_location_tracker.stopTracking(); } + + public void createLocationTracker() + { + m_location_tracker.createLocationTracker(); + } } From 0b857f1baa6ce695a2abf5f1cd5ce11966a4aa7a Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Thu, 26 Jul 2018 22:27:07 +0100 Subject: [PATCH 04/19] Removed unnecessary comparison --- engine/src/java/com/runrev/android/Engine.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index 3629eab7192..3c278ad3fb6 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -1900,16 +1900,13 @@ else if (p_source.equals("library")) private void onCameraRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) { - if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) - { - onCameraPermissionGranted(m_source); - } - else - { - doPhotoPickerError("Permission denied. You can change this in the Settings app"); - } + onCameraPermissionGranted(m_source); + } + else + { + doPhotoPickerError("Permission denied. You can change this in the Settings app"); } } @@ -1925,7 +1922,6 @@ private void onLocationRequestPermissionResult(int requestCode, String[] permiss } } - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) From 3224c7d09c47a1dd8c08951301e023a89e002d07 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Fri, 27 Jul 2018 18:21:56 +0100 Subject: [PATCH 05/19] [Runtime permissions] Created API that checks for permission --- .../src/java/com/runrev/android/Engine.java | 91 +++++++++++-------- .../java/com/runrev/android/SensorModule.java | 20 ---- engine/src/mblandroidcamera.cpp | 6 +- engine/src/mblandroidcontact.cpp | 20 ++++ engine/src/mblandroiddc.cpp | 29 ++++++ engine/src/mblandroidfs.cpp | 8 ++ engine/src/mblandroidsensor.cpp | 10 +- 7 files changed, 123 insertions(+), 61 deletions(-) diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index 3c278ad3fb6..5b779ac7f75 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -1153,6 +1153,11 @@ public void onListPickerDone(int p_index, boolean p_done) if (m_wake_on_event) doProcess(false); } + + public void onAskPermissionDone(boolean p_granted) + { + doAskPermissionDone(p_granted); + } //////////////////////////////////////////////////////////////////////////////// @@ -1861,31 +1866,49 @@ else if (t_caminfo.facing == CameraCompat.CameraInfo.CAMERA_FACING_FRONT) return new String(t_directions); } - private String m_source; public static final int CAMERA_PERMISSION_REQUEST_CODE = 1; - public static final int LOCATION_PERMISSION_REQUEST_CODE = 3; - public void showPhotoPicker(String p_source, int p_width, int p_height) - { - m_photo_width = p_width; - m_photo_height = p_height; - m_source = p_source; - - // Camera permission not granted, so ask for it - if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(Manifest.permission.CAMERA) + public static final int COARSE_LOCATION_PERMISSION_REQUEST_CODE = 2; + public static final int FINE_LOCATION_PERMISSION_REQUEST_CODE = 3; + public static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 4; + public static final int WRITE_CONTACTS_PERMISSION_REQUEST_CODE = 5; + public static final int WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6; + + public boolean askPermission(String p_permission) + { + if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(p_permission) != PackageManager.PERMISSION_GRANTED) { Activity t_activity = (LiveCodeActivity)getContext(); - t_activity.requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); + t_activity.requestPermissions(new String[]{p_permission}, mapPermissionToRequestCode(p_permission)); } - // Camera permission already granted, or we are in a device running Android API < 23 else - { - onCameraPermissionGranted(m_source); - } - } + onAskPermissionDone(true); + return true; + } - private void onCameraPermissionGranted(String p_source) + private int mapPermissionToRequestCode(String p_permission) + { + if (p_permission.equals("android.permission.CAMERA")) + return CAMERA_PERMISSION_REQUEST_CODE; + else if (p_permission.equals("android.permission.ACCESS_COARSE_LOCATION")) + return COARSE_LOCATION_PERMISSION_REQUEST_CODE; + else if (p_permission.equals("android.permission.ACCESS_FINE_LOCATION")) + return FINE_LOCATION_PERMISSION_REQUEST_CODE; + else if (p_permission.equals("android.permission.READ_CONTACTS")) + return READ_CONTACTS_PERMISSION_REQUEST_CODE; + else if (p_permission.equals("android.permission.WRITE_CONTACTS")) + return WRITE_CONTACTS_PERMISSION_REQUEST_CODE; + else if (p_permission.equals("android.permission.WRITE_EXTERNAL_STORAGE")) + return WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE; + else + return -1; + } + + public void showPhotoPicker(String p_source, int p_width, int p_height) { + m_photo_width = p_width; + m_photo_height = p_height; + if (p_source.equals("camera")) showCamera(); else if (p_source.equals("album")) @@ -1896,38 +1919,27 @@ else if (p_source.equals("library")) { doPhotoPickerError("source not available"); } + } - private void onCameraRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) + private void onCameraPermissionGranted(String p_source) { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) - { - onCameraPermissionGranted(m_source); - } + if (p_source.equals("camera")) + showCamera(); + else if (p_source.equals("album")) + showLibrary(); + else if (p_source.equals("library")) + showLibrary(); else { - doPhotoPickerError("Permission denied. You can change this in the Settings app"); + doPhotoPickerError("source not available"); } } - private void onLocationRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) - { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) - { - m_sensor_module.createLocationTracker(); - } - else - { - Log.e(TAG,"Permission denied. You can change this in the Settings app"); - } - } - + // sent by the callback public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) - onCameraRequestPermissionResult(requestCode, permissions, grantResults); - else if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) - onLocationRequestPermissionResult(requestCode, permissions, grantResults); + onAskPermissionDone(grantResults[0] == PackageManager.PERMISSION_GRANTED); } public void showCamera() @@ -3791,6 +3803,7 @@ public static native void doHeadingChanged(double p_heading, double p_magnetic_h public static native void doDatePickerDone(int year, int month, int day, boolean done); public static native void doTimePickerDone(int hour, int minute, boolean done); public static native void doListPickerDone(int index, boolean done); + public static native void doAskPermissionDone(boolean granted); public static native void doMovieStopped(); public static native void doMovieTouched(); diff --git a/engine/src/java/com/runrev/android/SensorModule.java b/engine/src/java/com/runrev/android/SensorModule.java index 40ef8f493a5..fc6ffb82cbc 100644 --- a/engine/src/java/com/runrev/android/SensorModule.java +++ b/engine/src/java/com/runrev/android/SensorModule.java @@ -235,21 +235,6 @@ class LocationTracker extends Tracker LocationListener m_gps_location_listener; public LocationTracker() - { - if (Build.VERSION.SDK_INT >= 23 && ((m_engine.getContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) - != m_engine.getContext().getPackageManager().PERMISSION_GRANTED || m_engine.getContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) - != m_engine.getContext().getPackageManager().PERMISSION_GRANTED))) - { - Activity t_activity = (LiveCodeActivity)m_engine.getContext(); - t_activity.requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); - } - else - { - createLocationTracker(); - } - } - - private void createLocationTracker() { // Get the number of seconds since the device was booted double t_seconds_since_boot; @@ -709,9 +694,4 @@ public void finish() m_accel_tracker.stopTracking(); m_location_tracker.stopTracking(); } - - public void createLocationTracker() - { - m_location_tracker.createLocationTracker(); - } } diff --git a/engine/src/mblandroidcamera.cpp b/engine/src/mblandroidcamera.cpp index 8b1e8421b6e..74a36c0b76b 100644 --- a/engine/src/mblandroidcamera.cpp +++ b/engine/src/mblandroidcamera.cpp @@ -80,9 +80,13 @@ MCCamerasFeaturesType MCSystemGetAllCameraFeatures() return t_features; } +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); bool MCAndroidPickPhoto(const char *p_source, int32_t p_max_width, int32_t p_max_height) { - MCAndroidEngineCall("showPhotoPicker", "vsii", nil, p_source, p_max_width, p_max_height); + if (!MCAndroidCheckRuntimePermission(MCSTR("android.permission.CAMERA"))) + return false; + + MCAndroidEngineCall("showPhotoPicker", "vsii", nil, p_source, p_max_width, p_max_height); // SN-2014-09-03: [[ Bug 13329 ]] MCAndroidPickPhoto's return value is ignored in 6.x, // but not in 7.0 - whence the failure in mobilePickPhoto return true; diff --git a/engine/src/mblandroidcontact.cpp b/engine/src/mblandroidcontact.cpp index cae48765ea6..70eb74cd9a9 100644 --- a/engine/src/mblandroidcontact.cpp +++ b/engine/src/mblandroidcontact.cpp @@ -58,9 +58,11 @@ static MCAndroidContactStatus s_contact_status = kMCAndroidContactWaiting; static int32_t s_contact_selected = 0; static MCString s_contacts_selected = ""; +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); bool MCSystemPickContact(int32_t& r_result) { MCLog("MCSystemPickContact"); + MCAndroidEngineRemoteCall("pickContact", "i", &r_result); s_contact_status = kMCAndroidContactWaiting; while (s_contact_status == kMCAndroidContactWaiting) @@ -156,6 +158,9 @@ bool MCSystemUpdateContact(MCArrayRef p_contact, MCStringRef p_title, MCStringRef p_message, MCStringRef p_alternate_name, int32_t &r_result) { + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_CONTACTS")))) + return false; + MCLog("MCSystemUpdateContact"); bool t_success = true; @@ -192,6 +197,10 @@ void MCAndroidUpdateContactCanceled(int32_t p_contact_id) bool MCSystemGetContactData(int32_t p_contact_id, MCArrayRef &r_contact_data) { MCLog("MCSystemGetContactData: %d", p_contact_id); + + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.READ_CONTACTS")))) + return false; + jobject t_jmap = nil; MCAndroidEngineRemoteCall("getContactData", "mi", &t_jmap, p_contact_id); MCLog("contact map: %p", t_jmap); @@ -209,6 +218,10 @@ bool MCSystemGetContactData(int32_t p_contact_id, MCArrayRef &r_contact_data) bool MCSystemRemoveContact(int32_t p_contact_id) { MCLog("MCSystemRemoveContact: %d", p_contact_id); + + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_CONTACTS")))) + return false; + MCAndroidEngineRemoteCall("removeContact", "vi", nil, p_contact_id); return true; } @@ -216,6 +229,9 @@ bool MCSystemRemoveContact(int32_t p_contact_id) bool MCSystemAddContact(MCArrayRef p_contact, int32_t &r_result) { MCLog("MCSystemAddContact"); + + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_CONTACTS")))) + return false; bool t_success = true; jobject t_map = nil; @@ -229,8 +245,12 @@ bool MCSystemAddContact(MCArrayRef p_contact, int32_t &r_result) return false; } + bool MCSystemFindContact(MCStringRef p_contact_name, MCStringRef& r_result) { + if (!(MCAndroidCheckRuntimePermission(MCSTR("android.permission.READ_CONTACTS")))) + return false; + MCAndroidEngineRemoteCall("findContact", "vx", nil, p_contact_name); return MCStringCreateWithCString(s_contacts_selected . getstring(), r_result); } diff --git a/engine/src/mblandroiddc.cpp b/engine/src/mblandroiddc.cpp index 4ef4f42b4d9..377ff388d83 100644 --- a/engine/src/mblandroiddc.cpp +++ b/engine/src/mblandroiddc.cpp @@ -2817,6 +2817,35 @@ void MCAndroidDisableOpenGLMode(void) //////////////////////////////////////////////////////////////////////////////// +static bool s_in_permission_dialog = false; +static bool s_permission_granted = false; +bool MCAndroidCheckRuntimePermission(MCStringRef p_permission) +{ + bool t_result; + s_in_permission_dialog = true; + MCAndroidEngineRemoteCall("askPermission", "bx", &t_result, p_permission); + + while (s_in_permission_dialog) + MCscreen -> wait(60.0, True, True); + + return s_permission_granted; +} + +extern "C" JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv *env, jobject object, bool granted) __attribute__((visibility("default"))); +JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv *env, jobject object, bool granted) +{ + s_in_permission_dialog = false; + s_permission_granted = granted; + MCAndroidBreakWait(); +} + + + + + + +///////////////////////////////////////////////////////////////////////////////// + bool android_run_on_main_thread(void *p_callback, void *p_callback_state, int p_options); typedef void (*MCExternalThreadOptionalCallback)(void *state); diff --git a/engine/src/mblandroidfs.cpp b/engine/src/mblandroidfs.cpp index 57f3ca7e306..e451d356859 100644 --- a/engine/src/mblandroidfs.cpp +++ b/engine/src/mblandroidfs.cpp @@ -415,8 +415,16 @@ bool MCAndroidSystem::GetTemporaryFileName(MCStringRef &r_tmp_name) return MCStringCreateWithCString(tmpnam(NULL), r_tmp_name); } +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); Boolean MCAndroidSystem::GetStandardFolder(MCNameRef p_folder, MCStringRef &r_folder) { + // accessing "external documents", "external cache" etc requires Write External Storage permission + if (MCStringBeginsWith(MCNameGetString(p_folder), MCSTR("external"), kMCStringOptionCompareCaseless) && !MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_EXTERNAL_STORAGE"))) + { + r_folder = MCValueRetain(kMCEmptyString); + return False; + } + // SN-2015-04-16: [[ Bug 14295 ]] The resources folder on Mobile is the same // as the engine folder. if (MCNameIsEqualToCaseless(p_folder, MCN_engine) diff --git a/engine/src/mblandroidsensor.cpp b/engine/src/mblandroidsensor.cpp index 4851f473e2d..be8f0acf323 100644 --- a/engine/src/mblandroidsensor.cpp +++ b/engine/src/mblandroidsensor.cpp @@ -58,9 +58,17 @@ void MCSystemSensorFinalize(void) } //////////////////////////////////////////////////////////////////////////////// +extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); bool MCSystemGetSensorAvailable(MCSensorType p_sensor, bool& r_available) -{ +{ + if (p_sensor == kMCSensorTypeLocation) + { + bool t_success = MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_COARSE_LOCATION")) && MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_FINE_LOCATION")); + if (!t_success) + return false; + } + MCAndroidEngineRemoteCall("isSensorAvailable", "bi", &r_available, (int32_t)p_sensor); return true; } From 912d98e5965412fffd28618d78b691242ae0b7bf Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Fri, 27 Jul 2018 18:30:19 +0100 Subject: [PATCH 06/19] Removed unused code --- engine/src/java/com/runrev/android/Engine.java | 16 ---------------- .../java/com/runrev/android/SensorModule.java | 6 ------ engine/src/mblandroiddc.cpp | 5 ----- 3 files changed, 27 deletions(-) diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index 5b779ac7f75..ca6596d80c1 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -53,8 +53,6 @@ import android.provider.MediaStore.Images.Media; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.Manifest; -import java.lang.Object; import java.net.*; import java.io.*; @@ -1922,20 +1920,6 @@ else if (p_source.equals("library")) } - private void onCameraPermissionGranted(String p_source) - { - if (p_source.equals("camera")) - showCamera(); - else if (p_source.equals("album")) - showLibrary(); - else if (p_source.equals("library")) - showLibrary(); - else - { - doPhotoPickerError("source not available"); - } - } - // sent by the callback public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { diff --git a/engine/src/java/com/runrev/android/SensorModule.java b/engine/src/java/com/runrev/android/SensorModule.java index fc6ffb82cbc..58b0f77a68c 100644 --- a/engine/src/java/com/runrev/android/SensorModule.java +++ b/engine/src/java/com/runrev/android/SensorModule.java @@ -19,10 +19,6 @@ import android.content.*; import android.os.*; import android.util.*; -import android.Manifest; -import java.lang.Object.*; -import android.app.Activity; - import java.util.*; import android.hardware.*; @@ -47,8 +43,6 @@ abstract class Tracker public static final int COARSE_TRACKING = 1; public static final int FINE_TRACKING = 2; - public static final int LOCATION_PERMISSION_REQUEST_CODE = 3; - protected boolean m_paused; protected int m_tracking_requested; protected int m_tracking_internal; diff --git a/engine/src/mblandroiddc.cpp b/engine/src/mblandroiddc.cpp index 377ff388d83..f5ff3cd2181 100644 --- a/engine/src/mblandroiddc.cpp +++ b/engine/src/mblandroiddc.cpp @@ -2839,11 +2839,6 @@ JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv MCAndroidBreakWait(); } - - - - - ///////////////////////////////////////////////////////////////////////////////// bool android_run_on_main_thread(void *p_callback, void *p_callback_state, int p_options); From 0509d62f90b120bd1181619a10d554177a023480 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Sat, 28 Jul 2018 07:55:06 +0100 Subject: [PATCH 07/19] Used blocking wait --- .../src/java/com/runrev/android/Engine.java | 28 ++----------------- engine/src/mblandroiddc.cpp | 2 +- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index ca6596d80c1..280f3399568 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -1864,44 +1864,20 @@ else if (t_caminfo.facing == CameraCompat.CameraInfo.CAMERA_FACING_FRONT) return new String(t_directions); } - public static final int CAMERA_PERMISSION_REQUEST_CODE = 1; - public static final int COARSE_LOCATION_PERMISSION_REQUEST_CODE = 2; - public static final int FINE_LOCATION_PERMISSION_REQUEST_CODE = 3; - public static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 4; - public static final int WRITE_CONTACTS_PERMISSION_REQUEST_CODE = 5; - public static final int WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6; - + public static final int PERMISSION_REQUEST_CODE = 1; public boolean askPermission(String p_permission) { if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(p_permission) != PackageManager.PERMISSION_GRANTED) { Activity t_activity = (LiveCodeActivity)getContext(); - t_activity.requestPermissions(new String[]{p_permission}, mapPermissionToRequestCode(p_permission)); + t_activity.requestPermissions(new String[]{p_permission}, PERMISSION_REQUEST_CODE); } else onAskPermissionDone(true); return true; } - private int mapPermissionToRequestCode(String p_permission) - { - if (p_permission.equals("android.permission.CAMERA")) - return CAMERA_PERMISSION_REQUEST_CODE; - else if (p_permission.equals("android.permission.ACCESS_COARSE_LOCATION")) - return COARSE_LOCATION_PERMISSION_REQUEST_CODE; - else if (p_permission.equals("android.permission.ACCESS_FINE_LOCATION")) - return FINE_LOCATION_PERMISSION_REQUEST_CODE; - else if (p_permission.equals("android.permission.READ_CONTACTS")) - return READ_CONTACTS_PERMISSION_REQUEST_CODE; - else if (p_permission.equals("android.permission.WRITE_CONTACTS")) - return WRITE_CONTACTS_PERMISSION_REQUEST_CODE; - else if (p_permission.equals("android.permission.WRITE_EXTERNAL_STORAGE")) - return WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE; - else - return -1; - } - public void showPhotoPicker(String p_source, int p_width, int p_height) { m_photo_width = p_width; diff --git a/engine/src/mblandroiddc.cpp b/engine/src/mblandroiddc.cpp index f5ff3cd2181..81a38421a7a 100644 --- a/engine/src/mblandroiddc.cpp +++ b/engine/src/mblandroiddc.cpp @@ -2826,7 +2826,7 @@ bool MCAndroidCheckRuntimePermission(MCStringRef p_permission) MCAndroidEngineRemoteCall("askPermission", "bx", &t_result, p_permission); while (s_in_permission_dialog) - MCscreen -> wait(60.0, True, True); + MCscreen -> wait(60.0, False, True); return s_permission_granted; } From 5c9490e443d5d446023c877d267f64c25ad62c57 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Sat, 28 Jul 2018 09:04:54 +0100 Subject: [PATCH 08/19] Implemented "androidRequestPermission()" function --- engine/src/exec-misc.cpp | 9 +++++++++ engine/src/exec.h | 1 + engine/src/mblandroidmisc.cpp | 8 ++++++++ engine/src/mblandroidutil.h | 1 + engine/src/mblhandlers.cpp | 27 +++++++++++++++++++++++++++ engine/src/mblsyntax.h | 1 + 6 files changed, 47 insertions(+) diff --git a/engine/src/exec-misc.cpp b/engine/src/exec-misc.cpp index f5bc5eebf22..e5a92186558 100644 --- a/engine/src/exec-misc.cpp +++ b/engine/src/exec-misc.cpp @@ -481,6 +481,15 @@ void MCMiscGetDoNotBackupFile(MCExecContext& ctxt, MCStringRef p_path, bool& r_n ctxt.Throw(); } +void MCMiscExecRequestPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_granted) +{ + if (MCSystemRequestPermission(p_permission, r_granted)) + return; + + ctxt.Throw(); +} + + void MCMiscSetDoNotBackupFile(MCExecContext& ctxt, MCStringRef p_path, bool p_no_backup) { if (MCSystemFileSetDoNotBackup(p_path, p_no_backup)) diff --git a/engine/src/exec.h b/engine/src/exec.h index 81f22a8fe02..6df71a37ed0 100644 --- a/engine/src/exec.h +++ b/engine/src/exec.h @@ -4043,6 +4043,7 @@ void MCMiscExecLibUrlDownloadToFile(MCExecContext& ctxt, MCStringRef p_url, MCSt void MCMiscExecLibUrlSetSSLVerification(MCExecContext& ctxt, bool p_enabled); void MCMiscGetBuildInfo(MCExecContext& ctxt, MCStringRef p_key, MCStringRef& r_value); +void MCMiscExecRequestPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_granted); void MCMiscExecEnableRemoteControl(MCExecContext& ctxt); void MCMiscExecDisableRemoteControl(MCExecContext& ctxt); diff --git a/engine/src/mblandroidmisc.cpp b/engine/src/mblandroidmisc.cpp index 5bc82aef127..49fd9a28b4e 100644 --- a/engine/src/mblandroidmisc.cpp +++ b/engine/src/mblandroidmisc.cpp @@ -802,6 +802,14 @@ bool MCSystemBuildInfo(MCStringRef p_key, MCStringRef& r_value) //////////////////////////////////////////////////////////////////////////////// +bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted) +{ + r_granted = MCAndroidCheckRuntimePermission(p_permission); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// diff --git a/engine/src/mblandroidutil.h b/engine/src/mblandroidutil.h index 42fd778739e..874574ed256 100644 --- a/engine/src/mblandroidutil.h +++ b/engine/src/mblandroidutil.h @@ -63,6 +63,7 @@ void MCAndroidObjectCall(jobject p_object, const char *p_method, const char *p_s void MCAndroidObjectRemoteCall(jobject p_object, const char *p_method, const char *p_signature, void *p_return_value, ...); bool MCAndroidGetBuildInfo(MCStringRef t_key, MCStringRef &r_value); +bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); typedef struct _android_device_configuration { diff --git a/engine/src/mblhandlers.cpp b/engine/src/mblhandlers.cpp index ab260dc5817..33aea63342c 100644 --- a/engine/src/mblhandlers.cpp +++ b/engine/src/mblhandlers.cpp @@ -3377,6 +3377,32 @@ Exec_stat MCHandleBuildInfo(void *context, MCParameter *p_parameters) return ES_ERROR; } +Exec_stat MCHandleRequestPermission(void *context, MCParameter *p_parameters) +{ + MCExecContext ctxt(nil, nil, nil); + + MCAutoStringRef t_permission; + bool t_success, t_granted; + + t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + + if (t_success) + MCMiscExecRequestPermission(ctxt, *t_permission, t_granted); + + if (!ctxt . HasError()) + { + if (t_granted) + ctxt.SetTheResultToValue(kMCTrueString); + else + ctxt.SetTheResultToValue(kMCFalseString); + + return ES_NORMAL; + } + + ctxt.SetTheResultToEmpty(); + return ES_ERROR; +} + ////////////////////////////////////////////////////////////////////////////////////// static MCMediaType MCMediaTypeFromString(MCStringRef p_string) @@ -4529,6 +4555,7 @@ static const MCPlatformMessageSpec s_platform_messages[] = {false, "mobileLocationAuthorizationStatus", MCHandleLocationAuthorizationStatus, nil}, {false, "mobileBuildInfo", MCHandleBuildInfo, nil}, + {false, "androidRequestPermission", MCHandleRequestPermission, nil}, {false, "mobileCanMakePurchase", MCHandleCanMakePurchase, nil}, {false, "mobileEnablePurchaseUpdates", MCHandleEnablePurchaseUpdates, nil}, diff --git a/engine/src/mblsyntax.h b/engine/src/mblsyntax.h index 9ccfaa68249..d668a186cef 100644 --- a/engine/src/mblsyntax.h +++ b/engine/src/mblsyntax.h @@ -466,6 +466,7 @@ bool MCSystemFileSetDataProtection(MCStringRef p_path, MCStringRef p_protection_ bool MCSystemFileGetDataProtection(MCStringRef p_path, MCStringRef& r_protection_string); bool MCSystemBuildInfo(MCStringRef p_key, MCStringRef& r_value); +bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted); bool MCSystemEnableRemoteControl(); bool MCSystemDisableRemoteControl(); From e5a704e29c5a248afa0ce290ad09288db9277590 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Sun, 29 Jul 2018 17:44:56 +0100 Subject: [PATCH 09/19] Added feature note --- .../feature-android_runtime_permissions.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/notes/feature-android_runtime_permissions.md diff --git a/docs/notes/feature-android_runtime_permissions.md b/docs/notes/feature-android_runtime_permissions.md new file mode 100644 index 00000000000..85d603f5ca8 --- /dev/null +++ b/docs/notes/feature-android_runtime_permissions.md @@ -0,0 +1,26 @@ +# Android 6.0 runtime permissions + +Android 6.0 (API 23) Marshmallow introduced a new permissions model +that lets apps request permissions from the user at runtime, rather +than prior to installation. Apps built with LC 9.0.1 do support this +new permissions model, and request permissions automatically when the +app actually requires the services or data protected by the services. + +For example, if the app calls `mobilePickPhoto "camera"`, a dialog will +be shown to the user asking for permission to access the device camera. + +If the user does not grant permission, the call will fail. Moreover, the +app can use the function `androidRequestPermission(permissionName)` to +check if the permission for `permissionName` has been granted. + +Notes: + +- You have to make sure that you check the required permissions for your +app in the standalone settings. +- Apps that run on devices running Android 6+ will work with the new +permissions model. +- Apps that run on older devices (less than Android 6) will continue to +work with the old permissions model. +- If the user does not grant a permission when the dialog appears for the +first time, they can change this preference from the Settings app. + From 62f16516000d3b4a740d1a10f9601aabd6df3106 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Sun, 29 Jul 2018 19:15:13 +0100 Subject: [PATCH 10/19] [androidRequestPermission] Added dictionary entry --- .../function/androidRequestPermission.lcdoc | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/dictionary/function/androidRequestPermission.lcdoc diff --git a/docs/dictionary/function/androidRequestPermission.lcdoc b/docs/dictionary/function/androidRequestPermission.lcdoc new file mode 100644 index 00000000000..bef2e45423f --- /dev/null +++ b/docs/dictionary/function/androidRequestPermission.lcdoc @@ -0,0 +1,44 @@ +Name: androidRequestPermission + +Type: function + +Syntax: androidRequestPermission() + +Summary: +Returns if permission has been granted by the user. If the user has not been asked before, a dialog is displayed, +showing a permission request for . + +Introduced: 9.0.1 + +OS: mobile + +Platforms: android + +Example: +local tCameraPermissionGranted +put androidRequestPermission("android.permission.CAMERA") into tCameraPermissionGranted +if not tCameraPermissionGranted then + answer "This app is not permitted to access the device camera. You can change this \ + in the Settings app." +end if + + +Parameters: +permissionName (enum): +The name of the permission to request. + +- "android.permission.CAMERA": permission to access the device camera. +- "android.permission.ACCESS_COARSE_LOCATION": permission to access the device coarse location. +- "android.permission.ACCESS_FINE_LOCATION": permission to access the device fine location. +- "android.permission.WRITE_CONTACTS": permission to write date to the device contacts. +- "android.permission.READ_CONTACTS": permission to read data from the device contacts. +- "android.permission.WRITE_EXTERNAL_STORAGE": permission to write data to the device external storage. + +Returns(boolean): +True if permission has been granted, false otherwise. + + +Description: +Use the function to request permission for from the user, or to find out if permission has been granted. + +>*Note:* Permission names are case sensitive. From 4c34eaf1dca12792e5cc338fb1a634ffcc33317c Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Sun, 29 Jul 2018 23:38:55 +0100 Subject: [PATCH 11/19] Fixed a couple of issues on docs and with long lines --- .../function/androidRequestPermission.lcdoc | 8 ++++---- engine/src/mblandroidfs.cpp | 11 ++++++----- engine/src/mblandroidsensor.cpp | 3 ++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/dictionary/function/androidRequestPermission.lcdoc b/docs/dictionary/function/androidRequestPermission.lcdoc index bef2e45423f..77d1ec486ae 100644 --- a/docs/dictionary/function/androidRequestPermission.lcdoc +++ b/docs/dictionary/function/androidRequestPermission.lcdoc @@ -10,16 +10,16 @@ showing a permission request for . Introduced: 9.0.1 -OS: mobile +OS: android -Platforms: android +Platforms: mobile Example: local tCameraPermissionGranted put androidRequestPermission("android.permission.CAMERA") into tCameraPermissionGranted if not tCameraPermissionGranted then - answer "This app is not permitted to access the device camera. You can change this \ - in the Settings app." + answer "This app is not permitted to access the device camera. You can change this" && \ + "in the Settings app." end if diff --git a/engine/src/mblandroidfs.cpp b/engine/src/mblandroidfs.cpp index e451d356859..1b18e3ffb90 100644 --- a/engine/src/mblandroidfs.cpp +++ b/engine/src/mblandroidfs.cpp @@ -419,7 +419,8 @@ extern bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); Boolean MCAndroidSystem::GetStandardFolder(MCNameRef p_folder, MCStringRef &r_folder) { // accessing "external documents", "external cache" etc requires Write External Storage permission - if (MCStringBeginsWith(MCNameGetString(p_folder), MCSTR("external"), kMCStringOptionCompareCaseless) && !MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_EXTERNAL_STORAGE"))) + if (MCStringBeginsWith(MCNameGetString(p_folder), MCSTR("external"), kMCStringOptionCompareCaseless) && \ + !MCAndroidCheckRuntimePermission(MCSTR("android.permission.WRITE_EXTERNAL_STORAGE"))) { r_folder = MCValueRetain(kMCEmptyString); return False; @@ -428,18 +429,18 @@ Boolean MCAndroidSystem::GetStandardFolder(MCNameRef p_folder, MCStringRef &r_fo // SN-2015-04-16: [[ Bug 14295 ]] The resources folder on Mobile is the same // as the engine folder. if (MCNameIsEqualToCaseless(p_folder, MCN_engine) - || MCNameIsEqualToCaseless(p_folder, MCN_resources)) + || MCNameIsEqualToCaseless(p_folder, MCN_resources)) { MCLog("GetStandardFolder(\"%@\") -> \"%@\"", MCNameGetString(p_folder), MCcmd); - return MCStringCopy(MCcmd, r_folder); + return MCStringCopy(MCcmd, r_folder); } - MCAutoStringRef t_stdfolder; + MCAutoStringRef t_stdfolder; MCAndroidEngineCall("getSpecialFolderPath", "xx", &(&t_stdfolder), MCNameGetString(p_folder)); MCLog("GetStandardFolder(\"%@\") -> \"%@\"", p_folder, *t_stdfolder == nil ? kMCEmptyString : *t_stdfolder); - r_folder = MCValueRetain(*t_stdfolder == nil ? kMCEmptyString : *t_stdfolder); + r_folder = MCValueRetain(*t_stdfolder == nil ? kMCEmptyString : *t_stdfolder); return True; } diff --git a/engine/src/mblandroidsensor.cpp b/engine/src/mblandroidsensor.cpp index be8f0acf323..3f1182c000b 100644 --- a/engine/src/mblandroidsensor.cpp +++ b/engine/src/mblandroidsensor.cpp @@ -64,7 +64,8 @@ bool MCSystemGetSensorAvailable(MCSensorType p_sensor, bool& r_available) { if (p_sensor == kMCSensorTypeLocation) { - bool t_success = MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_COARSE_LOCATION")) && MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_FINE_LOCATION")); + bool t_success = MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_COARSE_LOCATION")) && \ + MCAndroidCheckRuntimePermission(MCSTR("android.permission.ACCESS_FINE_LOCATION")); if (!t_success) return false; } From d2c5a3a021f347d2ed4230fe219cc0caaa9fa2b9 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Mon, 30 Jul 2018 15:06:26 +0100 Subject: [PATCH 12/19] Added "androidHasPermission" and "androidPermissionExists" commands --- .../androidRequestPermission.lcdoc | 15 ++-- .../function/androidHasPermission.lcdoc | 46 +++++++++++ .../function/androidPermissionExists.lcdoc | 33 ++++++++ engine/src/exec-misc.cpp | 16 ++++ engine/src/exec.h | 2 + engine/src/executionerrors.h | 3 + .../src/java/com/runrev/android/Engine.java | 77 +++++++++++++++++++ engine/src/mblandroiddc.cpp | 16 ++++ engine/src/mblandroidmisc.cpp | 12 +++ engine/src/mblandroidutil.h | 2 + engine/src/mblhandlers.cpp | 71 ++++++++++++++++- engine/src/mblsyntax.h | 2 + 12 files changed, 287 insertions(+), 8 deletions(-) rename docs/dictionary/{function => command}/androidRequestPermission.lcdoc (65%) create mode 100644 docs/dictionary/function/androidHasPermission.lcdoc create mode 100644 docs/dictionary/function/androidPermissionExists.lcdoc diff --git a/docs/dictionary/function/androidRequestPermission.lcdoc b/docs/dictionary/command/androidRequestPermission.lcdoc similarity index 65% rename from docs/dictionary/function/androidRequestPermission.lcdoc rename to docs/dictionary/command/androidRequestPermission.lcdoc index 77d1ec486ae..e098fea43ad 100644 --- a/docs/dictionary/function/androidRequestPermission.lcdoc +++ b/docs/dictionary/command/androidRequestPermission.lcdoc @@ -1,12 +1,11 @@ Name: androidRequestPermission -Type: function +Type: command Syntax: androidRequestPermission() Summary: -Returns if permission has been granted by the user. If the user has not been asked before, a dialog is displayed, -showing a permission request for . +Displays a dialog showing a permission request for . If a user has already granted permission for , this command does nothing. Introduced: 9.0.1 @@ -16,7 +15,11 @@ Platforms: mobile Example: local tCameraPermissionGranted -put androidRequestPermission("android.permission.CAMERA") into tCameraPermissionGranted +put androidHasPermission("android.permission.CAMERA") into tCameraPermissionGranted +if not tCameraPermissionGranted then + androidRequestPermission("android.permission.CAMERA") +end if +put androidHasPermission("android.permission.CAMERA") into tCameraPermissionGranted if not tCameraPermissionGranted then answer "This app is not permitted to access the device camera. You can change this" && \ "in the Settings app." @@ -34,11 +37,9 @@ The name of the permission to request. - "android.permission.READ_CONTACTS": permission to read data from the device contacts. - "android.permission.WRITE_EXTERNAL_STORAGE": permission to write data to the device external storage. -Returns(boolean): -True if permission has been granted, false otherwise. Description: -Use the function to request permission for from the user, or to find out if permission has been granted. +Use the command to request permission for from the user. >*Note:* Permission names are case sensitive. diff --git a/docs/dictionary/function/androidHasPermission.lcdoc b/docs/dictionary/function/androidHasPermission.lcdoc new file mode 100644 index 00000000000..856aedfe73c --- /dev/null +++ b/docs/dictionary/function/androidHasPermission.lcdoc @@ -0,0 +1,46 @@ +Name: androidHasPermission + +Type: function + +Syntax: androidHasPermission() + +Summary: +Returns if permission has been granted by the user. + +Introduced: 9.0.1 + +OS: android + +Platforms: mobile + +Example: +local tLocationPermissionGranted +put androidHasPermission("android.permission.ACCESS_FINE_LOCATION") into tLocationPermissionGranted +if not tLocationPermissionGranted then + androidRequestPermission("android.permission.ACCESS_FINE_LOCATION") +end if +if not tLocationPermissionGranted then + answer "This app is not permitted to access the device location. You can change this" && \ + "in the Settings app." +end if + + +Parameters: +permissionName (enum): +The name of the permission to request. + +- "android.permission.CAMERA": permission to access the device camera. +- "android.permission.ACCESS_COARSE_LOCATION": permission to access the device coarse location. +- "android.permission.ACCESS_FINE_LOCATION": permission to access the device fine location. +- "android.permission.WRITE_CONTACTS": permission to write date to the device contacts. +- "android.permission.READ_CONTACTS": permission to read data from the device contacts. +- "android.permission.WRITE_EXTERNAL_STORAGE": permission to write data to the device external storage. + +Returns(boolean): +True if permission has been granted, false otherwise. + + +Description: +Use the function to find out if permission has been granted by the user. + +>*Note:* Permission names are case sensitive. diff --git a/docs/dictionary/function/androidPermissionExists.lcdoc b/docs/dictionary/function/androidPermissionExists.lcdoc new file mode 100644 index 00000000000..46f1341b896 --- /dev/null +++ b/docs/dictionary/function/androidPermissionExists.lcdoc @@ -0,0 +1,33 @@ +Name: androidPermissionExists + +Type: function + +Syntax: androidPermissionExists() + +Summary: +Returns true if is a valid Android permission name + +Introduced: 9.0.1 + +OS: android + +Platforms: mobile + +Example: + +if not androidPermissionExists(pPermission) then + answer pPermission && "is not a valid permission name" +end if + + +Parameters: +permissionName (string): +The name of the permission to check. + +Returns(boolean): +True if is a valid Android permission name, false otherwise. + +Description: +Use the function to check if is a valid Android permission name. + +>*Note:* Permission names are case sensitive. diff --git a/engine/src/exec-misc.cpp b/engine/src/exec-misc.cpp index e5a92186558..432396ca6e0 100644 --- a/engine/src/exec-misc.cpp +++ b/engine/src/exec-misc.cpp @@ -489,6 +489,22 @@ void MCMiscExecRequestPermission(MCExecContext& ctxt, MCStringRef p_permission, ctxt.Throw(); } +void MCMiscExecPermissionExists(MCExecContext& ctxt, MCStringRef p_permission, bool& r_exists) +{ + if (MCSystemPermissionExists(p_permission, r_exists)) + return; + + ctxt.Throw(); +} + +void MCMiscExecHasPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_permission_granted) +{ + if (MCSystemHasPermission(p_permission, r_permission_granted)) + return; + + ctxt.Throw(); +} + void MCMiscSetDoNotBackupFile(MCExecContext& ctxt, MCStringRef p_path, bool p_no_backup) { diff --git a/engine/src/exec.h b/engine/src/exec.h index 6df71a37ed0..2644f12c558 100644 --- a/engine/src/exec.h +++ b/engine/src/exec.h @@ -4044,6 +4044,8 @@ void MCMiscExecLibUrlSetSSLVerification(MCExecContext& ctxt, bool p_enabled); void MCMiscGetBuildInfo(MCExecContext& ctxt, MCStringRef p_key, MCStringRef& r_value); void MCMiscExecRequestPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_granted); +void MCMiscExecPermissionExists(MCExecContext& ctxt, MCStringRef p_permission, bool& r_exists); +void MCMiscExecHasPermission(MCExecContext& ctxt, MCStringRef p_permission, bool& r_permission_granted); void MCMiscExecEnableRemoteControl(MCExecContext& ctxt); void MCMiscExecDisableRemoteControl(MCExecContext& ctxt); diff --git a/engine/src/executionerrors.h b/engine/src/executionerrors.h index 90b4e821948..7a4f1dd565c 100644 --- a/engine/src/executionerrors.h +++ b/engine/src/executionerrors.h @@ -2774,6 +2774,9 @@ enum Exec_errors // {EE-0908} fontLanguage: bad font name EE_FONTLANGUAGE_BADFONTNAME, + // {EE-0909} android permission: bad permission name + EE_BAD_PERMISSION_NAME, + }; extern const char *MCexecutionerrors; diff --git a/engine/src/java/com/runrev/android/Engine.java b/engine/src/java/com/runrev/android/Engine.java index 280f3399568..e88af53fd66 100644 --- a/engine/src/java/com/runrev/android/Engine.java +++ b/engine/src/java/com/runrev/android/Engine.java @@ -1878,6 +1878,83 @@ public boolean askPermission(String p_permission) return true; } + public boolean checkHasPermissionGranted(String p_permission) + { + if (Build.VERSION.SDK_INT >= 23) + { + return getContext().checkSelfPermission(p_permission) == PackageManager.PERMISSION_GRANTED; + } + return true; + } + + + public boolean checkPermissionExists(String p_permission) + { + if (Build.VERSION.SDK_INT >= 23) + { + List t_group_info_list = getAllPermissionGroups(); + if (t_group_info_list == null) + return false; + + ArrayList t_group_name_list = new ArrayList(); + for (PermissionGroupInfo t_group_info : t_group_info_list) + { + String t_group_name = t_group_info.name; + if (t_group_name != null) + t_group_name_list.add(t_group_name); + } + + for (String t_group_name : t_group_name_list) + { + ArrayList t_permission_name_list = getPermissionsForGroup(t_group_name); + + if (t_permission_name_list.contains(p_permission)) + return true; + } + return false; + } + return true; + } + + private List getAllPermissionGroups() + { + final PackageManager t_package_manager = getContext().getPackageManager(); + if (t_package_manager == null) + return null; + + return t_package_manager.getAllPermissionGroups(0); + } + + private ArrayList getPermissionsForGroup(String p_group_name) + { + final PackageManager t_package_manager = getContext().getPackageManager(); + final ArrayList t_permission_name_list = new ArrayList(); + + try + { + List t_permission_info_list = + t_package_manager.queryPermissionsByGroup(p_group_name, PackageManager.GET_META_DATA); + if (t_permission_info_list != null) + { + for (PermissionInfo t_permission_info : t_permission_info_list) + { + String t_permission_name = t_permission_info.name; + t_permission_name_list.add(t_permission_name); + } + } + } + catch (PackageManager.NameNotFoundException e) + { + // e.printStackTrace(); + Log.d(TAG, "permissions not found for group = " + p_group_name); + } + + Collections.sort(t_permission_name_list); + + return t_permission_name_list; + } + + public void showPhotoPicker(String p_source, int p_width, int p_height) { m_photo_width = p_width; diff --git a/engine/src/mblandroiddc.cpp b/engine/src/mblandroiddc.cpp index 81a38421a7a..7d8801ae5f9 100644 --- a/engine/src/mblandroiddc.cpp +++ b/engine/src/mblandroiddc.cpp @@ -2831,6 +2831,22 @@ bool MCAndroidCheckRuntimePermission(MCStringRef p_permission) return s_permission_granted; } +bool MCAndroidCheckPermissionExists(MCStringRef p_permission) +{ + bool t_result; + MCAndroidEngineRemoteCall("checkPermissionExists", "bx", &t_result, p_permission); + + return t_result; +} + +bool MCAndroidHasPermission(MCStringRef p_permission) +{ + bool t_result; + MCAndroidEngineRemoteCall("checkHasPermissionGranted", "bx", &t_result, p_permission); + + return t_result; +} + extern "C" JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv *env, jobject object, bool granted) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_runrev_android_Engine_doAskPermissionDone(JNIEnv *env, jobject object, bool granted) { diff --git a/engine/src/mblandroidmisc.cpp b/engine/src/mblandroidmisc.cpp index 49fd9a28b4e..477ea6fa94b 100644 --- a/engine/src/mblandroidmisc.cpp +++ b/engine/src/mblandroidmisc.cpp @@ -808,6 +808,18 @@ bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted) return true; } +bool MCSystemPermissionExists(MCStringRef p_permission, bool& r_exists) +{ + r_exists = MCAndroidCheckPermissionExists(p_permission); + return true; +} + +bool MCSystemHasPermission(MCStringRef p_permission, bool& r_permission_granted) +{ + r_permission_granted = MCAndroidHasPermission(p_permission); + return true; +} + //////////////////////////////////////////////////////////////////////////////// diff --git a/engine/src/mblandroidutil.h b/engine/src/mblandroidutil.h index 874574ed256..108877c0a7f 100644 --- a/engine/src/mblandroidutil.h +++ b/engine/src/mblandroidutil.h @@ -64,6 +64,8 @@ void MCAndroidObjectRemoteCall(jobject p_object, const char *p_method, const cha bool MCAndroidGetBuildInfo(MCStringRef t_key, MCStringRef &r_value); bool MCAndroidCheckRuntimePermission(MCStringRef p_permission); +bool MCAndroidCheckPermissionExists(MCStringRef p_permission); +bool MCAndroidHasPermission(MCStringRef p_permission); typedef struct _android_device_configuration { diff --git a/engine/src/mblhandlers.cpp b/engine/src/mblhandlers.cpp index 33aea63342c..2a5f0b48422 100644 --- a/engine/src/mblhandlers.cpp +++ b/engine/src/mblhandlers.cpp @@ -3377,6 +3377,7 @@ Exec_stat MCHandleBuildInfo(void *context, MCParameter *p_parameters) return ES_ERROR; } +/////////////////// Android 6.0 runtime permissions ///////////////////////// Exec_stat MCHandleRequestPermission(void *context, MCParameter *p_parameters) { MCExecContext ctxt(nil, nil, nil); @@ -3386,12 +3387,78 @@ Exec_stat MCHandleRequestPermission(void *context, MCParameter *p_parameters) t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + bool t_permission_exists; + MCMiscExecPermissionExists(ctxt, *t_permission, t_permission_exists); + + if (!t_permission_exists) + { + ctxt.LegacyThrow(EE_BAD_PERMISSION_NAME); + t_success = false; + } + if (t_success) MCMiscExecRequestPermission(ctxt, *t_permission, t_granted); + Exec_stat t_stat; + if (!ctxt . HasError()) + t_stat = ES_NORMAL; + else + t_stat = ES_ERROR; + + ctxt.SetTheResultToEmpty(); + return t_stat; +} + +Exec_stat MCHandlePermissionExists(void *context, MCParameter *p_parameters) +{ + MCExecContext ctxt(nil, nil, nil); + + MCAutoStringRef t_permission; + bool t_success, t_exists; + + t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + + if (t_success) + MCMiscExecPermissionExists(ctxt, *t_permission, t_exists); + + if (!ctxt . HasError()) + { + if (t_exists) + ctxt.SetTheResultToValue(kMCTrueString); + else + ctxt.SetTheResultToValue(kMCFalseString); + + return ES_NORMAL; + } + + ctxt.SetTheResultToEmpty(); + return ES_ERROR; +} + +Exec_stat MCHandleHasPermission(void *context, MCParameter *p_parameters) +{ + MCExecContext ctxt(nil, nil, nil); + + MCAutoStringRef t_permission; + bool t_success, t_permission_granted; + + t_success = MCParseParameters(p_parameters, "x", &(&t_permission)); + + bool t_permission_exists; + MCMiscExecPermissionExists(ctxt, *t_permission, t_permission_exists); + + if (!t_permission_exists) + { + ctxt.LegacyThrow(EE_BAD_PERMISSION_NAME); + t_success = false; + } + + if (t_success) + MCMiscExecHasPermission(ctxt, *t_permission, t_permission_granted); + if (!ctxt . HasError()) { - if (t_granted) + if (t_permission_granted) ctxt.SetTheResultToValue(kMCTrueString); else ctxt.SetTheResultToValue(kMCFalseString); @@ -4556,6 +4623,8 @@ static const MCPlatformMessageSpec s_platform_messages[] = {false, "mobileBuildInfo", MCHandleBuildInfo, nil}, {false, "androidRequestPermission", MCHandleRequestPermission, nil}, + {false, "androidPermissionExists", MCHandlePermissionExists, nil}, + {false, "androidHasPermission", MCHandleHasPermission, nil}, {false, "mobileCanMakePurchase", MCHandleCanMakePurchase, nil}, {false, "mobileEnablePurchaseUpdates", MCHandleEnablePurchaseUpdates, nil}, diff --git a/engine/src/mblsyntax.h b/engine/src/mblsyntax.h index d668a186cef..7f653d92584 100644 --- a/engine/src/mblsyntax.h +++ b/engine/src/mblsyntax.h @@ -467,6 +467,8 @@ bool MCSystemFileGetDataProtection(MCStringRef p_path, MCStringRef& r_protection bool MCSystemBuildInfo(MCStringRef p_key, MCStringRef& r_value); bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted); +bool MCSystemPermissionExists(MCStringRef p_permission, bool& r_exists); +bool MCSystemHasPermission(MCStringRef p_permission, bool& r_permission_granted); bool MCSystemEnableRemoteControl(); bool MCSystemDisableRemoteControl(); From 120b71fde68f4c4cac94d4632778a888d5a3190d Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Tue, 31 Jul 2018 09:30:57 +0100 Subject: [PATCH 13/19] Added stubs for iphone and updated docs --- .../command/androidRequestPermission.lcdoc | 34 +++++++++++++++---- .../function/androidHasPermission.lcdoc | 34 +++++++++++++++---- engine/src/mbliphonemisc.mm | 22 ++++++++++++ 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/docs/dictionary/command/androidRequestPermission.lcdoc b/docs/dictionary/command/androidRequestPermission.lcdoc index e098fea43ad..63a2ed2b168 100644 --- a/docs/dictionary/command/androidRequestPermission.lcdoc +++ b/docs/dictionary/command/androidRequestPermission.lcdoc @@ -17,7 +17,7 @@ Example: local tCameraPermissionGranted put androidHasPermission("android.permission.CAMERA") into tCameraPermissionGranted if not tCameraPermissionGranted then - androidRequestPermission("android.permission.CAMERA") + androidRequestPermission "android.permission.CAMERA" end if put androidHasPermission("android.permission.CAMERA") into tCameraPermissionGranted if not tCameraPermissionGranted then @@ -30,12 +30,32 @@ Parameters: permissionName (enum): The name of the permission to request. -- "android.permission.CAMERA": permission to access the device camera. -- "android.permission.ACCESS_COARSE_LOCATION": permission to access the device coarse location. -- "android.permission.ACCESS_FINE_LOCATION": permission to access the device fine location. -- "android.permission.WRITE_CONTACTS": permission to write date to the device contacts. -- "android.permission.READ_CONTACTS": permission to read data from the device contacts. -- "android.permission.WRITE_EXTERNAL_STORAGE": permission to write data to the device external storage. +- "android.permission.READ_CALENDAR": permission to allow an application to read the device's calendar. +- "android.permission.WRITE_CALENDAR": permission to allow an application to write to the device's calendar. +- "android.permission.CAMERA": permission to allow an application to access the device's camera. +- "android.permission.ACCESS_COARSE_LOCATION": permission to allow an application to access the device's coarse location. +- "android.permission.ACCESS_FINE_LOCATION": permission to allow an application to access the device's fine location. +- "android.permission.READ_CONTACTS": permission to allow an application to read data from the device's contacts. +- "android.permission.WRITE_CONTACTS": permission to allow an application to write date to the device's contacts. +- "android.permission.GET_ACCOUNTS": permission to allow an application to access to the list of accounts in the Accounts Service. +- "android.permission.RECORD_AUDIO": permission to allow an application to allow an application to record audio. +- "android.permission.READ_EXTERNAL_STORAGE": permission to allow an application to read data from the device's external storage. +- "android.permission.WRITE_EXTERNAL_STORAGE": permission to allow an application to write data to the device's external storage. +- "android.permission.READ_PHONE_STATE": permission to allow an application to access phone state, including the phone number of the device, current cellular network information, the status of any ongoing calls, and a list of any PhoneAccounts registered on the device. +- "android.permission.READ_PHONE_NUMBERS": permission to allow an application to access the device's phone number(s). +- "android.permission.CALL_PHONE": permission to allow an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call. +- "android.permission.ANSWER_PHONE_CALLS": permission to allow an application to answer an incoming phone call. +- "android.permission.READ_CALL_LOG": permission to allow an application to read the user's call log. +- "android.permission.WRITE_CALL_LOG": permission to allow an application to write to the user's call log. +- "android.permission.ADD_VOICEMAIL": permission to allow an application to add voicemails into the system. +- "android.permission.USE_SIP": permission to allow an application to use SIP service. +- "android.permission.PROCESS_OUTGOING_CALLS": permission to allow an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. +- "android.permission.SEND_SMS": permission to allow an application to send SMS messages. +- "android.permission.RECEIVE_SMS": permission to allow an application to receive SMS messages +- "android.permission.READ_SMS": permission to allow an application to read SMS messages. +- "android.permission.RECEIVE_WAP_PUSH": permission to allow an application to receive WAP push messages. +- "android.permission.RECEIVE_MMS": permission to allow an application to receive MMS messages. +- "android.permission.BODY_SENSORS": permission to allow an application to access data from sensors that the user uses to measure what is happening inside his/her body, such as heart rate. diff --git a/docs/dictionary/function/androidHasPermission.lcdoc b/docs/dictionary/function/androidHasPermission.lcdoc index 856aedfe73c..48219cce0e2 100644 --- a/docs/dictionary/function/androidHasPermission.lcdoc +++ b/docs/dictionary/function/androidHasPermission.lcdoc @@ -17,7 +17,7 @@ Example: local tLocationPermissionGranted put androidHasPermission("android.permission.ACCESS_FINE_LOCATION") into tLocationPermissionGranted if not tLocationPermissionGranted then - androidRequestPermission("android.permission.ACCESS_FINE_LOCATION") + androidRequestPermission "android.permission.ACCESS_FINE_LOCATION" end if if not tLocationPermissionGranted then answer "This app is not permitted to access the device location. You can change this" && \ @@ -29,12 +29,32 @@ Parameters: permissionName (enum): The name of the permission to request. -- "android.permission.CAMERA": permission to access the device camera. -- "android.permission.ACCESS_COARSE_LOCATION": permission to access the device coarse location. -- "android.permission.ACCESS_FINE_LOCATION": permission to access the device fine location. -- "android.permission.WRITE_CONTACTS": permission to write date to the device contacts. -- "android.permission.READ_CONTACTS": permission to read data from the device contacts. -- "android.permission.WRITE_EXTERNAL_STORAGE": permission to write data to the device external storage. +- "android.permission.READ_CALENDAR": permission to allow an application to read the device's calendar. +- "android.permission.WRITE_CALENDAR": permission to allow an application to write to the device's calendar. +- "android.permission.CAMERA": permission to allow an application to access the device's camera. +- "android.permission.ACCESS_COARSE_LOCATION": permission to allow an application to access the device's coarse location. +- "android.permission.ACCESS_FINE_LOCATION": permission to allow an application to access the device's fine location. +- "android.permission.READ_CONTACTS": permission to allow an application to read data from the device's contacts. +- "android.permission.WRITE_CONTACTS": permission to allow an application to write date to the device's contacts. +- "android.permission.GET_ACCOUNTS": permission to allow an application to access to the list of accounts in the Accounts Service. +- "android.permission.RECORD_AUDIO": permission to allow an application to allow an application to record audio. +- "android.permission.READ_EXTERNAL_STORAGE": permission to allow an application to read data from the device's external storage. +- "android.permission.WRITE_EXTERNAL_STORAGE": permission to allow an application to write data to the device's external storage. +- "android.permission.READ_PHONE_STATE": permission to allow an application to access phone state, including the phone number of the device, current cellular network information, the status of any ongoing calls, and a list of any PhoneAccounts registered on the device. +- "android.permission.READ_PHONE_NUMBERS": permission to allow an application to access the device's phone number(s). +- "android.permission.CALL_PHONE": permission to allow an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call. +- "android.permission.ANSWER_PHONE_CALLS": permission to allow an application to answer an incoming phone call. +- "android.permission.READ_CALL_LOG": permission to allow an application to read the user's call log. +- "android.permission.WRITE_CALL_LOG": permission to allow an application to write to the user's call log. +- "android.permission.ADD_VOICEMAIL": permission to allow an application to add voicemails into the system. +- "android.permission.USE_SIP": permission to allow an application to use SIP service. +- "android.permission.PROCESS_OUTGOING_CALLS": permission to allow an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. +- "android.permission.SEND_SMS": permission to allow an application to send SMS messages. +- "android.permission.RECEIVE_SMS": permission to allow an application to receive SMS messages +- "android.permission.READ_SMS": permission to allow an application to read SMS messages. +- "android.permission.RECEIVE_WAP_PUSH": permission to allow an application to receive WAP push messages. +- "android.permission.RECEIVE_MMS": permission to allow an application to receive MMS messages. +- "android.permission.BODY_SENSORS": permission to allow an application to access data from sensors that the user uses to measure what is happening inside his/her body, such as heart rate. Returns(boolean): True if permission has been granted, false otherwise. diff --git a/engine/src/mbliphonemisc.mm b/engine/src/mbliphonemisc.mm index 3b497d95ac1..2fbcceb7fee 100644 --- a/engine/src/mbliphonemisc.mm +++ b/engine/src/mbliphonemisc.mm @@ -26,3 +26,25 @@ int32_t MCCustomPrinterComputeFontSize(void *font) { return CTFontGetSize((CTFontRef)font); } + +//////////////////////////////////////////////////////////////////////////////// + +bool MCSystemRequestPermission(MCStringRef p_permission, bool& r_granted) +{ + // Not implemented + return false; +} + +bool MCSystemPermissionExists(MCStringRef p_permission, bool& r_exists) +{ + // Not implemented + return false; +} + +bool MCSystemHasPermission(MCStringRef p_permission, bool& r_permission_granted) +{ + // Not implemented + return false; +} + +//////////////////////////////////////////////////////////////////////////////// From 39b59eeee08b75e6968c872bc865258757f11c73 Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Tue, 31 Jul 2018 09:32:48 +0100 Subject: [PATCH 14/19] [androidRequestPermission] Docs: removed parentheses from syntax --- docs/dictionary/command/androidRequestPermission.lcdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dictionary/command/androidRequestPermission.lcdoc b/docs/dictionary/command/androidRequestPermission.lcdoc index 63a2ed2b168..07e5f1ec831 100644 --- a/docs/dictionary/command/androidRequestPermission.lcdoc +++ b/docs/dictionary/command/androidRequestPermission.lcdoc @@ -2,7 +2,7 @@ Name: androidRequestPermission Type: command -Syntax: androidRequestPermission() +Syntax: androidRequestPermission Summary: Displays a dialog showing a permission request for . If a user has already granted permission for , this command does nothing. From b1be19ca9592f82b137fd8e3564fff76381606ae Mon Sep 17 00:00:00 2001 From: livecodepanos Date: Tue, 31 Jul 2018 11:32:02 +0100 Subject: [PATCH 15/19] [9.0.1-rc-2] Updated version --- version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version b/version index 12491871bcd..493f72c3dca 100644 --- a/version +++ b/version @@ -1,6 +1,6 @@ -BUILD_REVISION = 15100 +BUILD_REVISION = 15101 BUILD_MAJOR_VERSION = 9 BUILD_MINOR_VERSION = 0 BUILD_POINT_VERSION = 1 -BUILD_SHORT_VERSION = 9.0.1-rc-1 -BUILD_LONG_VERSION = 9.0.1.15100 (RC 1) +BUILD_SHORT_VERSION = 9.0.1-rc-2 +BUILD_LONG_VERSION = 9.0.1.15101 (RC 2) From 3ba44983945dd33540668376409f71d9ec985c40 Mon Sep 17 00:00:00 2001 From: Monte Goulding Date: Thu, 2 Aug 2018 12:52:29 +1000 Subject: [PATCH 16/19] [[ Bug 21443 ]] Dirty rect even if layer id is 0 This patch ensures that groups with `m_layer_id` of 0 still have their effective rect dirtied. Previously a `return` was causing the dirtying of the rect to be skipped. --- docs/notes/bugfix-21443.md | 1 + engine/src/redraw.cpp | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) create mode 100644 docs/notes/bugfix-21443.md diff --git a/docs/notes/bugfix-21443.md b/docs/notes/bugfix-21443.md new file mode 100644 index 00000000000..152fe0a6576 --- /dev/null +++ b/docs/notes/bugfix-21443.md @@ -0,0 +1 @@ +# Fix groups sometimes not redrawing when scrolled \ No newline at end of file diff --git a/engine/src/redraw.cpp b/engine/src/redraw.cpp index 9e09d160851..0fe196e8d79 100644 --- a/engine/src/redraw.cpp +++ b/engine/src/redraw.cpp @@ -599,14 +599,10 @@ void MCControl::layer_dirtyeffectiverect(const MCRectangle& p_effective_rect, bo MCRectangle32 t_device_rect; t_device_rect = MCRectangle32GetTransformedBounds(t_dirty_rect, t_transform); - // Notify any tilecache of the changes. - if (t_tilecache != nil) + // Notify any tilecache of the changes + // If the layer id is zero, there is nothing to do. + if (t_tilecache != nil && t_control -> m_layer_id != 0) { - // We must be in tile-cache mode with a top-level control, but if the layer - // id is zero, there is nothing to do. - if (t_control -> m_layer_id == 0) - return; - // How we handle the layer depends on whether it is a sprite or not. if (!t_control -> layer_issprite()) { From 7e3c2bbb1338cc42a6448cac86873e368805fefc Mon Sep 17 00:00:00 2001 From: livecodeali Date: Wed, 15 Aug 2018 16:05:55 +0100 Subject: [PATCH 17/19] [[ Bug 21496 ]] Ensure emscripten startup_script is suitable for capsule section Previously the emscripten startup script was a startup handler for a boot stack. Now that emscripten deploy uses the deploy command and capsule format, it needs to be a script snippet (that will ultimately be placed in a generated `on message` handler which is called on startup). --- docs/notes/bugfix-21496.md | 1 + ...emscripten-startup-template.livecodescript | 68 +++++++++---------- ...vsaveasemscriptenstandalone.livecodescript | 6 +- 3 files changed, 34 insertions(+), 41 deletions(-) create mode 100644 docs/notes/bugfix-21496.md diff --git a/docs/notes/bugfix-21496.md b/docs/notes/bugfix-21496.md new file mode 100644 index 00000000000..89812e0345b --- /dev/null +++ b/docs/notes/bugfix-21496.md @@ -0,0 +1 @@ +# Ensure emscripten aux stacks are loaded on startup diff --git a/engine/rsrc/emscripten-startup-template.livecodescript b/engine/rsrc/emscripten-startup-template.livecodescript index 3638d194e57..337612d6959 100644 --- a/engine/rsrc/emscripten-startup-template.livecodescript +++ b/engine/rsrc/emscripten-startup-template.livecodescript @@ -1,4 +1,3 @@ -script "__startup" constant kEngineVersion = "@ENGINE_VERSION@" -- Directories that engine expects to normally be present @@ -7,41 +6,38 @@ constant kStandardFolders = "/tmp:/livecode:/boot:/boot/standalone:/boot/fonts" -- Directory containing the initial stack files constant kStartupFolder = "/boot/standalone" -on startup - local tError, tFolder - try - ---------------------------------------------------------------- - -- Create standard filesystem layout - set the itemdelimiter to ":" - repeat for each item tFolder in kStandardFolders - if there is not a folder tFolder then - create folder tFolder - if the result is not empty then - throw the result - end if +local tError, tFolder +try + ---------------------------------------------------------------- + -- Create standard filesystem layout + set the itemdelimiter to ":" + repeat for each item tFolder in kStandardFolders + if there is not a folder tFolder then + create folder tFolder + if the result is not empty then + throw the result end if - end repeat - - ------------------------------------------------------------- - -- Validate engine version - if the version is not kEngineVersion then - throw "Engine mismatch: found" && the version & ", expected" && kEngineVersion end if - - - @STARTUP_SCRIPT@ - - catch tError - end try - - -- Set the initial working directory to the directory that contains - -- the initial stack. - set the defaultfolder to kStartupFolder - - -- Try to print something vaguely helpful to the the log - if tError is not empty then - write "startup failed:" && tError & return to stderr + end repeat + + ------------------------------------------------------------- + -- Validate engine version + if the version is not kEngineVersion then + throw "Engine mismatch: found" && the version & ", expected" && kEngineVersion end if - - return tError -end startup + + + @STARTUP_SCRIPT@ + +catch tError +end try + +-- Try to print something vaguely helpful to the the log +if tError is not empty then + write "startup failed:" && tError & return to stderr + throw tError +end if + +-- Set the initial working directory to the directory that contains +-- the initial stack. +set the defaultfolder to kStartupFolder diff --git a/ide-support/revsaveasemscriptenstandalone.livecodescript b/ide-support/revsaveasemscriptenstandalone.livecodescript index 6cb19c822a6..deeafe06bcf 100644 --- a/ide-support/revsaveasemscriptenstandalone.livecodescript +++ b/ide-support/revsaveasemscriptenstandalone.livecodescript @@ -253,13 +253,9 @@ private function getStartupScript pGeneratedStartupScript throw tTemplateFile & ":" && the result end if close file tTemplateFile - - -- Trim to the actual script - delete line 1 of tScript - + -- Make substitutions in the startup script replace "@ENGINE_VERSION@" with the version in tScript - replace "@MODULE_VERSION@" with extensionLCCompileVersion() in tScript replace "@STARTUP_SCRIPT@" with pGeneratedStartupScript in tScript return tScript end getStartupScript From 016f200bce13f16fc24a095bee61409b75d05a52 Mon Sep 17 00:00:00 2001 From: livecodeali Date: Wed, 15 Aug 2018 16:10:33 +0100 Subject: [PATCH 18/19] [[ Bug 21417 ]] Remove externals list from emscripten deploy params Now that the deploy command is used on emscripten, we need to empty the externals deploy param as they are not supported for emscripten builds. --- docs/notes/bugfix-21417.md | 1 + ide-support/revsaveasemscriptenstandalone.livecodescript | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 docs/notes/bugfix-21417.md diff --git a/docs/notes/bugfix-21417.md b/docs/notes/bugfix-21417.md new file mode 100644 index 00000000000..e0c98c759f6 --- /dev/null +++ b/docs/notes/bugfix-21417.md @@ -0,0 +1 @@ +# Don't include any externals in emscripten standalones diff --git a/ide-support/revsaveasemscriptenstandalone.livecodescript b/ide-support/revsaveasemscriptenstandalone.livecodescript index 6cb19c822a6..fb288076a14 100644 --- a/ide-support/revsaveasemscriptenstandalone.livecodescript +++ b/ide-support/revsaveasemscriptenstandalone.livecodescript @@ -202,7 +202,10 @@ private command storeDeployedStack pZip, pDeployPath, pBuildFolder, pMainStack, put tTempStackPath into pDeployParams["stackfile"] put tTempDeployPath into pDeployParams["output"] put empty into pDeployParams["engine"] - + + // Externals are not yet supported in emscripten builds + put empty into pDeployParams["externals"] + ---------- Perform standalone deployment logDebug "deploy", "Deploying standalone" From 964d263d729b746dae105d777af2a492909f1505 Mon Sep 17 00:00:00 2001 From: Monte Goulding Date: Thu, 16 Aug 2018 08:47:01 +1000 Subject: [PATCH 19/19] [[ Bug 21396 ]] Fix crash on startup in iOS 12 beta This patch fixes an issue where the view layer appears to be retaining the `CGDataProvider` from an image when it is drawn via `CGContextDrawImage`. As we were freeing the raster directly after the draw the `CALayer` could not access it from the `CGDataProvider` when rendering causing a crash. This patch therefore changes the behavior of `MCGRasterCreateCGDataProvider` where previously when calling it with the copy parameter `false` it would be left to the caller to free the buffer the data provider now takes ownership of it and it is freed via the data provider free callback. As a consequence of the change custom cursor creation on mac now copies the buffer for simplicity. --- docs/notes/bugfix-21396.md | 1 + engine/src/cgimageutil.cpp | 6 ++++-- engine/src/mac-cursor.mm | 2 +- engine/src/mac-surface.mm | 1 - engine/src/mbliphonegfx.mm | 20 ++++++++++++++------ 5 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 docs/notes/bugfix-21396.md diff --git a/docs/notes/bugfix-21396.md b/docs/notes/bugfix-21396.md new file mode 100644 index 00000000000..c3b4faed0b7 --- /dev/null +++ b/docs/notes/bugfix-21396.md @@ -0,0 +1 @@ +# Fix crash on startup in iOS 12 beta \ No newline at end of file diff --git a/engine/src/cgimageutil.cpp b/engine/src/cgimageutil.cpp index fc09c878f08..573343b62d0 100644 --- a/engine/src/cgimageutil.cpp +++ b/engine/src/cgimageutil.cpp @@ -114,7 +114,7 @@ bool MCGRasterCreateCGDataProvider(const MCGRaster &p_raster, const MCGIntegerRe t_width = p_src_rect.size.width; t_height = p_src_rect.size.height; - const uint8_t *t_src_ptr = (uint8_t*)MCGRasterGetPixelPtr(p_raster, t_x, t_y); + uint8_t *t_src_ptr = (uint8_t*)MCGRasterGetPixelPtr(p_raster, t_x, t_y); uint32_t t_dst_stride; @@ -125,8 +125,10 @@ bool MCGRasterCreateCGDataProvider(const MCGRaster &p_raster, const MCGIntegerRe if (!p_copy) { t_dst_stride = p_raster.stride; - t_data_provider = CGDataProviderCreateWithData(nil, t_src_ptr, t_height * p_raster.stride, nil); + t_data_provider = CGDataProviderCreateWithData(nil, t_src_ptr, t_height * p_raster.stride, __CGDataProviderDeallocate); t_success = t_data_provider != nil; + if (!t_success) + MCMemoryDeallocate(t_src_ptr); } else { diff --git a/engine/src/mac-cursor.mm b/engine/src/mac-cursor.mm index 31d2aeae782..135c6d5007f 100644 --- a/engine/src/mac-cursor.mm +++ b/engine/src/mac-cursor.mm @@ -137,7 +137,7 @@ void MCPlatformCreateCustomCursor(MCImageBitmap *p_image, MCPoint p_hotspot, MCP t_cursor -> is_standard = false; CGImageRef t_cg_image; - /* UNCHECKED */ MCImageBitmapToCGImage(p_image, false, false, t_cg_image); + /* UNCHECKED */ MCImageBitmapToCGImage(p_image, true, false, t_cg_image); // Convert the CGImage into an NSIMage NSImage *t_cursor_image; diff --git a/engine/src/mac-surface.mm b/engine/src/mac-surface.mm index cccbaa9149c..559c0b58ddb 100644 --- a/engine/src/mac-surface.mm +++ b/engine/src/mac-surface.mm @@ -317,7 +317,6 @@ CGRect MCMacFlipCGRect(const CGRect &p_rect, uint32_t p_surface_height) // IM-2014-10-03: [[ Bug 13432 ]] Render with copy blend mode to replace destination alpha with the source alpha. MCMacRenderRasterToCG(m_cg_context, t_dst_rect, m_raster, MCGRectangleMake(0, 0, m_raster.width, m_raster.height), 1.0, kMCGBlendModeCopy); - free(m_raster . pixels); m_raster . pixels = nil; } diff --git a/engine/src/mbliphonegfx.mm b/engine/src/mbliphonegfx.mm index 22ac7370a2a..a75861d9f02 100644 --- a/engine/src/mbliphonegfx.mm +++ b/engine/src/mbliphonegfx.mm @@ -102,7 +102,7 @@ static void do_update(void *p_dirty) MCGRegionRef m_region; bool m_own_region; - virtual void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) = 0; + virtual void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride, bool &x_taken) = 0; public: MCIPhoneStackSurface(MCGRegionRef p_region) @@ -197,10 +197,16 @@ void UnlockPixels(MCGIntegerRectangle p_area, MCGRaster& p_raster, bool p_update if (p_raster . pixels == nil) return; + bool t_taken = false; + if (p_update) - FlushBits(p_area, p_raster . pixels, p_raster . stride); + FlushBits(p_area, p_raster . pixels, p_raster . stride, t_taken); + + if (!t_taken) + { + free(p_raster.pixels); + } - free(p_raster . pixels); } bool Composite(MCGRectangle p_dst_rect, MCGImageRef p_src, MCGRectangle p_src_rect, MCGFloat p_alpha, MCGBlendMode p_blend) @@ -337,7 +343,7 @@ void UnlockTarget(void) protected: // MM-2014-07-31: [[ ThreadedRendering ]] Updated to pass in the area we wish to draw. - void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) + void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride, bool &x_taken) { void *t_target; if (!LockTarget(kMCStackSurfaceTargetCoreGraphics, t_target)) @@ -368,6 +374,8 @@ void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) if (MCGRasterToCGImage(t_raster, MCGIntegerRectangleMake(0, 0, p_area.size.width, p_area.size.height), t_colorspace, false, false, t_image)) { + x_taken = true; + CGContextDrawImage(t_context, CGRectMake((float)p_area.origin.x, (float)(m_height - (p_area.origin.y + p_area.size.height)), (float)p_area.size.width, (float)p_area.size.height), t_image); CGImageRelease(t_image); } @@ -521,7 +529,7 @@ void UnlockTarget(void) protected: // MM-2014-07-31: [[ ThreadedRendering ]] Updated to pass in the area we wish to draw. - void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) + void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride, bool& x_taken) { GLuint t_texture; glGenTextures(1, &t_texture); @@ -582,7 +590,7 @@ void FlushBits(MCGIntegerRectangle p_area, void *p_bits, uint32_t p_stride) } glDeleteTextures(1, &t_texture); - } + } }; @implementation MCIPhoneOpenGLDisplayView