如何从C++代码请求Android NDK相机权限?

Dis*_*ame 5 c++ android android-ndk

我正在用纯 C++ 编写应用程序,并且我有打开相机的代码。并将 AndroidManifest.xml 设置为:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera2" android:required="true" />
Run Code Online (Sandbox Code Playgroud)

首次运行应用程序时,不会提示打开权限。安装后,我必须通过“设置”->“应用程序”->“MyApp”->“权限”手动执行此操作。

如何在不引入java代码的情况下用c++提供提示?非常感谢所有帮助。

Bru*_*evy 7

这可以使用 JNI 调用来完成。请参阅下面的 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 示例(可以轻松适应相机权限)。

调用该函数:

check_android_permissions(struct android_app* 应用程序)
(来源如下)应用程序启动时。

几个陷阱:

  • android.Manifest.permission 是一个嵌套在 android.Manifest 中的类,因此它的 JNI 名称是“android/Manifest$permission”。
  • 我没有设法从 JNI 访问 ContextCompat (android.support.v4.content.ContextCompat) 和 ActivityCompat (android.support.v4.app.ActivityCompat ),所以我使用了 Context (android.content.Context) 和 Activity (android. app.Activity)直接。因此,需要 Android API 级别 23(Marshmallow,2015 年 5 月)。
  • 需要在类中检索常量,特别是 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 是 android.Manifest.permission 中的静态字符串,而 PERMISSION_GRANTED 是 android.content.pm.PackageManager 中的静态整数。
  • 我没有实现当用户单击“不要再问我”按钮时如何处理,这需要实现 onRequestPermissionsResult() 回调。


/**
 * \brief Gets the internal name for an android permission.
 * \param[in] lJNIEnv a pointer to the JNI environment
 * \param[in] perm_name the name of the permission, e.g.,
 *   "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
 * \return a jstring with the internal name of the permission,
 *   to be used with android Java functions 
 *   Context.checkSelfPermission() or Activity.requestPermissions()
 */
jstring android_permission_name(JNIEnv* lJNIEnv, const char* perm_name) {
    // nested class permission in class android.Manifest,
    // hence android 'slash' Manifest 'dollar' permission
    jclass ClassManifestpermission = lJNIEnv->FindClass(
       "android/Manifest$permission"
    );
    jfieldID lid_PERM = lJNIEnv->GetStaticFieldID(
       ClassManifestpermission, perm_name, "Ljava/lang/String;"
    );
    jstring ls_PERM = (jstring)(lJNIEnv->GetStaticObjectField(
        ClassManifestpermission, lid_PERM
    )); 
    return ls_PERM;
}

/**
 * \brief Tests whether a permission is granted.
 * \param[in] app a pointer to the android app.
 * \param[in] perm_name the name of the permission, e.g.,
 *   "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
 * \retval true if the permission is granted.
 * \retval false otherwise.
 * \note Requires Android API level 23 (Marshmallow, May 2015)
 */
bool android_has_permission(struct android_app* app, const char* perm_name) {
    JavaVM* lJavaVM = app->activity->vm;
    JNIEnv* lJNIEnv = nullptr; 
    bool lThreadAttached = false;

    // Get JNIEnv from lJavaVM using GetEnv to test whether
    // thread is attached or not to the VM. If not, attach it
    // (and note that it will need to be detached at the end
    //  of the function).
    switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) {
      case JNI_OK:
        break;
      case JNI_EDETACHED: {
        jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr);
        if(lResult == JNI_ERR) {
          throw std::runtime_error("Could not attach current thread");
        }
        lThreadAttached = true;
      } break;
      case JNI_EVERSION:
        throw std::runtime_error("Invalid java version");
    }

    bool result = false;

    jstring ls_PERM = android_permission_name(lJNIEnv, perm_name);

    jint PERMISSION_GRANTED = jint(-1);
    {
       jclass ClassPackageManager = lJNIEnv->FindClass(
          "android/content/pm/PackageManager"
       );
       jfieldID lid_PERMISSION_GRANTED = lJNIEnv->GetStaticFieldID(
          ClassPackageManager, "PERMISSION_GRANTED", "I"
       );
       PERMISSION_GRANTED = lJNIEnv->GetStaticIntField(
          ClassPackageManager, lid_PERMISSION_GRANTED
       );
    }
    {
       jobject activity = app->activity->clazz;
       jclass ClassContext = lJNIEnv->FindClass(
          "android/content/Context"
       );
       jmethodID MethodcheckSelfPermission = lJNIEnv->GetMethodID(
          ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I"
       );
       jint int_result = lJNIEnv->CallIntMethod(
           activity, MethodcheckSelfPermission, ls_PERM
       );
       result = (int_result == PERMISSION_GRANTED);
    }

    if(lThreadAttached) {
      lJavaVM->DetachCurrentThread();
    }

    return result;
}

/**
 * \brief Query file permissions.
 * \details This opens the system dialog that lets the user
 *  grant (or deny) the permission.
 * \param[in] app a pointer to the android app.
 * \note Requires Android API level 23 (Marshmallow, May 2015)
 */
void android_request_file_permissions(struct android_app* app) {
    JavaVM* lJavaVM = app->activity->vm;
    JNIEnv* lJNIEnv = nullptr; 
    bool lThreadAttached = false;

    // Get JNIEnv from lJavaVM using GetEnv to test whether
    // thread is attached or not to the VM. If not, attach it
    // (and note that it will need to be detached at the end
    //  of the function).
    switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) {
      case JNI_OK:
        break;
      case JNI_EDETACHED: {
        jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr);
        if(lResult == JNI_ERR) {
          throw std::runtime_error("Could not attach current thread");
        }
        lThreadAttached = true;
      } break;
      case JNI_EVERSION:
        throw std::runtime_error("Invalid java version");
      }

    jobjectArray perm_array = lJNIEnv->NewObjectArray(
      2,
      lJNIEnv->FindClass("java/lang/String"),
      lJNIEnv->NewStringUTF("")
    );

    lJNIEnv->SetObjectArrayElement(
      perm_array, 0,
      android_permission_name(lJNIEnv, "READ_EXTERNAL_STORAGE")
    );

    lJNIEnv->SetObjectArrayElement(
      perm_array, 1,
      android_permission_name(lJNIEnv, "WRITE_EXTERNAL_STORAGE")        
    );

    jobject activity = app->activity->clazz;

    jclass ClassActivity = lJNIEnv->FindClass(
       "android/app/Activity"
    );

    jmethodID MethodrequestPermissions = lJNIEnv->GetMethodID(
       ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V"
    );

    // Last arg (0) is just for the callback (that I do not use)
    lJNIEnv->CallVoidMethod(
       activity, MethodrequestPermissions, perm_array, 0
    );

    if(lThreadAttached) {
       lJavaVM->DetachCurrentThread();
    }
}

void check_android_permissions(struct android_app* app) {
    bool OK = android_has_permission(
       app, "READ_EXTERNAL_STORAGE"
    ) && android_has_permission(
       app, "WRITE_EXTERNAL_STORAGE"
    );
    if(!OK) {
       android_request_file_permissions(app);
    }
}
    


Fri*_*esh 2

所以我也看到了这一点。

  1. 要手动执行此操作,您也可以通过命令行执行此操作adb shell pm grant com.package.name android.permission.CAMERA

  2. 检查是否有权限adb shell dumpsys package com.package.name

  3. 这是使用 Java 请求权限的一系列步骤

  4. 要弄清楚如何在没有 Java 的情况下做到这一点,您需要深入研究 AOSP 以了解它是如何在幕后工作的……这不是微不足道的,也没有找到任何示例。