diff --git a/BaseLibrary/build.gradle b/BaseLibrary/build.gradle index ebf9912..2c6c920 100644 --- a/BaseLibrary/build.gradle +++ b/BaseLibrary/build.gradle @@ -54,7 +54,7 @@ dependencies { api 'com.github.getActivity:Toaster:12.8' //权限请求 - api 'com.github.getActivity:XXPermissions:23.0' + api 'com.github.getActivity:XXPermissions:26.5' //RecyclerView api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.28' @@ -71,7 +71,7 @@ afterEvaluate { release(MavenPublication) { groupId = 'com.chuangketie.jk' artifactId = 'lib_base' - version = rootProject.maven_version.version + version = rootProject.maven_version.baselib_version artifact("$buildDir/outputs/aar/${project.name}-release.aar") // 添加依赖传递配置 diff --git a/BaseLibrary/src/main/java/com/tfq/library/base/BaseFragment.java b/BaseLibrary/src/main/java/com/tfq/library/base/BaseFragment.java index 9f54504..d572a96 100644 --- a/BaseLibrary/src/main/java/com/tfq/library/base/BaseFragment.java +++ b/BaseLibrary/src/main/java/com/tfq/library/base/BaseFragment.java @@ -1,30 +1,33 @@ package com.tfq.library.base; -import android.animation.ObjectAnimator; +import static com.tfq.library.app.BaseConstants.REQUEST_PER; +import static com.tfq.library.permission.PermissionUtils.convertToIPermissionArray; +import static com.tfq.library.permission.PermissionUtils.showGrantedPermissionsToast; + +import android.Manifest; +import android.content.Context; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; -import android.view.animation.AnimationUtils; - -import com.hjq.permissions.OnPermissionCallback; -import com.hjq.permissions.XXPermissions; -import com.tfq.library.R; -import com.tfq.library.app.BaseConstants; -import com.tfq.library.app.LibraryApp; -import com.tfq.library.view.Animation; -import com.tfq.library.view.AuthDialog; - -import java.util.List; +import android.widget.Toast; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import static com.tfq.library.app.BaseConstants.REQUEST_PER; +import com.hjq.permissions.OnPermissionCallback; +import com.hjq.permissions.XXPermissions; +import com.hjq.permissions.permission.base.IPermission; +import com.tfq.library.view.Animation; +import com.tfq.library.view.AuthDialog; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; /** * Created by JiangKe on 2025/02/20. @@ -115,29 +118,35 @@ public abstract class BaseFragment extends Fragment { } private void requestPermission(String[] permission, boolean show_doNotAskAgain, Listener listener) { + List iPermissionList = convertToIPermissionArray(requireActivity(),permission); + if (iPermissionList.isEmpty()){ + return; + } XXPermissions.with(getActivity()) - .permission(permission) + .permissions(iPermissionList) .request(new OnPermissionCallback() { @Override - public void onGranted(@NonNull List permissions, boolean allGranted) { + public void onResult(@NonNull List grantedList, @NonNull List deniedList) { + boolean allGranted = deniedList.isEmpty(); if (!allGranted) { - //获取部分权限成功,但部分权限未正常授予 return; } listener.success(); + showGrantedPermissionsToast(requireActivity(),grantedList); } - @Override - public void onDenied(@NonNull List permissions, boolean doNotAskAgain) { - if (show_doNotAskAgain) { - if (doNotAskAgain) { - //如果是被永久拒绝就跳转到应用权限系统设置页面 - XXPermissions.startPermissionActivity(getActivity(), permissions); - } else { - //获取权限失败 - } - } - } + +// @Override +// public void onDenied(@NonNull List permissions, boolean doNotAskAgain) { +// if (show_doNotAskAgain) { +// if (doNotAskAgain) { +// //如果是被永久拒绝就跳转到应用权限系统设置页面 +// XXPermissions.startPermissionActivity(getActivity(), iPermissionList); +// } else { +// //获取权限失败 +// } +// } +// } }); } diff --git a/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionConverter.java b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionConverter.java new file mode 100644 index 0000000..42543c5 --- /dev/null +++ b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionConverter.java @@ -0,0 +1,333 @@ +package com.tfq.library.permission; + +import android.content.Context; +import android.os.Build; +import android.text.TextUtils; + +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hjq.permissions.permission.PermissionGroups; +import com.hjq.permissions.permission.PermissionNames; +import com.hjq.permissions.permission.base.IPermission; +import com.tfq.library.R; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/XXPermissions + * time : 2025/05/30 + * desc : 权限转换器(根据权限获取对应的名称和说明) + */ +public final class PermissionConverter { + + /** + * 权限名称映射(为了适配多语种,这里存储的是 StringId,而不是 String) + */ + private static final Map PERMISSION_NAME_MAP = new HashMap<>(); + + /** + * 权限描述映射(为了适配多语种,这里存储的是 StringId,而不是 String) + */ + private static final Map PERMISSION_DESCRIPTION_MAP = new HashMap<>(); + + static { + PERMISSION_NAME_MAP.put(PermissionGroups.STORAGE, R.string.common_permission_storage); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_storage, R.string.common_permission_storage_description); + + PERMISSION_NAME_MAP.put(PermissionGroups.IMAGE_AND_VIDEO_MEDIA, R.string.common_permission_image_and_video); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_image_and_video, R.string.common_permission_image_and_video_description); + + PERMISSION_NAME_MAP.put(PermissionNames.READ_MEDIA_AUDIO, R.string.common_permission_music_and_audio); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_music_and_audio, R.string.common_permission_music_and_audio_description); + + PERMISSION_NAME_MAP.put(PermissionNames.CAMERA, R.string.common_permission_camera); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_camera, R.string.common_permission_camera_description); + + PERMISSION_NAME_MAP.put(PermissionNames.RECORD_AUDIO, R.string.common_permission_microphone); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_microphone, R.string.common_permission_microphone_description); + + PERMISSION_NAME_MAP.put(PermissionGroups.NEARBY_DEVICES, R.string.common_permission_nearby_devices); + // 注意:在 Android 13 的时候,WIFI 相关的权限已经归到附近设备的权限组了,但是在 Android 13 之前,WIFI 相关的权限归属定位权限组 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // 需要填充文案:蓝牙权限描述 + WIFI 权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_nearby_devices, R.string.common_permission_nearby_devices_description); + } else { + // 需要填充文案:蓝牙权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_nearby_devices, R.string.common_permission_nearby_devices_description); + } + + PERMISSION_NAME_MAP.put(PermissionGroups.LOCATION, R.string.common_permission_location); + // 注意:在 Android 12 的时候,蓝牙相关的权限已经归到附近设备的权限组了,但是在 Android 12 之前,蓝牙相关的权限归属定位权限组 + // 注意:在 Android 13 的时候,WIFI 相关的权限已经归到附近设备的权限组了,但是在 Android 13 之前,WIFI 相关的权限归属定位权限组 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // 需要填充文案:前台定位权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_location, R.string.common_permission_location_description); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // 需要填充文案:前台定位权限描述 + WIFI 权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_location, R.string.common_permission_location_description); + } else { + // 需要填充文案:前台定位权限描述 + 蓝牙权限描述 + WIFI 权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_location, R.string.common_permission_location_description); + } + + // 后台定位权限虽然属于定位权限组,但是只要是属于后台权限,都有独属于自己的一套规则 + PERMISSION_NAME_MAP.put(PermissionNames.ACCESS_BACKGROUND_LOCATION, R.string.common_permission_location_background); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_location_background, R.string.common_permission_location_background_description); + + int sensorsPermissionNameStringId; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { + sensorsPermissionNameStringId = R.string.common_permission_health_data; + } else { + sensorsPermissionNameStringId = R.string.common_permission_body_sensors; + } + PERMISSION_NAME_MAP.put(PermissionGroups.SENSORS, sensorsPermissionNameStringId); + PERMISSION_DESCRIPTION_MAP.put(sensorsPermissionNameStringId, R.string.common_permission_body_sensors_description); + + // 后台传感器权限虽然属于传感器权限组,但是只要是属于后台权限,都有独属于自己的一套规则 + int bodySensorsBackgroundPermissionNameStringId; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { + bodySensorsBackgroundPermissionNameStringId = R.string.common_permission_health_data_background; + } else { + bodySensorsBackgroundPermissionNameStringId = R.string.common_permission_body_sensors_background; + } + PERMISSION_NAME_MAP.put(PermissionNames.BODY_SENSORS_BACKGROUND, bodySensorsBackgroundPermissionNameStringId); + PERMISSION_DESCRIPTION_MAP.put(bodySensorsBackgroundPermissionNameStringId, R.string.common_permission_body_sensors_background_description); + + // Android 16 这个版本开始,传感器权限被进行了精细化拆分,拆分成了无数个健康权限 + PERMISSION_NAME_MAP.put(PermissionGroups.HEALTH, R.string.common_permission_health_data); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_health_data, R.string.common_permission_health_data_description); + + PERMISSION_NAME_MAP.put(PermissionNames.READ_HEALTH_DATA_IN_BACKGROUND, R.string.common_permission_health_data_background); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_health_data_background, R.string.common_permission_health_data_background_description); + + PERMISSION_NAME_MAP.put(PermissionNames.READ_HEALTH_DATA_HISTORY, R.string.common_permission_health_data_past); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_health_data_past, R.string.common_permission_health_data_past_description); + + PERMISSION_NAME_MAP.put(PermissionGroups.CALL_LOG, R.string.common_permission_call_logs); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_call_logs, R.string.common_permission_call_logs_description); + + PERMISSION_NAME_MAP.put(PermissionGroups.PHONE, R.string.common_permission_phone); + // 注意:在 Android 9.0 的时候,读写通话记录权限已经归到一个单独的权限组了,但是在 Android 9.0 之前,读写通话记录权限归属电话权限组 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // 需要填充文案:电话权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_phone, R.string.common_permission_phone_description); + } else { + // 需要填充文案:电话权限描述 + 通话记录权限描述 + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_phone, R.string.common_permission_phone_description); + } + + PERMISSION_NAME_MAP.put(PermissionGroups.CONTACTS, R.string.common_permission_contacts); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_contacts, R.string.common_permission_contacts_description); + + PERMISSION_NAME_MAP.put(PermissionGroups.CALENDAR, R.string.common_permission_calendar); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_calendar, R.string.common_permission_calendar_description); + + // 注意:在 Android 10 的版本,这个权限的名称为《健身运动权限》,但是到了 Android 11 的时候,这个权限的名称被修改成了《身体活动权限》 + // 没错就改了一下权限的叫法,其他的一切没有变,Google 产品经理真的是闲的蛋疼,但是吐槽归吐槽,框架也要灵活应对一下,避免小白用户跳转到设置页找不到对应的选项 + int activityRecognitionPermissionNameStringId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ? R.string.common_permission_activity_recognition_api30 : R.string.common_permission_activity_recognition_api29; + PERMISSION_NAME_MAP.put(PermissionNames.ACTIVITY_RECOGNITION, activityRecognitionPermissionNameStringId); + PERMISSION_DESCRIPTION_MAP.put(activityRecognitionPermissionNameStringId, R.string.common_permission_activity_recognition_description); + + PERMISSION_NAME_MAP.put(PermissionNames.ACCESS_MEDIA_LOCATION, R.string.common_permission_access_media_location_information); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_access_media_location_information, R.string.common_permission_access_media_location_information_description); + + PERMISSION_NAME_MAP.put(PermissionGroups.SMS, R.string.common_permission_sms); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_sms, R.string.common_permission_sms_description); + + PERMISSION_NAME_MAP.put(PermissionNames.GET_INSTALLED_APPS, R.string.common_permission_get_installed_apps); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_get_installed_apps, R.string.common_permission_get_installed_apps_description); + + PERMISSION_NAME_MAP.put(PermissionNames.MANAGE_EXTERNAL_STORAGE, R.string.common_permission_all_file_access); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_all_file_access, R.string.common_permission_all_file_access_description); + + PERMISSION_NAME_MAP.put(PermissionNames.REQUEST_INSTALL_PACKAGES, R.string.common_permission_install_unknown_apps); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_install_unknown_apps, R.string.common_permission_install_unknown_apps_description); + + PERMISSION_NAME_MAP.put(PermissionNames.SYSTEM_ALERT_WINDOW, R.string.common_permission_display_over_other_apps); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_display_over_other_apps, R.string.common_permission_display_over_other_apps_description); + + PERMISSION_NAME_MAP.put(PermissionNames.WRITE_SETTINGS, R.string.common_permission_modify_system_settings); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_modify_system_settings, R.string.common_permission_modify_system_settings_description); + + PERMISSION_NAME_MAP.put(PermissionNames.NOTIFICATION_SERVICE, R.string.common_permission_allow_notifications); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_allow_notifications, R.string.common_permission_allow_notifications_description); + + PERMISSION_NAME_MAP.put(PermissionNames.POST_NOTIFICATIONS, R.string.common_permission_post_notifications); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_post_notifications, R.string.common_permission_post_notifications_description); + + PERMISSION_NAME_MAP.put(PermissionNames.BIND_NOTIFICATION_LISTENER_SERVICE, R.string.common_permission_allow_notifications_access); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_allow_notifications_access, R.string.common_permission_allow_notifications_access_description); + + PERMISSION_NAME_MAP.put(PermissionNames.PACKAGE_USAGE_STATS, R.string.common_permission_apps_with_usage_access); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_apps_with_usage_access, R.string.common_permission_apps_with_usage_access_description); + + PERMISSION_NAME_MAP.put(PermissionNames.SCHEDULE_EXACT_ALARM, R.string.common_permission_alarms_reminders); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_alarms_reminders, R.string.common_permission_alarms_reminders_description); + + PERMISSION_NAME_MAP.put(PermissionNames.ACCESS_NOTIFICATION_POLICY, R.string.common_permission_do_not_disturb_access); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_do_not_disturb_access, R.string.common_permission_do_not_disturb_access_description); + + PERMISSION_NAME_MAP.put(PermissionNames.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, R.string.common_permission_ignore_battery_optimize); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_ignore_battery_optimize, R.string.common_permission_ignore_battery_optimize_description); + + PERMISSION_NAME_MAP.put(PermissionNames.BIND_VPN_SERVICE, R.string.common_permission_vpn); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_vpn, R.string.common_permission_vpn_description); + + PERMISSION_NAME_MAP.put(PermissionNames.PICTURE_IN_PICTURE, R.string.common_permission_picture_in_picture); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_picture_in_picture, R.string.common_permission_picture_in_picture_description); + + PERMISSION_NAME_MAP.put(PermissionNames.USE_FULL_SCREEN_INTENT, R.string.common_permission_full_screen_notifications); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_full_screen_notifications, R.string.common_permission_full_screen_notifications_description); + + PERMISSION_NAME_MAP.put(PermissionNames.BIND_DEVICE_ADMIN, R.string.common_permission_device_admin); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_device_admin, R.string.common_permission_device_admin_description); + + PERMISSION_NAME_MAP.put(PermissionNames.BIND_ACCESSIBILITY_SERVICE, R.string.common_permission_accessibility_service); + PERMISSION_DESCRIPTION_MAP.put(R.string.common_permission_accessibility_service, R.string.common_permission_accessibility_service_description); + } + + /** + * 通过权限获得名称 + */ + @NonNull + public static String getNickNamesByPermissions(@NonNull Context context, @NonNull List permissions) { + List permissionNameList = getNickNameListByPermissions(context, permissions, true); + + StringBuilder builder = new StringBuilder(); + for (String permissionName : permissionNameList) { + if (TextUtils.isEmpty(permissionName)) { + continue; + } + if (builder.length() == 0) { + builder.append(permissionName); + } else { + builder.append(context.getString(R.string.common_permission_comma)) + .append(permissionName); + } + } + if (builder.length() == 0) { + // 如果没有获得到任何信息,则返回一个默认的文本 + return context.getString(R.string.common_permission_unknown); + } + return builder.toString(); + } + + @NonNull + public static List getNickNameListByPermissions(@NonNull Context context, @NonNull List permissions, boolean filterHighVersionPermissions) { + List permissionNickNameList = new ArrayList<>(); + for (IPermission permission : permissions) { + // 如果当前设置了过滤高版本权限,并且这个权限是高版本系统才出现的权限,则不继续往下执行 + // 避免出现在低版本上面执行拒绝权限后,连带高版本的名称也一起显示出来,但是在低版本上面是没有这个权限的 + if (filterHighVersionPermissions && permission.getFromAndroidVersion(context) > Build.VERSION.SDK_INT) { + continue; + } + String permissionName = getNickNameByPermission(context, permission); + if (TextUtils.isEmpty(permissionName)) { + continue; + } + if (permissionNickNameList.contains(permissionName)) { + continue; + } + permissionNickNameList.add(permissionName); + } + return permissionNickNameList; + } + + public static String getNickNameByPermission(@NonNull Context context, @NonNull IPermission permission) { + Integer permissionNameStringId = getPermissionNickNameStringId(context, permission); + if (permissionNameStringId == null || permissionNameStringId == 0) { + return ""; + } + return context.getString(permissionNameStringId); + } + + /** + * 通过权限获得描述 + */ + @NonNull + public static String getDescriptionsByPermissions(@NonNull Context context, @NonNull List permissions) { + List descriptionList = getDescriptionListByPermissions(context, permissions); + + StringBuilder builder = new StringBuilder(); + for (String description : descriptionList) { + if (TextUtils.isEmpty(description)) { + continue; + } + if (builder.length() == 0) { + builder.append(description); + } else { + builder.append("\n") + .append(description); + } + } + return builder.toString(); + } + + @NonNull + public static List getDescriptionListByPermissions(@NonNull Context context, @NonNull List permissions) { + List descriptionList = new ArrayList<>(); + for (IPermission permission : permissions) { + String permissionDescription = getDescriptionByPermission(context, permission); + if (TextUtils.isEmpty(permissionDescription)) { + continue; + } + if (descriptionList.contains(permissionDescription)) { + continue; + } + descriptionList.add(permissionDescription); + } + return descriptionList; + } + + /** + * 通过权限获得描述 + */ + @NonNull + public static String getDescriptionByPermission(@NonNull Context context, @NonNull IPermission permission) { + Integer permissionNameStringId = getPermissionNickNameStringId(context, permission); + if (permissionNameStringId == null || permissionNameStringId == 0) { + return ""; + } + String permissionNickName = context.getString(permissionNameStringId); + Integer permissionDescriptionStringId = getPermissionDescriptionStringId(permissionNameStringId); + String permissionDescription; + if (permissionDescriptionStringId == null || permissionDescriptionStringId == 0) { + permissionDescription = ""; + } else { + permissionDescription = context.getString(permissionDescriptionStringId); + } + return permissionNickName + context.getString(R.string.common_permission_colon) + permissionDescription; + } + + /** + * 获取这个权限对应的别名 StringId + */ + @Nullable + public static Integer getPermissionNickNameStringId(@NonNull Context context, @NonNull IPermission permission) { + String permissionName = permission.getPermissionName(); + String permissionGroup = permission.getPermissionGroup(context); + Integer permissionNameStringId = PERMISSION_NAME_MAP.get(permissionName); + if (permissionNameStringId != null && permissionNameStringId > 0) { + return permissionNameStringId; + } + Integer permissionGroupStringId = PERMISSION_NAME_MAP.get(permissionGroup); + if (permissionGroupStringId != null && permissionGroupStringId > 0) { + return permissionGroupStringId; + } + return permissionNameStringId; + } + + /** + * 获取这个权限对应的描述 StringId + */ + @Nullable + public static Integer getPermissionDescriptionStringId(@IdRes int permissionNickNameStringId) { + return PERMISSION_DESCRIPTION_MAP.get(permissionNickNameStringId); + } +} \ No newline at end of file diff --git a/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionDescription.java b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionDescription.java new file mode 100644 index 0000000..388f750 --- /dev/null +++ b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionDescription.java @@ -0,0 +1,270 @@ +package com.tfq.library.permission; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.drawable.ColorDrawable; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hjq.permissions.OnPermissionDescription; +import com.hjq.permissions.permission.PermissionPageType; +import com.hjq.permissions.permission.base.IPermission; +import com.tfq.library.R; +import com.tfq.library.app.BaseConstants; +import com.tfq.library.view.AuthDialog; + +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/XXPermissions + * time : 2025/05/30 + * desc : 权限请求描述实现 + */ +public final class PermissionDescription implements OnPermissionDescription { + + /** + * 消息处理 Handler 对象 + */ + public static final Handler HANDLER = new Handler(Looper.getMainLooper()); + + /** + * 权限请求描述弹窗显示类型:Dialog + */ + private static final int DESCRIPTION_WINDOW_TYPE_DIALOG = 0; + /** + * 权限请求描述弹窗显示类型:PopupWindow + */ + private static final int DESCRIPTION_WINDOW_TYPE_POPUP = 1; + + /** + * 权限请求描述弹窗显示类型 + */ + private int mDescriptionWindowType = DESCRIPTION_WINDOW_TYPE_DIALOG; + + /** + * 消息 Token + */ + @NonNull + private final Object mHandlerToken = new Object(); + + /** + * 权限申请说明弹窗 + */ + @Nullable + private PopupWindow mPermissionPopupWindow; + + /** + * 权限申请说明对话框 + */ + @Nullable + private Dialog mPermissionDialog; + private AuthDialog mAuthDialog; + + @Override + public void askWhetherRequestPermission(@NonNull Activity activity, + @NonNull List requestList, + @NonNull Runnable continueRequestRunnable, + @NonNull Runnable breakRequestRunnable) { + // 以下情况使用 Dialog 来展示权限说明弹窗,否则使用 PopupWindow 来展示权限说明弹窗 + // 1. 如果请求的权限显示的系统界面是不透明的 Activity + // 2. 如果当前 Activity 的屏幕是竖屏的话,并且设备的物理屏幕尺寸还小于 9 寸(目前大多数小屏平板大多数集中在 8、8.7、8.8、10 寸) + // 实测 8 寸的平板获取到的物理尺寸到只有 7.958788793906728,所以这里的代码判断基本上是针对 10 寸及以上的平板做优化 + if (isActivityLandscape(activity) && getPhysicalScreenSize(activity) < 9) { + mDescriptionWindowType = DESCRIPTION_WINDOW_TYPE_DIALOG; + } else { + mDescriptionWindowType = DESCRIPTION_WINDOW_TYPE_POPUP; + for (IPermission permission : requestList) { + if (permission.getPermissionPageType(activity) == PermissionPageType.OPAQUE_ACTIVITY) { + mDescriptionWindowType = DESCRIPTION_WINDOW_TYPE_DIALOG; + } + } + } + + if (mDescriptionWindowType == DESCRIPTION_WINDOW_TYPE_POPUP) { + continueRequestRunnable.run(); + return; + } + + String dialogTitle = activity.getString(R.string.common_permission_description_title); + String dialogMessage = generatePermissionDescription(activity, requestList); + String confirmButtonText = activity.getString(R.string.common_permission_confirm); + + showDialog(activity, dialogTitle, dialogMessage, confirmButtonText, continueRequestRunnable); + } + + @Override + public void onRequestPermissionStart(@NonNull Activity activity, @NonNull List requestList) { + if (mDescriptionWindowType != DESCRIPTION_WINDOW_TYPE_POPUP) { + return; + } + + Runnable showPopupRunnable = () -> showPopupWindow(activity, generatePermissionDescription(activity, requestList)); + // 这里解释一下为什么要延迟一段时间再显示 PopupWindow,这是因为系统没有开放任何 API 给外层直接获取权限是否永久拒绝 + // 目前只有申请过了权限才能通过 shouldShowRequestPermissionRationale 判断是不是永久拒绝,如果此前没有申请过权限,则无法判断 + // 针对这个问题能想到最佳的解决方案是:先申请权限,如果极短的时间内,权限申请没有结束,则证明权限之前没有被用户勾选了《不再询问》 + // 此时系统的权限弹窗正在显示给用户,这个时候再去显示应用的 PopupWindow 权限说明弹窗给用户看,所以这个 PopupWindow 是在发起权限申请后才显示的 + // 这样做是为了避免 PopupWindow 显示了又马上消失,这样就不会出现 PopupWindow 一闪而过的效果,提升用户的视觉体验 + // 最后补充一点:350 毫秒只是一个经验值,经过测试可覆盖大部分机型,具体可根据实际情况进行调整,这里不做强制要求 + // 相关 Github issue 地址:https://github.com/getActivity/XXPermissions/issues/366 + HANDLER.postAtTime(showPopupRunnable, mHandlerToken, SystemClock.uptimeMillis() + 350); + } + + @Override + public void onRequestPermissionEnd(@NonNull Activity activity, @NonNull List requestList) { + // 移除跟这个 Token 有关但是没有还没有执行的消息 + HANDLER.removeCallbacksAndMessages(mHandlerToken); + // 销毁当前正在显示的弹窗 + dismissPopupWindow(); + dismissDialog(); + } + + /** + * 生成权限描述文案 + */ + private String generatePermissionDescription(@NonNull Activity activity, @NonNull List requestList) { + return PermissionConverter.getDescriptionsByPermissions(activity, requestList); + } + + /** + * 显示 Dialog + * + * @param dialogTitle 对话框标题 + * @param dialogMessage 对话框消息 + * @param confirmButtonText 对话框确认按钮文本 + * @param continueRequestRunnable 执行继续请求权限的操作 + */ + private void showDialog(@NonNull Activity activity, @Nullable String dialogTitle, @Nullable String dialogMessage, + @Nullable String confirmButtonText, @Nullable Runnable continueRequestRunnable) { + if (mAuthDialog != null) { + dismissDialog(); + } + if (activity.isFinishing() || activity.isDestroyed()) { + return; + } + + mAuthDialog = new AuthDialog(activity, null, dialogTitle, dialogMessage, null, confirmButtonText, new AuthDialog.Listener() { + @Override + public void callBack() { + assert continueRequestRunnable != null; + continueRequestRunnable.run(); + } + }); + // 这里需要设置成可取消的,这样用户不想授权了,随时可以点击返回键或者对话框蒙层来取消显示 Dialog + mAuthDialog.setCanceledOnTouchOutside(true); + mAuthDialog.setCancelable(true); + + mAuthDialog.show(); + // 将 Activity 和 Dialog 生命周期绑定在一起,避免可能会出现的内存泄漏 + // 当然如果上面创建的 Dialog 已经有做了生命周期管理,则不需要执行下面这行代码 + WindowLifecycleManager.bindDialogLifecycle(activity, mAuthDialog); + } + + /** + * 销毁 Dialog + */ + private void dismissDialog() { + if (mAuthDialog == null) { + return; + } + if (!mAuthDialog.isShowing()) { + return; + } + mAuthDialog.dismiss(); + mAuthDialog = null; + } + + /** + * 显示 PopupWindow + * + * @param content 弹窗显示的内容 + */ + private void showPopupWindow(@NonNull Activity activity, @NonNull String content) { + if (mPermissionPopupWindow != null) { + dismissPopupWindow(); + } + if (activity.isFinishing() || activity.isDestroyed()) { + return; + } + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View contentView = LayoutInflater.from(activity) + .inflate(R.layout.permission_description_popup, decorView, false); + mPermissionPopupWindow = new PopupWindow(activity); + mPermissionPopupWindow.setContentView(contentView); + mPermissionPopupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT); + mPermissionPopupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); + mPermissionPopupWindow.setAnimationStyle(android.R.style.Animation_Dialog); + mPermissionPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + mPermissionPopupWindow.setTouchable(true); + mPermissionPopupWindow.setOutsideTouchable(true); + TextView messageView = mPermissionPopupWindow.getContentView().findViewById(R.id.tv_permission_description_message); + messageView.setText(content); + mPermissionPopupWindow.showAtLocation(decorView, Gravity.TOP, 0, 0); + // 将 Activity 和 PopupWindow 生命周期绑定在一起,避免可能会出现的内存泄漏 + // 当然如果上面创建的 PopupWindow 已经有做了生命周期管理,则不需要执行下面这行代码 + WindowLifecycleManager.bindPopupWindowLifecycle(activity, mPermissionPopupWindow); + } + + /** + * 销毁 PopupWindow + */ + private void dismissPopupWindow() { + if (mPermissionPopupWindow == null) { + return; + } + if (!mPermissionPopupWindow.isShowing()) { + return; + } + mPermissionPopupWindow.dismiss(); + mPermissionPopupWindow = null; + } + + /** + * 判断当前 Activity 是否是横盘显示 + */ + public static boolean isActivityLandscape(@NonNull Activity activity) { + return activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } + + /** + * 获取当前设备的物理屏幕尺寸 + */ + @SuppressWarnings("deprecation") + public static double getPhysicalScreenSize(@NonNull Context context) { + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display defaultDisplay = windowManager.getDefaultDisplay(); + if (defaultDisplay == null) { + return 0; + } + + DisplayMetrics metrics = new DisplayMetrics(); + defaultDisplay.getMetrics(metrics); + + float screenWidthInInches; + float screenHeightInInches; + Point point = new Point(); + defaultDisplay.getRealSize(point); + screenWidthInInches = point.x / metrics.xdpi; + screenHeightInInches = point.y / metrics.ydpi; + + // 勾股定理:直角三角形的两条直角边的平方和等于斜边的平方 + return Math.sqrt(Math.pow(screenWidthInInches, 2) + Math.pow(screenHeightInInches, 2)); + } +} \ No newline at end of file diff --git a/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionInterceptor.java b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionInterceptor.java new file mode 100644 index 0000000..299f71e --- /dev/null +++ b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionInterceptor.java @@ -0,0 +1,252 @@ +package com.tfq.library.permission; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.hjq.permissions.OnPermissionCallback; +import com.hjq.permissions.OnPermissionInterceptor; +import com.hjq.permissions.XXPermissions; +import com.hjq.permissions.permission.PermissionGroups; +import com.hjq.permissions.permission.PermissionNames; +import com.hjq.permissions.permission.base.IPermission; +import com.hjq.toast.Toaster; +import com.tfq.library.R; +import com.tfq.library.app.BaseConstants; +import com.tfq.library.view.AuthDialog; + +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/XXPermissions + * time : 2021/01/04 + * desc : 权限申请拦截器 + */ +public final class PermissionInterceptor implements OnPermissionInterceptor { + + @Override + public void onRequestPermissionEnd(@NonNull Activity activity, boolean skipRequest, + @NonNull List requestList, + @NonNull List grantedList, + @NonNull List deniedList, + @Nullable OnPermissionCallback callback) { + if (callback != null) { + callback.onResult(grantedList, deniedList); + } + + if (deniedList.isEmpty()) { + return; + } + boolean doNotAskAgain = XXPermissions.isDoNotAskAgainPermissions(activity, deniedList); + String permissionHint = generatePermissionHint(activity, deniedList, doNotAskAgain); + if (!doNotAskAgain) { + // 如果没有勾选不再询问选项,就弹 Toast 提示给用户 + Toaster.show(permissionHint); + return; + } + + // 如果勾选了不再询问选项,就弹 Dialog 引导用户去授权 + showPermissionSettingDialog(activity, requestList, deniedList, callback, permissionHint); + } + + private void showPermissionSettingDialog(@NonNull Activity activity, + @NonNull List requestList, + @NonNull List deniedList, + @Nullable OnPermissionCallback callback, + @NonNull String permissionHint) { + if (activity.isFinishing() || activity.isDestroyed()) { + return; + } + + String dialogTitle = activity.getString(R.string.common_permission_alert); + String confirmButtonText = activity.getString(R.string.common_permission_go_to_authorization); + + AuthDialog mAuthDialog = new AuthDialog(activity, null, dialogTitle, permissionHint, null, confirmButtonText, new AuthDialog.Listener() { + @Override + public void callBack() { + XXPermissions.startPermissionActivity(activity, deniedList, new OnPermissionCallback() { + @Override + public void onResult(@NonNull List grantedList, @NonNull List deniedList) { + List latestDeniedList = XXPermissions.getDeniedPermissions(activity, requestList); + boolean allGranted = latestDeniedList.isEmpty(); + if (!allGranted) { + // 递归显示对话框,让提示用户授权,只不过对话框是可取消的,用户不想授权了,随时可以点击返回键或者对话框蒙层来取消显示 + showPermissionSettingDialog(activity, requestList, latestDeniedList, callback, + generatePermissionHint(activity, latestDeniedList, true)); + return; + } + + if (callback == null) { + return; + } + // 用户全部授权了,回调成功给外层监听器,免得用户还要再发起权限申请 + callback.onResult(requestList, latestDeniedList); + } + }); + } + }); + // 这里需要设置成可取消的,这样用户不想授权了,随时可以点击返回键或者对话框蒙层来取消显示 Dialog + mAuthDialog.setCanceledOnTouchOutside(true); + mAuthDialog.setCancelable(true); + + mAuthDialog.show(); + // 将 Activity 和 Dialog 生命周期绑定在一起,避免可能会出现的内存泄漏 + // 当然如果上面创建的 Dialog 已经有做了生命周期管理,则不需要执行下面这行代码 + WindowLifecycleManager.bindDialogLifecycle(activity, mAuthDialog); + } + + + /** + * 生成权限提示文案 + */ + @NonNull + private String generatePermissionHint(@NonNull Activity activity, @NonNull List deniedList, boolean doNotAskAgain) { + int deniedPermissionCount = deniedList.size(); + int deniedLocationPermissionCount = 0; + int deniedSensorsPermissionCount = 0; + int deniedHealthPermissionCount = 0; + for (IPermission deniedPermission : deniedList) { + String permissionGroup = deniedPermission.getPermissionGroup(activity); + if (TextUtils.isEmpty(permissionGroup)) { + continue; + } + if (PermissionGroups.LOCATION.equals(permissionGroup)) { + deniedLocationPermissionCount++; + } else if (PermissionGroups.SENSORS.equals(permissionGroup)) { + deniedSensorsPermissionCount++; + } else if (XXPermissions.isHealthPermission(deniedPermission)) { + deniedHealthPermissionCount++; + } + } + + if (deniedLocationPermissionCount == deniedPermissionCount && VERSION.SDK_INT >= VERSION_CODES.Q) { + if (deniedLocationPermissionCount == 1) { + if (XXPermissions.equalsPermission(deniedList.get(0), PermissionNames.ACCESS_BACKGROUND_LOCATION)) { + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_location_background), + getBackgroundPermissionOptionLabel(activity)); + } else if (VERSION.SDK_INT >= VERSION_CODES.S && + XXPermissions.equalsPermission(deniedList.get(0), PermissionNames.ACCESS_FINE_LOCATION)) { + // 如果请求的定位权限中,既包含了精确定位权限,又包含了模糊定位权限或者后台定位权限, + // 但是用户只同意了模糊定位权限的情况或者后台定位权限,并没有同意精确定位权限的情况,就提示用户开启确切位置选项 + // 需要注意的是 Android 12 才将模糊定位权限和精确定位权限的授权选项进行分拆,之前的版本没有区分得那么仔细 + return activity.getString(R.string.common_permission_fail_hint_3, + activity.getString(R.string.common_permission_location_fine), + activity.getString(R.string.common_permission_location_fine_option)); + } + } else { + if (XXPermissions.containsPermission(deniedList, PermissionNames.ACCESS_BACKGROUND_LOCATION)) { + if (VERSION.SDK_INT >= VERSION_CODES.S && + XXPermissions.containsPermission(deniedList, PermissionNames.ACCESS_FINE_LOCATION)) { + return activity.getString(R.string.common_permission_fail_hint_2, + activity.getString(R.string.common_permission_location), + getBackgroundPermissionOptionLabel(activity), + activity.getString(R.string.common_permission_location_fine_option)); + } else { + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_location), + getBackgroundPermissionOptionLabel(activity)); + } + } + } + } else if (deniedSensorsPermissionCount == deniedPermissionCount && VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + if (deniedPermissionCount == 1) { + if (XXPermissions.equalsPermission(deniedList.get(0), PermissionNames.BODY_SENSORS_BACKGROUND)) { + if (VERSION.SDK_INT >= VERSION_CODES.BAKLAVA) { + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_health_data_background), + activity.getString(R.string.common_permission_health_data_background_option)); + } else { + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_body_sensors_background), + getBackgroundPermissionOptionLabel(activity)); + } + } + } else { + if (doNotAskAgain) { + if (VERSION.SDK_INT >= VERSION_CODES.BAKLAVA) { + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_health_data), + activity.getString(R.string.common_permission_allow_all_option)); + } else { + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_body_sensors), + getBackgroundPermissionOptionLabel(activity)); + } + } + } + } else if (deniedHealthPermissionCount == deniedPermissionCount && VERSION.SDK_INT >= VERSION_CODES.BAKLAVA) { + + switch (deniedPermissionCount) { + case 1: + if (XXPermissions.equalsPermission(deniedList.get(0), PermissionNames.READ_HEALTH_DATA_IN_BACKGROUND)) { + return activity.getString(R.string.common_permission_fail_hint_3, + activity.getString(R.string.common_permission_health_data_background), + activity.getString(R.string.common_permission_health_data_background_option)); + } else if (XXPermissions.equalsPermission(deniedList.get(0), PermissionNames.READ_HEALTH_DATA_HISTORY)) { + return activity.getString(R.string.common_permission_fail_hint_3, + activity.getString(R.string.common_permission_health_data_past), + activity.getString(R.string.common_permission_health_data_past_option)); + } + break; + case 2: + if (XXPermissions.containsPermission(deniedList, PermissionNames.READ_HEALTH_DATA_HISTORY) && + XXPermissions.containsPermission(deniedList, PermissionNames.READ_HEALTH_DATA_IN_BACKGROUND)) { + return activity.getString(R.string.common_permission_fail_hint_3, + activity.getString(R.string.common_permission_health_data_past) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_background), + activity.getString(R.string.common_permission_health_data_past_option) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_background_option)); + } else if (XXPermissions.containsPermission(deniedList, PermissionNames.READ_HEALTH_DATA_HISTORY)) { + return activity.getString(R.string.common_permission_fail_hint_2, + activity.getString(R.string.common_permission_health_data) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_past), + activity.getString(R.string.common_permission_allow_all_option), + activity.getString(R.string.common_permission_health_data_background_option)); + } else if (XXPermissions.containsPermission(deniedList, PermissionNames.READ_HEALTH_DATA_IN_BACKGROUND)) { + return activity.getString(R.string.common_permission_fail_hint_2, + activity.getString(R.string.common_permission_health_data) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_background), + activity.getString(R.string.common_permission_allow_all_option), + activity.getString(R.string.common_permission_health_data_background_option)); + } + break; + default: + if (XXPermissions.containsPermission(deniedList, PermissionNames.READ_HEALTH_DATA_HISTORY) && + XXPermissions.containsPermission(deniedList, PermissionNames.READ_HEALTH_DATA_IN_BACKGROUND)) { + return activity.getString(R.string.common_permission_fail_hint_2, + activity.getString(R.string.common_permission_health_data) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_past) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_background), + activity.getString(R.string.common_permission_allow_all_option), + activity.getString(R.string.common_permission_health_data_past_option) + activity.getString(R.string.common_permission_and) + activity.getString(R.string.common_permission_health_data_background_option)); + } + break; + } + return activity.getString(R.string.common_permission_fail_hint_1, + activity.getString(R.string.common_permission_health_data), + activity.getString(R.string.common_permission_allow_all_option)); + } + + return activity.getString(doNotAskAgain ? R.string.common_permission_fail_assign_hint_1 : + R.string.common_permission_fail_assign_hint_2, + PermissionConverter.getNickNamesByPermissions(activity, deniedList)); + } + + /** + * 获取后台权限的《始终允许》选项的文案 + */ + @NonNull + private String getBackgroundPermissionOptionLabel(Context context) { + PackageManager packageManager = context.getPackageManager(); + if (packageManager != null && VERSION.SDK_INT >= VERSION_CODES.R) { + CharSequence backgroundPermissionOptionLabel = packageManager.getBackgroundPermissionOptionLabel(); + if (!TextUtils.isEmpty(backgroundPermissionOptionLabel)) { + return backgroundPermissionOptionLabel.toString(); + } + } + + return context.getString(R.string.common_permission_allow_all_the_time_option); + } +} \ No newline at end of file diff --git a/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionUtils.java b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionUtils.java new file mode 100644 index 0000000..9b93be1 --- /dev/null +++ b/BaseLibrary/src/main/java/com/tfq/library/permission/PermissionUtils.java @@ -0,0 +1,87 @@ +package com.tfq.library.permission; + +import android.content.Context; +import android.util.Log; + +import com.hjq.permissions.permission.base.IPermission; +import com.hjq.toast.Toaster; +import com.tfq.library.R; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class PermissionUtils { + + private static IPermission invokeGetCachePermission(Context context, String permission) { + try { + Class xxPermissionsClass = Class.forName("com.hjq.permissions.XXPermissions"); + // 尝试获取方法,可能需要调整方法名和参数 + Method method = null; + try { + // 尝试获取静态方法 + method = xxPermissionsClass.getDeclaredMethod("getCachePermission", Context.class, String.class); + } catch (NoSuchMethodException e1) { + try { + // 尝试获取实例方法 + method = xxPermissionsClass.getDeclaredMethod("getCachePermission", Context.class, String.class); + } catch (NoSuchMethodException e2) { + // 尝试其他可能的方法名 + String[] possibleMethodNames = { + "getPermissionCache", + "getCachedPermission", + "checkCachedPermission" + }; + for (String methodName : possibleMethodNames) { + try { + method = xxPermissionsClass.getDeclaredMethod(methodName, Context.class, String.class); + break; + } catch (NoSuchMethodException e3) { + // 继续尝试下一个方法名 + } + } + if (method == null) { + throw new NoSuchMethodException("No suitable cache method found"); + } + } + } + // 设置可访问 + method.setAccessible(true); + // 调用方法 + Object result = method.invoke(null, context, permission); + // 处理返回值 + if (result instanceof IPermission) { + return (IPermission) result; + } else { + return null; + } + } catch (Exception e) { + Log.e("Reflection", "Failed to invoke cache method", e); + return null; + } + } + + public static List convertToIPermissionArray(Context context, String[] permissions) { + // 将String[]转换为List + List permissionList = new ArrayList<>(); + + if (permissions == null || permissions.length == 0) { + return permissionList; + } + for (int i = 0; i < permissions.length; i++) { + String permission = permissions[i]; + // 1. 尝试通过反射获取缓存的IPermission + IPermission cachedPermission = invokeGetCachePermission(context, permission); + if (cachedPermission != null) { + permissionList.add(cachedPermission); + } + } + return permissionList; + } + + + public static void showGrantedPermissionsToast(Context context, List grantedList) { + Toaster.show(String.format(context.getString(R.string.demo_obtain_permission_success_hint) + , PermissionConverter.getNickNamesByPermissions(context, grantedList))); + } +} diff --git a/BaseLibrary/src/main/java/com/tfq/library/permission/WindowLifecycleManager.java b/BaseLibrary/src/main/java/com/tfq/library/permission/WindowLifecycleManager.java new file mode 100644 index 0000000..7974edd --- /dev/null +++ b/BaseLibrary/src/main/java/com/tfq/library/permission/WindowLifecycleManager.java @@ -0,0 +1,134 @@ +package com.tfq.library.permission; + +import android.app.Activity; +import android.app.Application.ActivityLifecycleCallbacks; +import android.app.Dialog; +import android.os.Build; +import android.os.Bundle; +import android.widget.PopupWindow; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/XXPermissions + * time : 2025/05/30 + * desc : 窗口生命周期管理 + */ +public final class WindowLifecycleManager { + + /** + * 将 Activity 和 Dialog 的生命周期绑定在一起 + */ + public static void bindDialogLifecycle(@NonNull Activity activity, @NonNull Dialog dialog) { + WindowLifecycleCallbacks windowLifecycleCallbacks = new WindowLifecycleCallbacks(activity) { + + @Override + public void onWindowDismiss() { + if (!dialog.isShowing()) { + return; + } + dialog.dismiss(); + } + }; + registerWindowLifecycleCallbacks(activity, windowLifecycleCallbacks); + } + + /** + * 将 Activity 和 PopupWindow 的生命周期绑定在一起 + */ + public static void bindPopupWindowLifecycle(@NonNull Activity activity, @NonNull PopupWindow popupWindow) { + WindowLifecycleCallbacks windowLifecycleCallbacks = new WindowLifecycleCallbacks(activity) { + + @Override + public void onWindowDismiss() { + if (!popupWindow.isShowing()) { + return; + } + popupWindow.dismiss(); + } + }; + registerWindowLifecycleCallbacks(activity, windowLifecycleCallbacks); + } + + /** + * 注册窗口回调 + */ + private static void registerWindowLifecycleCallbacks(@NonNull Activity activity, @NonNull WindowLifecycleCallbacks callbacks) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.registerActivityLifecycleCallbacks(callbacks); + } else { + activity.getApplication().registerActivityLifecycleCallbacks(callbacks); + } + } + + /** + * 反注册窗口回调 + */ + private static void unregisterWindowLifecycleCallbacks(@NonNull Activity activity, @NonNull WindowLifecycleCallbacks callbacks) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.unregisterActivityLifecycleCallbacks(callbacks); + } else { + activity.getApplication().unregisterActivityLifecycleCallbacks(callbacks); + } + } + + /** + * 窗口生命周期回调 + */ + private abstract static class WindowLifecycleCallbacks implements ActivityLifecycleCallbacks { + + @Nullable + private Activity mActivity; + + private WindowLifecycleCallbacks(@NonNull Activity activity) { + mActivity = activity; + } + + public abstract void onWindowDismiss(); + + @Override + public final void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { + // default implementation ignored + } + + @Override + public final void onActivityStarted(@NonNull Activity activity) { + // default implementation ignored + } + + @Override + public final void onActivityResumed(@NonNull Activity activity) { + // default implementation ignored + } + + @Override + public final void onActivityPaused(@NonNull Activity activity) { + // default implementation ignored + } + + @Override + public final void onActivityStopped(@NonNull Activity activity) { + // default implementation ignored + } + + @Override + public final void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { + // default implementation ignored + } + + @Override + public final void onActivityDestroyed(@NonNull Activity activity) { + if (activity != mActivity) { + return; + } + // 释放 Activity 对象 + mActivity = null; + // 反注册窗口监听 + unregisterWindowLifecycleCallbacks(activity, this); + // 通知外层销毁窗口 + onWindowDismiss(); + } + } +} \ No newline at end of file diff --git a/BaseLibrary/src/main/java/com/tfq/library/view/AuthDialog.java b/BaseLibrary/src/main/java/com/tfq/library/view/AuthDialog.java index 19b29b6..4e41695 100644 --- a/BaseLibrary/src/main/java/com/tfq/library/view/AuthDialog.java +++ b/BaseLibrary/src/main/java/com/tfq/library/view/AuthDialog.java @@ -32,6 +32,8 @@ public class AuthDialog extends Dialog { private String type; private String title; private String content; + private String leftText; + private String rightText; public AuthDialog(Context context, Listener listener) { super(context, R.style.Dialog); @@ -65,6 +67,29 @@ public class AuthDialog extends Dialog { inflater = LayoutInflater.from(context); } + public AuthDialog(Context context, String type, String title, String content, String rightText, Listener listener) { + super(context, R.style.Dialog); + this.listener = listener; + this.mContext = context; + this.type = type; + this.title = title; + this.content = content; + this.rightText = rightText; + inflater = LayoutInflater.from(context); + } + + public AuthDialog(Context context, String type, String title, String content, String leftText, String rightText, Listener listener) { + super(context, R.style.Dialog); + this.listener = listener; + this.mContext = context; + this.type = type; + this.title = title; + this.content = content; + this.leftText = leftText; + this.rightText = rightText; + inflater = LayoutInflater.from(context); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -93,148 +118,183 @@ public class AuthDialog extends Dialog { TextView tv_content = contentView.findViewById(R.id.tv_content); TextView tv_title = contentView.findViewById(R.id.tv_title); - if (PRIVACY.equals(type)) { - tv_title.setText("隐私权限管理"); - tv_content.setText("请在本应用的详情页面的权限列表找到'权限'并打开进行设置。"); - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (listener != null) { - listener.callBack(); - } else { - Intent intent = new Intent(); - intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); - intent.setData(Uri.fromParts("package", mContext.getPackageName(), null)); - mContext.startActivity(intent); - } - dismiss(); - } - }); - - tv_left.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - } - }); - } else if (AUTHORIZATIO.equals(type)) { - tv_title.setText("撤回隐私授权"); - tv_content.setText("若您撤回本App的隐私授权,我们将会停止收集您的个人信息,并且不再为您提供相应服务,谨慎进行此步操作。"); - tv_left.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - } - }); - - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (listener != null) { - listener.callBack(); - } - dismiss(); - } - }); - } else if (PLAYURL.equals(type)) { - tv_title.setText("此链接无效"); - tv_content.setText("请输入正确地址"); - tv_content.setGravity(Gravity.CENTER); - tv_left.setVisibility(View.GONE); -// tv_right.setImageDrawable(mContext.getDrawable(R.mipmap.ic_confirm)); - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - if (listener != null) { - listener.callBack(); - } - } - }); - this.setCanceledOnTouchOutside(false); - this.setCancelable(false); - } else if (REQUEST_PER.equals(type)) { - tv_title.setText("权限申请"); - tv_content.setText("当前操作需要您授权相应权限,否则可能无法正常使用此功能"); - tv_content.setGravity(Gravity.CENTER); - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - if (listener != null) { - listener.callBack(); - } - } - }); - tv_left.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - this.setCanceledOnTouchOutside(false); - this.setCancelable(false); - } else if (EXIT_APP.equals(type)) { - tv_title.setText("退出应用"); - tv_content.setText("退出应用请点击确定按钮"); - tv_content.setGravity(Gravity.CENTER); - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - if (listener != null) { - listener.callBack(); - } - } - }); - tv_left.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - this.setCanceledOnTouchOutside(false); - this.setCancelable(false); - } else if (CUSTOM_TYPE.equals(type)) { - tv_title.setText(title); - tv_content.setText(content); - tv_content.setGravity(Gravity.CENTER); - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - if (listener != null) { - listener.callBack(); - } - } - }); - tv_left.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - this.setCanceledOnTouchOutside(false); - this.setCancelable(false); + if (type == null || type.isEmpty()) { + setOtherType(tv_title, tv_content, tv_left, tv_right); } else { - tv_right.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (listener != null) listener.callBack(); - dismiss(); - } - }); + if (PRIVACY.equals(type)) { + tv_title.setText("隐私权限管理"); + tv_content.setText("请在本应用的详情页面的权限列表找到'权限'并打开进行设置。"); + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null) { + listener.callBack(); + } else { + Intent intent = new Intent(); + intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + intent.setData(Uri.fromParts("package", mContext.getPackageName(), null)); + mContext.startActivity(intent); + } + dismiss(); + } + }); - tv_left.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - dismiss(); - } - }); + tv_left.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + } + }); + } else if (AUTHORIZATIO.equals(type)) { + tv_title.setText("撤回隐私授权"); + tv_content.setText("若您撤回本App的隐私授权,我们将会停止收集您的个人信息,并且不再为您提供相应服务,谨慎进行此步操作。"); + tv_left.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + } + }); + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null) { + listener.callBack(); + } + dismiss(); + } + }); + } else if (PLAYURL.equals(type)) { + tv_title.setText("此链接无效"); + tv_content.setText("请输入正确地址"); + tv_content.setGravity(Gravity.CENTER); + tv_left.setVisibility(View.GONE); +// tv_right.setImageDrawable(mContext.getDrawable(R.mipmap.ic_confirm)); + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + if (listener != null) { + listener.callBack(); + } + } + }); + this.setCanceledOnTouchOutside(false); + this.setCancelable(false); + } else if (REQUEST_PER.equals(type)) { + tv_title.setText("权限申请"); + tv_content.setText("当前操作需要您授权相应权限,否则可能无法正常使用此功能"); + tv_content.setGravity(Gravity.CENTER); + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + if (listener != null) { + listener.callBack(); + } + } + }); + tv_left.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + this.setCanceledOnTouchOutside(false); + this.setCancelable(false); + } else if (EXIT_APP.equals(type)) { + tv_title.setText("退出应用"); + tv_content.setText("退出应用请点击确定按钮"); + tv_content.setGravity(Gravity.CENTER); + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + if (listener != null) { + listener.callBack(); + } + } + }); + tv_left.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + this.setCanceledOnTouchOutside(false); + this.setCancelable(false); + } else if (CUSTOM_TYPE.equals(type)) { + + // 如果 title 不为空则设置,否则不处理 + regexNoEmptySet(title, tv_title); + // 如果 content 不为空则设置,否则不处理 + regexNoEmptySet(content, tv_content); + + tv_content.setGravity(Gravity.CENTER); + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + if (listener != null) { + listener.callBack(); + } + } + }); + tv_left.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + this.setCanceledOnTouchOutside(false); + this.setCancelable(false); + } else { + setOtherType(tv_title, tv_content, tv_left, tv_right); + } } + } + /** + * 显示 Dialog 默认随时可以点击返回键或者对话框蒙层来取消显示 Dialog + * + * @param tv_title 对话框标题 + * @param tv_content 对话框消息 + * @param tv_left 对话框左侧取消按钮文本 + * @param tv_right 对话框右侧确认按钮文本 + */ + private void setOtherType(TextView tv_title, TextView tv_content, TextView tv_left, TextView tv_right) { + + // 如果 title 不为空则设置,否则不处理 + regexNoEmptySet(title, tv_title); + // 如果 content 不为空则设置,否则不处理 + regexNoEmptySet(content, tv_content); + // 如果 leftText 不为空则设置,否则不处理 + regexNoEmptySet(leftText, tv_left); + // 如果 rightText 不为空则设置,否则不处理 + regexNoEmptySet(rightText, tv_right); + + tv_right.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null) listener.callBack(); + dismiss(); + } + }); + + tv_left.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + } + }); + } + + private void regexNoEmptySet(String string, TextView textView) { + // 如果 title 不为空则设置,否则不处理 + if (string != null && !string.isEmpty()) { + textView.setText(string); + } } public interface Listener { diff --git a/BaseLibrary/src/main/res/drawable/permission_description_popup_bg.xml b/BaseLibrary/src/main/res/drawable/permission_description_popup_bg.xml new file mode 100644 index 0000000..06e300e --- /dev/null +++ b/BaseLibrary/src/main/res/drawable/permission_description_popup_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BaseLibrary/src/main/res/layout/permission_description_popup.xml b/BaseLibrary/src/main/res/layout/permission_description_popup.xml new file mode 100644 index 0000000..39926d5 --- /dev/null +++ b/BaseLibrary/src/main/res/layout/permission_description_popup.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/BaseLibrary/src/main/res/values-zh/strings_permission.xml b/BaseLibrary/src/main/res/values-zh/strings_permission.xml new file mode 100644 index 0000000..63a7958 --- /dev/null +++ b/BaseLibrary/src/main/res/values-zh/strings_permission.xml @@ -0,0 +1,149 @@ + + + + 权限说明 + 确定 + + 授权提醒 + + 申请权限失败,请手动授予%s + 申请权限失败,请正确授予%s + + 申请%s失败,请选择 \“%s\” 选项 + 申请%s失败,请选择 \“%s\” 并开启 \“%s\” 选项 + 申请%s失败,请开启 \“%s\” 选项 + + 全部允许 + 始终允许 + + 前往授权 + + + + + + 权限 + 需要先申请该权限,才能继续使用该业务 + + 日历权限 + 用于创建、查看和管理日程安排,例如同步会议提醒、自动导入活动时间,让您随时掌握行程动态。 + + 相机权限 + 用于拍摄照片、录制视频或扫描二维码,例如上传头像、记录生活瞬间或快速识别信息。 + + 通讯录权限 + 用于读取联系人信息,例如快速查找好友、同步联系人到应用或向通讯录好友分享内容。 + + 定位权限 + 用于获取您的实时位置,例如显示附近的服务、导航路线规划或基于位置推荐内容。 + + 精确定位权限 + 确切位置 + + 后台定位权限 + 用于在后台持续获取位置信息,例如实时轨迹记录、后台导航或紧急情况下的位置共享。 + + 附近设备权限 + 用于发现和连接蓝牙、Wi-Fi等附近设备,例如无线传输文件、连接智能设备或共享网络。 + + 麦克风权限 + 用于录制声音,例如语音聊天、录制音频、语音输入或使用语音识别功能。 + + 电话权限 + 用于读取通话状态和电话号码,例如来电自动识别、快速拨号或统计通话信息。 + + 通话记录权限 + 用于读取通话历史,例如快速回拨号码、统计通话时长或标记骚扰电话。 + + 身体传感器权限 + 用于获取心率、步数等生理数据,例如健康监测、运动记录或个性化健康建议。 + + 后台身体传感器权限 + 用于在后台持续获取生理数据,例如睡眠监测、全天候健康追踪或紧急健康状况预警。 + + 健康数据共享权限 + @string/common_permission_body_sensors_description + + 后台健康数据共享权限 + 在后台访问健身与健康数据 + @string/common_permission_body_sensors_background_description + + 过往的健身与健康数据权限 + 访问过往的健身与健康数据 + 访问过往的健身与健康数据以判断当前身体状态。 + + 健身运动权限 + 身体活动权限 + 用于识别步行、跑步等运动状态,例如运动模式自动切换、运动数据统计或健身计划制定。 + + 访问媒体的位置信息权限 + 用于获取照片、视频等媒体文件的拍摄位置,例如按地点整理相册、显示照片拍摄地图或分享带位置的媒体内容。 + + 短信权限 + 用于读取短信内容(如验证码)、发送短信或备份短信记录,例如自动填充验证码、批量发送信息或短信归档。 + + 存储权限 + 用于读取和写入手机存储,例如保存图片、下载文件、缓存数据或备份应用设置。 + + 发送通知权限 + 用于向您发送消息通知,例如新消息提醒、活动通知或重要事项推送。 + + 照片和视频权限 + 用于访问相册中的照片和视频,例如上传图片、编辑视频或分享媒体文件到应用。 + + 音乐和音频权限 + 用于访问手机中的音乐和音频文件,例如播放本地音乐、设置铃声或编辑音频内容。 + + 读取应用列表权限 + 用于获取已安装的应用信息,以提供个性化推荐。例如,根据您安装的应用推荐相关工具,或根据您的应用使用习惯定制内容(您的应用列表数据将匿名使用,不会分享给第三方)。 + + 所有文件访问权限 + @string/common_permission_storage_description + + 安装应用权限 + 用于应用自更新功能,当官方版本发布时可直接在App内下载并安装最新版本,避免手动到浏览器下载的繁琐。 + + 悬浮窗权限 + 用于在其他应用上方显示内容,当应用处于后台状态时,依然能够展示悬浮窗口。 + + 修改系统设置权限 + 用于修改系统级设置,例如调整音量、切换网络模式或自定义显示设置。 + + 通知权限 + @string/common_permission_post_notifications_description + + 通知栏监听权限 + 用于读取通知栏内容,例如自动识别验证码、汇总通知信息或智能管理通知提醒。 + + 查看使用情况权限 + 用于获取应用使用时间和频率,例如屏幕时间管理、应用使用统计或个性化推荐。 + + 闹钟和提醒权限 + 用于创建和管理闹钟、提醒事项,例如日程闹钟、任务提醒或周期性活动通知。 + + 勿扰权限 + 用于管理手机勿扰模式,例如自动切换勿扰时段、设置例外通知或临时屏蔽干扰信息。 + + 忽略电池优化权限 + 用于保持应用后台运行,例如实时接收消息、持续定位或后台数据同步。 + + 画中画权限 + 用于开启画中画模式,例如边看视频边操作其他应用、悬浮视频窗口或多任务处理。 + + 全屏通知权限 + 允许在设备锁定时显示占据整个屏幕空间的通知,确保重要信息(如来电、紧急消息)不会被错过,即使设备处于锁屏状态也能立即看到提醒。 + + 设备管理器权限 + 用于提供设备级管理功能,例如远程锁定设备、擦除数据或强制密码策略。 + + 无障碍服务权限 + 用于帮助残障用户使用手机,例如语音播报、手势辅助或自动点击。(请在已下载的应用或者服务列表中找到并开启) + + \tVPN\t权限 + 用于创建和管理 VPN 连接,例如访问内网资源、网络代理或加密网络传输。 + + + 获取%s成功 + + + \ No newline at end of file diff --git a/BaseLibrary/src/main/res/values/colors.xml b/BaseLibrary/src/main/res/values/colors.xml index acb7b22..787f56e 100644 --- a/BaseLibrary/src/main/res/values/colors.xml +++ b/BaseLibrary/src/main/res/values/colors.xml @@ -6,8 +6,12 @@ #3F51B5 #303F9F #4285F6 + + #ffcf06 + #ffffff + #FFFFD700 #00000000 diff --git a/BaseLibrary/src/main/res/values/string.xml b/BaseLibrary/src/main/res/values/string.xml new file mode 100644 index 0000000..0f3f7fb --- /dev/null +++ b/BaseLibrary/src/main/res/values/string.xml @@ -0,0 +1,147 @@ + + + + Permission description + Confirm + + Authorization prompt + + Failed to request permission, please grant \"%s\" manually + Failed to request permission, please agree to grant %s + + Failed to request the %s,please select \"%s\" option + Failed to request the %s,please select \"%s\" option and enable the \"%s\" option + Failed to request the %s,please enable the \"%s\" option + + Allow all + Allow all the time + Go to authorization + + , + : + and + + permission + you need to apply for this permission first before you can continue to use this service + + calendar permission + allows creating, viewing, and managing schedules, such as syncing meeting reminders or automatically importing event times to keep your schedule updated. + + camera permission + allows taking photos, recording videos, or scanning qr codes, such as uploading profile pictures, capturing moments, or quickly identifying information. + + contacts permission + allows accessing contact information to quickly find friends, sync contacts to the app, or share content with contacts. + + location permission + allows accessing real-time location to display nearby services, plan navigation routes, or recommend location-based content. + + precise location permission + precise location + + background location permission + allows continuous location access in the background for real-time track recording, background navigation, or emergency location sharing. + + nearby devices permission + allows discovering and connecting to nearby devices (e.g., bluetooth, wi-fi) for wireless file transfer, smart device connection, or network sharing. + + microphone permission + allows recording audio for voice chats, audio recordings, voice input, or voice recognition features. + + phone permission + allows reading call status and phone numbers for caller id, quick dialing, or call statistics. + + call logs permission + allows accessing call history for quick redialing, call duration tracking, or marking spam calls. + + body sensors permission + allows accessing physiological data (e.g., heart rate, steps) for health monitoring, fitness tracking, or personalized health advice. + + background body sensors permission + allows continuous physiological data access in the background for sleep monitoring, 24/7 health tracking, or emergency health alerts. + + fitness and wellness data permission + @string/common_permission_body_sensors_description + + background fitness and wellness data permission + Access fitness and wellness data in the background + @string/common_permission_body_sensors_background_description + + past fitness and wellness data permission + Access past fitness and wellness data + access past fitness and health data to determine your current physical condition. + + physical activity permission + physical activity permission + allows recognizing activities (e.g., walking, running) for automatic exercise mode switching, activity data statistics, or fitness plan creation. + + access media location information permission + allows accessing the location metadata of photos and videos for organizing albums by location, displaying photo maps, or sharing media with location info. + + sms permission + allows reading sms (e.g., verification codes), sending messages, or backing up sms records for automatic otp filling, bulk messaging, or sms archiving. + + storage permission + allows reading and writing to device storage for saving images, downloading files, caching data, or backing up app settings. + + send notification permission + allows sending message notifications (e.g., new messages, event alerts, important updates), with customizable notification types in settings. + + image and video permission + allows accessing photos and videos in the gallery for uploading images, editing videos, or sharing media files within the app. + + music and audio permission + allows accessing music and audio files on the device for playing local music, setting ringtones, or editing audio content. + + get info about installed apps permission + allows accessing your installed apps to provide personalized recommendations. For example, suggesting related tools based on your installed apps or tailoring content to your app usage habits (your app list data is used anonymously and will not be shared with third parties). + + all file access permission + @string/common_permission_storage_description + + install unknown apps permission + allows the app to update itself automatically. When a new version is available, you can install it directly within the app without manually downloading from a browser or app store. + + display over other apps permission + allows displaying content over other apps. Even when the app is in the background, it can show floating windows. + + modify system settings permission + allows modifying system-level settings (e.g., volume, network mode, display settings). + + allow notifications permission + @string/common_permission_post_notifications_description + + allow notifications access permission + allows reading notification panel content for automatic otp recognition, notification summarization, or intelligent notification management. + + apps with usage access permission + allows accessing app usage data (time and frequency) for screen time management, app usage statistics, or personalized recommendations. + + alarms and reminders permission + allows creating and managing alarms and reminders for schedule alerts, task notifications, or recurring event reminders. + + do not disturb access permission + allows managing do not disturb mode for automatic time switching, setting exception notifications, or temporarily blocking interruptions. + + ignore battery optimize permission + allows keeping the app running in the background for real-time messaging, continuous location tracking, or background data sync. + + picture-in-picture permission + allows enabling picture-in-picture mode for watching videos while using other apps, floating video windows, or multitasking. + + full screen notifications permission + allows displaying notifications that occupy the entire screen when the device is locked, ensuring important information (e.g., incoming calls, emergency messages) is not missed. These notifications appear even when the device is locked. + + device admin permission + allows device-level management functions (e.g., remote lock, data wipe, password policies). + + accessibility service permission + allows assisting users with disabilities through features like voice announcements, gesture assistance, or automatic tapping. (please find and open it in the list of downloaded applications or services) + + \tvpn\tpermission + allows creating and managing vpn connections for accessing internal networks, network proxy, or encrypted data transmission. + + Obtained %s successfully + + + \ No newline at end of file diff --git a/LibraryAd/build.gradle b/LibraryAd/build.gradle index fac2aef..2ee4c0a 100644 --- a/LibraryAd/build.gradle +++ b/LibraryAd/build.gradle @@ -46,10 +46,11 @@ dependencies { } else { //线上使用这个,因为是线上库 //假如使用这个的话,需要先将LibraryAdLib进行publish后再同步项目,更新下来 - implementation "com.chuangketie.jk:lib_ad_GDTSDK.unionNormal:$rootProject.maven_version.version" - implementation "com.chuangketie.jk:lib_ad_mediation_gdt_adapter:$rootProject.maven_version.version" - implementation "com.chuangketie.jk:lib_ad_open_ad_sdk:$rootProject.maven_version.version" -// compileOnly fileTree(dir: '../LibraryAdLib/libs', include: ['*.aar']) +// implementation "com.chuangketie.jk:lib_ad_GDTSDK.unionNormal:$rootProject.maven_version.adlib_version" +// implementation "com.chuangketie.jk:lib_ad_mediation_gdt_adapter:$rootProject.maven_version.adlib_version" +// implementation "com.chuangketie.jk:lib_ad_open_ad_sdk:$rootProject.maven_version.adlib_version" + + compileOnly fileTree(dir: '../LibraryAdLib/libs', include: ['*.aar']) } } @@ -59,7 +60,7 @@ afterEvaluate { release(MavenPublication) { groupId = 'com.chuangketie.jk' // 自定义组织标识 artifactId = 'lib_ad' // 库名称 - version = rootProject.maven_version.version //版本号 + version = rootProject.maven_version.adlib_version //版本号 artifact("$buildDir/outputs/aar/${project.name}-release.aar")// 指定 AAR 文件路径 } } diff --git a/LibraryAdLib/build.gradle b/LibraryAdLib/build.gradle index 15e1dc7..0f4e3ec 100644 --- a/LibraryAdLib/build.gradle +++ b/LibraryAdLib/build.gradle @@ -17,7 +17,7 @@ afterEvaluate { "$pubName"(MavenPublication) { groupId = 'com.chuangketie.jk' artifactId = 'lib_ad_' + pubName - version = rootProject.maven_version.version + version = rootProject.maven_version.adlib_aar_version artifact file("libs/${aarName}") } } diff --git a/README.md b/README.md index 0cf4325..8bce4d1 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,16 @@ maven { #### 模块的gradle ```java -//Base_And_Ad -def lib_base_and_ad = "1.0.0" -implementation "com.chuangketie.jk:lib_base:$lib_base_and_ad" -implementation "com.chuangketie.jk:lib_ad:$lib_base_and_ad" //noinspection Aligned16KB -implementation "com.chuangketie.jk:lib_ad_open_ad_sdk:$lib_base_and_ad" -implementation "com.chuangketie.jk:lib_ad_GDTSDK.unionNormal:$lib_base_and_ad" -implementation "com.chuangketie.jk:lib_ad_mediation_gdt_adapter:$lib_base_and_ad" +//Base_And_Ad +def lib_base = "1.0.1" +implementation "com.chuangketie.jk:lib_base:$lib_base" +def lib_ad = "1.0.1" +implementation "com.chuangketie.jk:lib_ad:$lib_ad" +def lib_ad_aar = "1.0.0" +implementation "com.chuangketie.jk:lib_ad_open_ad_sdk:$lib_ad_aar" +implementation "com.chuangketie.jk:lib_ad_GDTSDK.unionNormal:$lib_ad_aar" +implementation "com.chuangketie.jk:lib_ad_mediation_gdt_adapter:$lib_ad_aar" ``` ## BASE diff --git a/android_data/data.gradle b/android_data/data.gradle index 142ea15..9bafb31 100644 --- a/android_data/data.gradle +++ b/android_data/data.gradle @@ -44,6 +44,8 @@ ext { appKaiPing: "@drawable/app_splash", ] maven_version = [ - version : "1.0.0" + baselib_version : "1.0.1", + adlib_version : "1.0.1", + adlib_aar_version: "1.0.0", ] } diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar new file mode 100644 index 0000000..a9f9a44 Binary files /dev/null and b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar differ diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.md5 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.md5 new file mode 100644 index 0000000..a38cdde --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.md5 @@ -0,0 +1 @@ +31ae7795386ce894b560e3e83ad09240 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha1 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha1 new file mode 100644 index 0000000..957e944 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha1 @@ -0,0 +1 @@ +e13f8c834e5755f7dc3ed51a31e0589da862e6f0 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha256 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha256 new file mode 100644 index 0000000..fc5c284 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha256 @@ -0,0 +1 @@ +ec8f2d1ad62d8fc249ce5e0d93bdb9fdcfde5e7ffe586a11ecef36f9cf3ee474 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha512 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha512 new file mode 100644 index 0000000..2a6d1bc --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.aar.sha512 @@ -0,0 +1 @@ +cab6e2ab2ada9ddf2eabaac188e79a93707ffd61e1e19c2a44132076238cdf972b121ca220273fe8c7cddd380080be4d1fd34173930b0e2e1d07de431ef8d9c8 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom new file mode 100644 index 0000000..4db8f0c --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + com.chuangketie.jk + lib_ad + 1.0.1 + aar + diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.md5 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.md5 new file mode 100644 index 0000000..c1fa2ca --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.md5 @@ -0,0 +1 @@ +cea8424a2b417f8369c6891ea7c9bedc \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha1 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha1 new file mode 100644 index 0000000..801bbd9 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha1 @@ -0,0 +1 @@ +47ef11ee386b829e4ad4980feed3f5e7cdbfca24 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha256 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha256 new file mode 100644 index 0000000..afdcd63 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha256 @@ -0,0 +1 @@ +af38deaf414b6e0fd8c64764447137f1ac96715367339049ebbec65387ce90e4 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha512 b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha512 new file mode 100644 index 0000000..788565b --- /dev/null +++ b/maven/com/chuangketie/jk/lib_ad/1.0.1/lib_ad-1.0.1.pom.sha512 @@ -0,0 +1 @@ +4077a9d44c56df5a37b6a64534d8a529779feec8334cd6c1b1e6c8520fe541780a904f14eb08c533136c2336a25c8698e8f7e48a573017cfc5c3d673e1be86f4 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml index 301bcd4..b04e488 100644 --- a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml +++ b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml @@ -3,11 +3,12 @@ com.chuangketie.jk lib_ad - 1.0.0 - 1.0.0 + 1.0.1 + 1.0.1 1.0.0 + 1.0.1 - 20250730090025 + 20251120060848 diff --git a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.md5 b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.md5 index bfc740c..abcf7f7 100644 --- a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.md5 +++ b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.md5 @@ -1 +1 @@ -e71cb21ebd83a2c863bc12bf7667b2ef \ No newline at end of file +650335f76d652ed2202d121b43cd474d \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha1 b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha1 index 867fb31..d633a98 100644 --- a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha1 +++ b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha1 @@ -1 +1 @@ -986355e4ecb7cb36c396053ddf6deb1a21061494 \ No newline at end of file +0acbd9e4bbde3104ec5d7586b859227d87356a4e \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha256 b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha256 index a5cd991..720a12e 100644 --- a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha256 +++ b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha256 @@ -1 +1 @@ -963063148b916ba363def5786ed13cc0380e8c9eaf929fff9863085c50f14348 \ No newline at end of file +e446a896a442cce95dcfe4bb28dacfe039b0d7be62eef2cc87c16278ed22c98a \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha512 b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha512 index 4326eb1..41f5e19 100644 --- a/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha512 +++ b/maven/com/chuangketie/jk/lib_ad/maven-metadata.xml.sha512 @@ -1 +1 @@ -7ae0c5244ac54c1ac5aa4888e3f2a1b32cdb7e06bdcd0552c4739667a78f60ba52afc20ee9957b59d2312ca30e303ee3dc22764fda0f354038d5b489649915be \ No newline at end of file +02d4a0cc0cb4d4930ad74529d5a9ee89e294b2524f74fd23b909e6033977ef86392eb6daf0cbd9fb9de5524930bb53187892a899f72ade61dfdaaddf5ed197ac \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar new file mode 100644 index 0000000..edcd468 Binary files /dev/null and b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar differ diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.md5 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.md5 new file mode 100644 index 0000000..ca91404 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.md5 @@ -0,0 +1 @@ +e05e23ece9281b5430c04a551ae1fdb0 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha1 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha1 new file mode 100644 index 0000000..193373f --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha1 @@ -0,0 +1 @@ +8ee3f5d4f7e4e9d7d0dbac0a6400899e8f5493b5 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha256 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha256 new file mode 100644 index 0000000..a422ac6 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha256 @@ -0,0 +1 @@ +4d9bcdfe16f8e575233e962a4ad3b8f198a0077b61a93fdb5a01758644094370 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha512 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha512 new file mode 100644 index 0000000..5077bcb --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.aar.sha512 @@ -0,0 +1 @@ +297865bd1a3f74adb38343391114062021b8cf20e8d43cff2c7cb81c2f39eaacd8137eed036a58aac1edd1f95f8fe270a7bc222c2223e92edf2ccb36b8babca5 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom new file mode 100644 index 0000000..05e53f6 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom @@ -0,0 +1,70 @@ + + + 4.0.0 + com.chuangketie.jk + lib_base + 1.0.1 + aar + + + androidx.appcompat + appcompat + 1.7.0 + + + com.google.android.material + material + 1.12.0 + + + com.google.code.gson + gson + 2.9.0 + + + com.squareup.okhttp3 + okhttp + 5.0.0-alpha.16 + + + com.squareup.okhttp3 + logging-interceptor + 5.0.0-alpha.16 + + + com.github.bumptech.glide + glide + 4.16.0 + + + jp.wasabeef + glide-transformations + 4.3.0 + + + com.github.centerzx + ShapeBlurView + 1.0.5 + + + com.github.getActivity + Toaster + 12.8 + + + com.github.getActivity + XXPermissions + 26.5 + + + com.github.CymChad + BaseRecyclerViewAdapterHelper + 2.9.28 + + + com.geyifeng.immersionbar + immersionbar + 3.2.2 + + + diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.md5 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.md5 new file mode 100644 index 0000000..c8980a3 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.md5 @@ -0,0 +1 @@ +7b477d49401ad1acb16c7fc9ea862943 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha1 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha1 new file mode 100644 index 0000000..18eb12b --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha1 @@ -0,0 +1 @@ +2a29201607d25f031f9538fc4cd65ca9d1a21875 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha256 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha256 new file mode 100644 index 0000000..9d45545 --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha256 @@ -0,0 +1 @@ +331fb260f57fed321157ca8e5731a507c8d2f360b34c1204a5749c1c0f59b2ca \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha512 b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha512 new file mode 100644 index 0000000..54be2cf --- /dev/null +++ b/maven/com/chuangketie/jk/lib_base/1.0.1/lib_base-1.0.1.pom.sha512 @@ -0,0 +1 @@ +4176f5db8e36d9e99543b4998127d0e893f08599c3b045ae3aeeef3a1c6f3dc007259311158abf8f5f06d2f57a4d65888726ee2e943d5f029e8871ec482e0ce5 \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml index 63ee763..f12832d 100644 --- a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml +++ b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml @@ -3,11 +3,12 @@ com.chuangketie.jk lib_base - 1.0.0 - 1.0.0 + 1.0.1 + 1.0.1 1.0.0 + 1.0.1 - 20251119064448 + 20251120060846 diff --git a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.md5 b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.md5 index eb71464..e6ee785 100644 --- a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.md5 +++ b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.md5 @@ -1 +1 @@ -60d1aa120c1b5201f5df48fde432b5fb \ No newline at end of file +1cd0bf6251b926a7ba05758f7a92d0dc \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha1 b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha1 index 7ac8cb6..06cf8bf 100644 --- a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha1 +++ b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha1 @@ -1 +1 @@ -e2513918c42a8a57a3589f416c0a2109880a4598 \ No newline at end of file +822ab63ae308bff16409d179ff48db4c509db9ca \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha256 b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha256 index 4c100ad..366eafe 100644 --- a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha256 +++ b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha256 @@ -1 +1 @@ -e64c8ca7470c03e17ceb73b89af8026a02b60fca38f32b915d55fd4c4c87deca \ No newline at end of file +c07078f400f601517dd429a72cac5d374a9016a2f0eeded2ec93f670b2ce8c9a \ No newline at end of file diff --git a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha512 b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha512 index 6b092ce..6b6c00d 100644 --- a/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha512 +++ b/maven/com/chuangketie/jk/lib_base/maven-metadata.xml.sha512 @@ -1 +1 @@ -bc22992cb482599fde455ec63b9253558341f770fffb7429c7110dc8a39bbd3f46652646413afea4bfbd1adfbf8916ae390ab5b9ca8600778697505109b83b7b \ No newline at end of file +ec29bbc4fcc2e91b7cbeeeadc5b22d342dc46037aba617bd2c2f671322707554988abd94ab3ee73a22ddca0cdaee4799a64a219502201068859e89d801c086c2 \ No newline at end of file