针对权限请求部分优化完成

base lib lib_aar版本分开维护
This commit is contained in:
jk 2025-11-20 14:21:42 +08:00
parent 086b13a50e
commit c90150e676
47 changed files with 1802 additions and 194 deletions

View File

@ -54,7 +54,7 @@ dependencies {
api 'com.github.getActivity:Toaster:12.8' api 'com.github.getActivity:Toaster:12.8'
// //
api 'com.github.getActivity:XXPermissions:23.0' api 'com.github.getActivity:XXPermissions:26.5'
//RecyclerView //RecyclerView
api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.28' api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.28'
@ -71,7 +71,7 @@ afterEvaluate {
release(MavenPublication) { release(MavenPublication) {
groupId = 'com.chuangketie.jk' groupId = 'com.chuangketie.jk'
artifactId = 'lib_base' artifactId = 'lib_base'
version = rootProject.maven_version.version version = rootProject.maven_version.baselib_version
artifact("$buildDir/outputs/aar/${project.name}-release.aar") artifact("$buildDir/outputs/aar/${project.name}-release.aar")
// //

View File

@ -1,30 +1,33 @@
package com.tfq.library.base; 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.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.AnimationUtils; import android.widget.Toast;
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 androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; 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. * 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) { private void requestPermission(String[] permission, boolean show_doNotAskAgain, Listener listener) {
List<IPermission> iPermissionList = convertToIPermissionArray(requireActivity(),permission);
if (iPermissionList.isEmpty()){
return;
}
XXPermissions.with(getActivity()) XXPermissions.with(getActivity())
.permission(permission) .permissions(iPermissionList)
.request(new OnPermissionCallback() { .request(new OnPermissionCallback() {
@Override @Override
public void onGranted(@NonNull List<String> permissions, boolean allGranted) { public void onResult(@NonNull List<IPermission> grantedList, @NonNull List<IPermission> deniedList) {
boolean allGranted = deniedList.isEmpty();
if (!allGranted) { if (!allGranted) {
//获取部分权限成功但部分权限未正常授予
return; return;
} }
listener.success(); listener.success();
showGrantedPermissionsToast(requireActivity(),grantedList);
} }
@Override
public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) { // @Override
if (show_doNotAskAgain) { // public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
if (doNotAskAgain) { // if (show_doNotAskAgain) {
//如果是被永久拒绝就跳转到应用权限系统设置页面 // if (doNotAskAgain) {
XXPermissions.startPermissionActivity(getActivity(), permissions); // //如果是被永久拒绝就跳转到应用权限系统设置页面
} else { // XXPermissions.startPermissionActivity(getActivity(), iPermissionList);
//获取权限失败 // } else {
} // //获取权限失败
} // }
} // }
// }
}); });
} }

View File

@ -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<String, Integer> PERMISSION_NAME_MAP = new HashMap<>();
/**
* 权限描述映射为了适配多语种这里存储的是 StringId而不是 String
*/
private static final Map<Integer, Integer> 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<IPermission> permissions) {
List<String> 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<String> getNickNameListByPermissions(@NonNull Context context, @NonNull List<IPermission> permissions, boolean filterHighVersionPermissions) {
List<String> 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<IPermission> permissions) {
List<String> 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<String> getDescriptionListByPermissions(@NonNull Context context, @NonNull List<IPermission> permissions) {
List<String> 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);
}
}

View File

@ -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<IPermission> requestList,
@NonNull Runnable continueRequestRunnable,
@NonNull Runnable breakRequestRunnable) {
// 以下情况使用 Dialog 来展示权限说明弹窗否则使用 PopupWindow 来展示权限说明弹窗
// 1. 如果请求的权限显示的系统界面是不透明的 Activity
// 2. 如果当前 Activity 的屏幕是竖屏的话并且设备的物理屏幕尺寸还小于 9 目前大多数小屏平板大多数集中在 88.78.810
// 实测 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<IPermission> 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<IPermission> requestList) {
// 移除跟这个 Token 有关但是没有还没有执行的消息
HANDLER.removeCallbacksAndMessages(mHandlerToken);
// 销毁当前正在显示的弹窗
dismissPopupWindow();
dismissDialog();
}
/**
* 生成权限描述文案
*/
private String generatePermissionDescription(@NonNull Activity activity, @NonNull List<IPermission> 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));
}
}

View File

@ -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<IPermission> requestList,
@NonNull List<IPermission> grantedList,
@NonNull List<IPermission> 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<IPermission> requestList,
@NonNull List<IPermission> 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<IPermission> grantedList, @NonNull List<IPermission> deniedList) {
List<IPermission> 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<IPermission> 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);
}
}

View File

@ -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<IPermission> convertToIPermissionArray(Context context, String[] permissions) {
// 将String[]转换为List<IPermission>
List<IPermission> 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<IPermission> grantedList) {
Toaster.show(String.format(context.getString(R.string.demo_obtain_permission_success_hint)
, PermissionConverter.getNickNamesByPermissions(context, grantedList)));
}
}

View File

@ -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();
}
}
}

View File

@ -32,6 +32,8 @@ public class AuthDialog extends Dialog {
private String type; private String type;
private String title; private String title;
private String content; private String content;
private String leftText;
private String rightText;
public AuthDialog(Context context, Listener listener) { public AuthDialog(Context context, Listener listener) {
super(context, R.style.Dialog); super(context, R.style.Dialog);
@ -65,6 +67,29 @@ public class AuthDialog extends Dialog {
inflater = LayoutInflater.from(context); 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -93,148 +118,183 @@ public class AuthDialog extends Dialog {
TextView tv_content = contentView.findViewById(R.id.tv_content); TextView tv_content = contentView.findViewById(R.id.tv_content);
TextView tv_title = contentView.findViewById(R.id.tv_title); TextView tv_title = contentView.findViewById(R.id.tv_title);
if (PRIVACY.equals(type)) { if (type == null || type.isEmpty()) {
tv_title.setText("隐私权限管理"); setOtherType(tv_title, tv_content, tv_left, tv_right);
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);
} else { } else {
tv_right.setOnClickListener(new View.OnClickListener() { if (PRIVACY.equals(type)) {
@Override tv_title.setText("隐私权限管理");
public void onClick(View view) { tv_content.setText("请在本应用的详情页面的权限列表找到'权限'并打开进行设置。");
if (listener != null) listener.callBack(); tv_right.setOnClickListener(new View.OnClickListener() {
dismiss(); @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() { tv_left.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
dismiss(); 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 { public interface Listener {

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- <solid android:color="@android:color/white" />-->
<!-- <solid android:color="@android:color/black" />-->
<solid android:color="#000000" />
<stroke
android:width="1px"
android:color="#ffffff"
/>
<!-- android:color="#ECECEC" -->
<corners android:radius="10dp" />
</shape>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:background="@drawable/permission_description_popup_bg"
android:fitsSystemWindows="true"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="12dp"
android:layout_marginRight="15dp"
android:text="@string/common_permission_description_title"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_permission_description_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="12dp"
android:textColor="@color/white"
android:lineSpacingExtra="3dp"
android:textSize="13sp"
tools:text="xxx" />
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="common_permission_description_title">权限说明</string>
<string name="common_permission_confirm">确定</string>
<string name="common_permission_alert">授权提醒</string>
<string name="common_permission_fail_assign_hint_1">申请权限失败,请手动授予%s</string>
<string name="common_permission_fail_assign_hint_2">申请权限失败,请正确授予%s</string>
<string name="common_permission_fail_hint_1">申请%s失败请选择 \“%s\” 选项</string>
<string name="common_permission_fail_hint_2">申请%s失败请选择 \“%s\” 并开启 \“%s\” 选项</string>
<string name="common_permission_fail_hint_3">申请%s失败请开启 \“%s\” 选项</string>
<string name="common_permission_allow_all_option">全部允许</string>
<string name="common_permission_allow_all_the_time_option">始终允许</string>
<string name="common_permission_go_to_authorization">前往授权</string>
<string name="common_permission_comma"></string>
<string name="common_permission_colon"></string>
<string name="common_permission_and"></string>
<string name="common_permission_unknown">权限</string>
<string name="common_permission_unknown_description">需要先申请该权限,才能继续使用该业务</string>
<string name="common_permission_calendar">日历权限</string>
<string name="common_permission_calendar_description">用于创建、查看和管理日程安排,例如同步会议提醒、自动导入活动时间,让您随时掌握行程动态。</string>
<string name="common_permission_camera">相机权限</string>
<string name="common_permission_camera_description">用于拍摄照片、录制视频或扫描二维码,例如上传头像、记录生活瞬间或快速识别信息。</string>
<string name="common_permission_contacts">通讯录权限</string>
<string name="common_permission_contacts_description">用于读取联系人信息,例如快速查找好友、同步联系人到应用或向通讯录好友分享内容。</string>
<string name="common_permission_location">定位权限</string>
<string name="common_permission_location_description">用于获取您的实时位置,例如显示附近的服务、导航路线规划或基于位置推荐内容。</string>
<string name="common_permission_location_fine">精确定位权限</string>
<string name="common_permission_location_fine_option">确切位置</string>
<string name="common_permission_location_background">后台定位权限</string>
<string name="common_permission_location_background_description">用于在后台持续获取位置信息,例如实时轨迹记录、后台导航或紧急情况下的位置共享。</string>
<string name="common_permission_nearby_devices">附近设备权限</string>
<string name="common_permission_nearby_devices_description">用于发现和连接蓝牙、Wi-Fi等附近设备例如无线传输文件、连接智能设备或共享网络。</string>
<string name="common_permission_microphone">麦克风权限</string>
<string name="common_permission_microphone_description">用于录制声音,例如语音聊天、录制音频、语音输入或使用语音识别功能。</string>
<string name="common_permission_phone">电话权限</string>
<string name="common_permission_phone_description">用于读取通话状态和电话号码,例如来电自动识别、快速拨号或统计通话信息。</string>
<string name="common_permission_call_logs">通话记录权限</string>
<string name="common_permission_call_logs_description">用于读取通话历史,例如快速回拨号码、统计通话时长或标记骚扰电话。</string>
<string name="common_permission_body_sensors">身体传感器权限</string>
<string name="common_permission_body_sensors_description">用于获取心率、步数等生理数据,例如健康监测、运动记录或个性化健康建议。</string>
<string name="common_permission_body_sensors_background">后台身体传感器权限</string>
<string name="common_permission_body_sensors_background_description">用于在后台持续获取生理数据,例如睡眠监测、全天候健康追踪或紧急健康状况预警。</string>
<string name="common_permission_health_data">健康数据共享权限</string>
<string name="common_permission_health_data_description">@string/common_permission_body_sensors_description</string>
<string name="common_permission_health_data_background">后台健康数据共享权限</string>
<string name="common_permission_health_data_background_option">在后台访问健身与健康数据</string>
<string name="common_permission_health_data_background_description">@string/common_permission_body_sensors_background_description</string>
<string name="common_permission_health_data_past">过往的健身与健康数据权限</string>
<string name="common_permission_health_data_past_option">访问过往的健身与健康数据</string>
<string name="common_permission_health_data_past_description">访问过往的健身与健康数据以判断当前身体状态。</string>
<string name="common_permission_activity_recognition_api29">健身运动权限</string>
<string name="common_permission_activity_recognition_api30">身体活动权限</string>
<string name="common_permission_activity_recognition_description">用于识别步行、跑步等运动状态,例如运动模式自动切换、运动数据统计或健身计划制定。</string>
<string name="common_permission_access_media_location_information">访问媒体的位置信息权限</string>
<string name="common_permission_access_media_location_information_description">用于获取照片、视频等媒体文件的拍摄位置,例如按地点整理相册、显示照片拍摄地图或分享带位置的媒体内容。</string>
<string name="common_permission_sms">短信权限</string>
<string name="common_permission_sms_description">用于读取短信内容(如验证码)、发送短信或备份短信记录,例如自动填充验证码、批量发送信息或短信归档。</string>
<string name="common_permission_storage">存储权限</string>
<string name="common_permission_storage_description">用于读取和写入手机存储,例如保存图片、下载文件、缓存数据或备份应用设置。</string>
<string name="common_permission_post_notifications">发送通知权限</string>
<string name="common_permission_post_notifications_description">用于向您发送消息通知,例如新消息提醒、活动通知或重要事项推送。</string>
<string name="common_permission_image_and_video">照片和视频权限</string>
<string name="common_permission_image_and_video_description">用于访问相册中的照片和视频,例如上传图片、编辑视频或分享媒体文件到应用。</string>
<string name="common_permission_music_and_audio">音乐和音频权限</string>
<string name="common_permission_music_and_audio_description">用于访问手机中的音乐和音频文件,例如播放本地音乐、设置铃声或编辑音频内容。</string>
<string name="common_permission_get_installed_apps">读取应用列表权限</string>
<string name="common_permission_get_installed_apps_description">用于获取已安装的应用信息,以提供个性化推荐。例如,根据您安装的应用推荐相关工具,或根据您的应用使用习惯定制内容(您的应用列表数据将匿名使用,不会分享给第三方)。</string>
<string name="common_permission_all_file_access">所有文件访问权限</string>
<string name="common_permission_all_file_access_description">@string/common_permission_storage_description</string>
<string name="common_permission_install_unknown_apps">安装应用权限</string>
<string name="common_permission_install_unknown_apps_description">用于应用自更新功能当官方版本发布时可直接在App内下载并安装最新版本避免手动到浏览器下载的繁琐。</string>
<string name="common_permission_display_over_other_apps">悬浮窗权限</string>
<string name="common_permission_display_over_other_apps_description">用于在其他应用上方显示内容,当应用处于后台状态时,依然能够展示悬浮窗口。</string>
<string name="common_permission_modify_system_settings">修改系统设置权限</string>
<string name="common_permission_modify_system_settings_description">用于修改系统级设置,例如调整音量、切换网络模式或自定义显示设置。</string>
<string name="common_permission_allow_notifications">通知权限</string>
<string name="common_permission_allow_notifications_description">@string/common_permission_post_notifications_description</string>
<string name="common_permission_allow_notifications_access">通知栏监听权限</string>
<string name="common_permission_allow_notifications_access_description">用于读取通知栏内容,例如自动识别验证码、汇总通知信息或智能管理通知提醒。</string>
<string name="common_permission_apps_with_usage_access">查看使用情况权限</string>
<string name="common_permission_apps_with_usage_access_description">用于获取应用使用时间和频率,例如屏幕时间管理、应用使用统计或个性化推荐。</string>
<string name="common_permission_alarms_reminders">闹钟和提醒权限</string>
<string name="common_permission_alarms_reminders_description">用于创建和管理闹钟、提醒事项,例如日程闹钟、任务提醒或周期性活动通知。</string>
<string name="common_permission_do_not_disturb_access">勿扰权限</string>
<string name="common_permission_do_not_disturb_access_description">用于管理手机勿扰模式,例如自动切换勿扰时段、设置例外通知或临时屏蔽干扰信息。</string>
<string name="common_permission_ignore_battery_optimize">忽略电池优化权限</string>
<string name="common_permission_ignore_battery_optimize_description">用于保持应用后台运行,例如实时接收消息、持续定位或后台数据同步。</string>
<string name="common_permission_picture_in_picture">画中画权限</string>
<string name="common_permission_picture_in_picture_description">用于开启画中画模式,例如边看视频边操作其他应用、悬浮视频窗口或多任务处理。</string>
<string name="common_permission_full_screen_notifications">全屏通知权限</string>
<string name="common_permission_full_screen_notifications_description">允许在设备锁定时显示占据整个屏幕空间的通知,确保重要信息(如来电、紧急消息)不会被错过,即使设备处于锁屏状态也能立即看到提醒。</string>
<string name="common_permission_device_admin">设备管理器权限</string>
<string name="common_permission_device_admin_description">用于提供设备级管理功能,例如远程锁定设备、擦除数据或强制密码策略。</string>
<string name="common_permission_accessibility_service">无障碍服务权限</string>
<string name="common_permission_accessibility_service_description">用于帮助残障用户使用手机,例如语音播报、手势辅助或自动点击。(请在已下载的应用或者服务列表中找到并开启)</string>
<string name="common_permission_vpn">\tVPN\t权限</string>
<string name="common_permission_vpn_description">用于创建和管理 VPN 连接,例如访问内网资源、网络代理或加密网络传输。</string>
<string name="demo_obtain_permission_success_hint">获取%s成功</string>
</resources>

View File

@ -6,8 +6,12 @@
<color name="colorPrimary">#3F51B5</color> <color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color> <color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#4285F6</color> <color name="colorAccent">#4285F6</color>
<!-- 自定义Auth样式的Dialog的返回和确定的颜色 -->
<color name="themeColor">#ffcf06</color> <color name="themeColor">#ffcf06</color>
<!-- 自定义Auth样式的Dialog的返回和确定的字体颜色 -->
<color name="themeColorText">#ffffff</color> <color name="themeColorText">#ffffff</color>
<color name="gold">#FFFFD700</color><!--金色--> <color name="gold">#FFFFD700</color><!--金色-->
<color name="transparent">#00000000</color> <color name="transparent">#00000000</color>

View File

@ -0,0 +1,147 @@
<resources>
<!-- 新版权限申请的方式说明 start -->
<string name="common_permission_description_title">Permission description</string>
<string name="common_permission_confirm">Confirm</string>
<string name="common_permission_alert">Authorization prompt</string>
<string name="common_permission_fail_assign_hint_1">Failed to request permission, please grant \"%s\" manually</string>
<string name="common_permission_fail_assign_hint_2">Failed to request permission, please agree to grant %s</string>
<string name="common_permission_fail_hint_1">Failed to request the %splease select \"%s\" option</string>
<string name="common_permission_fail_hint_2">Failed to request the %splease select \"%s\" option and enable the \"%s\" option</string>
<string name="common_permission_fail_hint_3">Failed to request the %splease enable the \"%s\" option</string>
<string name="common_permission_allow_all_option">Allow all</string>
<string name="common_permission_allow_all_the_time_option">Allow all the time</string>
<string name="common_permission_go_to_authorization">Go to authorization</string>
<string name="common_permission_comma">, </string>
<string name="common_permission_colon">: </string>
<string name="common_permission_and"> and </string>
<string name="common_permission_unknown">permission</string>
<string name="common_permission_unknown_description">you need to apply for this permission first before you can continue to use this service</string>
<string name="common_permission_calendar">calendar permission</string>
<string name="common_permission_calendar_description">allows creating, viewing, and managing schedules, such as syncing meeting reminders or automatically importing event times to keep your schedule updated.</string>
<string name="common_permission_camera">camera permission</string>
<string name="common_permission_camera_description">allows taking photos, recording videos, or scanning qr codes, such as uploading profile pictures, capturing moments, or quickly identifying information.</string>
<string name="common_permission_contacts">contacts permission</string>
<string name="common_permission_contacts_description">allows accessing contact information to quickly find friends, sync contacts to the app, or share content with contacts.</string>
<string name="common_permission_location">location permission</string>
<string name="common_permission_location_description">allows accessing real-time location to display nearby services, plan navigation routes, or recommend location-based content.</string>
<string name="common_permission_location_fine">precise location permission</string>
<string name="common_permission_location_fine_option">precise location</string>
<string name="common_permission_location_background">background location permission</string>
<string name="common_permission_location_background_description">allows continuous location access in the background for real-time track recording, background navigation, or emergency location sharing.</string>
<string name="common_permission_nearby_devices">nearby devices permission</string>
<string name="common_permission_nearby_devices_description">allows discovering and connecting to nearby devices (e.g., bluetooth, wi-fi) for wireless file transfer, smart device connection, or network sharing.</string>
<string name="common_permission_microphone">microphone permission</string>
<string name="common_permission_microphone_description">allows recording audio for voice chats, audio recordings, voice input, or voice recognition features.</string>
<string name="common_permission_phone">phone permission</string>
<string name="common_permission_phone_description">allows reading call status and phone numbers for caller id, quick dialing, or call statistics.</string>
<string name="common_permission_call_logs">call logs permission</string>
<string name="common_permission_call_logs_description">allows accessing call history for quick redialing, call duration tracking, or marking spam calls.</string>
<string name="common_permission_body_sensors">body sensors permission</string>
<string name="common_permission_body_sensors_description">allows accessing physiological data (e.g., heart rate, steps) for health monitoring, fitness tracking, or personalized health advice.</string>
<string name="common_permission_body_sensors_background">background body sensors permission</string>
<string name="common_permission_body_sensors_background_description">allows continuous physiological data access in the background for sleep monitoring, 24/7 health tracking, or emergency health alerts.</string>
<string name="common_permission_health_data">fitness and wellness data permission</string>
<string name="common_permission_health_data_description">@string/common_permission_body_sensors_description</string>
<string name="common_permission_health_data_background">background fitness and wellness data permission</string>
<string name="common_permission_health_data_background_option">Access fitness and wellness data in the background</string>
<string name="common_permission_health_data_background_description">@string/common_permission_body_sensors_background_description</string>
<string name="common_permission_health_data_past">past fitness and wellness data permission</string>
<string name="common_permission_health_data_past_option">Access past fitness and wellness data</string>
<string name="common_permission_health_data_past_description">access past fitness and health data to determine your current physical condition.</string>
<string name="common_permission_activity_recognition_api29">physical activity permission</string>
<string name="common_permission_activity_recognition_api30">physical activity permission</string>
<string name="common_permission_activity_recognition_description">allows recognizing activities (e.g., walking, running) for automatic exercise mode switching, activity data statistics, or fitness plan creation.</string>
<string name="common_permission_access_media_location_information">access media location information permission</string>
<string name="common_permission_access_media_location_information_description">allows accessing the location metadata of photos and videos for organizing albums by location, displaying photo maps, or sharing media with location info.</string>
<string name="common_permission_sms">sms permission</string>
<string name="common_permission_sms_description">allows reading sms (e.g., verification codes), sending messages, or backing up sms records for automatic otp filling, bulk messaging, or sms archiving.</string>
<string name="common_permission_storage">storage permission</string>
<string name="common_permission_storage_description">allows reading and writing to device storage for saving images, downloading files, caching data, or backing up app settings.</string>
<string name="common_permission_post_notifications">send notification permission</string>
<string name="common_permission_post_notifications_description">allows sending message notifications (e.g., new messages, event alerts, important updates), with customizable notification types in settings.</string>
<string name="common_permission_image_and_video">image and video permission</string>
<string name="common_permission_image_and_video_description">allows accessing photos and videos in the gallery for uploading images, editing videos, or sharing media files within the app.</string>
<string name="common_permission_music_and_audio">music and audio permission</string>
<string name="common_permission_music_and_audio_description">allows accessing music and audio files on the device for playing local music, setting ringtones, or editing audio content.</string>
<string name="common_permission_get_installed_apps">get info about installed apps permission</string>
<string name="common_permission_get_installed_apps_description">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).</string>
<string name="common_permission_all_file_access">all file access permission</string>
<string name="common_permission_all_file_access_description">@string/common_permission_storage_description</string>
<string name="common_permission_install_unknown_apps">install unknown apps permission</string>
<string name="common_permission_install_unknown_apps_description">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.</string>
<string name="common_permission_display_over_other_apps">display over other apps permission</string>
<string name="common_permission_display_over_other_apps_description">allows displaying content over other apps. Even when the app is in the background, it can show floating windows.</string>
<string name="common_permission_modify_system_settings">modify system settings permission</string>
<string name="common_permission_modify_system_settings_description">allows modifying system-level settings (e.g., volume, network mode, display settings).</string>
<string name="common_permission_allow_notifications">allow notifications permission</string>
<string name="common_permission_allow_notifications_description">@string/common_permission_post_notifications_description</string>
<string name="common_permission_allow_notifications_access">allow notifications access permission</string>
<string name="common_permission_allow_notifications_access_description">allows reading notification panel content for automatic otp recognition, notification summarization, or intelligent notification management.</string>
<string name="common_permission_apps_with_usage_access">apps with usage access permission</string>
<string name="common_permission_apps_with_usage_access_description">allows accessing app usage data (time and frequency) for screen time management, app usage statistics, or personalized recommendations.</string>
<string name="common_permission_alarms_reminders">alarms and reminders permission</string>
<string name="common_permission_alarms_reminders_description">allows creating and managing alarms and reminders for schedule alerts, task notifications, or recurring event reminders.</string>
<string name="common_permission_do_not_disturb_access">do not disturb access permission</string>
<string name="common_permission_do_not_disturb_access_description">allows managing do not disturb mode for automatic time switching, setting exception notifications, or temporarily blocking interruptions.</string>
<string name="common_permission_ignore_battery_optimize">ignore battery optimize permission</string>
<string name="common_permission_ignore_battery_optimize_description">allows keeping the app running in the background for real-time messaging, continuous location tracking, or background data sync.</string>
<string name="common_permission_picture_in_picture">picture-in-picture permission</string>
<string name="common_permission_picture_in_picture_description">allows enabling picture-in-picture mode for watching videos while using other apps, floating video windows, or multitasking.</string>
<string name="common_permission_full_screen_notifications">full screen notifications permission</string>
<string name="common_permission_full_screen_notifications_description">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.</string>
<string name="common_permission_device_admin">device admin permission</string>
<string name="common_permission_device_admin_description">allows device-level management functions (e.g., remote lock, data wipe, password policies).</string>
<string name="common_permission_accessibility_service">accessibility service permission</string>
<string name="common_permission_accessibility_service_description">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)</string>
<string name="common_permission_vpn">\tvpn\tpermission</string>
<string name="common_permission_vpn_description">allows creating and managing vpn connections for accessing internal networks, network proxy, or encrypted data transmission.</string>
<string name="demo_obtain_permission_success_hint">Obtained %s successfully</string>
<!-- 新版权限申请的方式说明 end -->
</resources>

View File

@ -46,10 +46,11 @@ dependencies {
} else { } else {
//线使,线 //线使,线
//使,LibraryAdLib进行publish后再同步项目, //使,LibraryAdLib进行publish后再同步项目,
implementation "com.chuangketie.jk:lib_ad_GDTSDK.unionNormal:$rootProject.maven_version.version" // 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.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.version" // implementation "com.chuangketie.jk:lib_ad_open_ad_sdk:$rootProject.maven_version.adlib_version"
// compileOnly fileTree(dir: '../LibraryAdLib/libs', include: ['*.aar'])
compileOnly fileTree(dir: '../LibraryAdLib/libs', include: ['*.aar'])
} }
} }
@ -59,7 +60,7 @@ afterEvaluate {
release(MavenPublication) { release(MavenPublication) {
groupId = 'com.chuangketie.jk' // groupId = 'com.chuangketie.jk' //
artifactId = 'lib_ad' // artifactId = 'lib_ad' //
version = rootProject.maven_version.version // version = rootProject.maven_version.adlib_version //
artifact("$buildDir/outputs/aar/${project.name}-release.aar")// AAR artifact("$buildDir/outputs/aar/${project.name}-release.aar")// AAR
} }
} }

View File

@ -17,7 +17,7 @@ afterEvaluate {
"$pubName"(MavenPublication) { "$pubName"(MavenPublication) {
groupId = 'com.chuangketie.jk' groupId = 'com.chuangketie.jk'
artifactId = 'lib_ad_' + pubName artifactId = 'lib_ad_' + pubName
version = rootProject.maven_version.version version = rootProject.maven_version.adlib_aar_version
artifact file("libs/${aarName}") artifact file("libs/${aarName}")
} }
} }

View File

@ -11,14 +11,16 @@ maven {
#### 模块的gradle #### 模块的gradle
```java ```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 //noinspection Aligned16KB
implementation "com.chuangketie.jk:lib_ad_open_ad_sdk:$lib_base_and_ad" //Base_And_Ad
implementation "com.chuangketie.jk:lib_ad_GDTSDK.unionNormal:$lib_base_and_ad" def lib_base = "1.0.1"
implementation "com.chuangketie.jk:lib_ad_mediation_gdt_adapter:$lib_base_and_ad" 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 ## BASE

View File

@ -44,6 +44,8 @@ ext {
appKaiPing: "@drawable/app_splash", appKaiPing: "@drawable/app_splash",
] ]
maven_version = [ maven_version = [
version : "1.0.0" baselib_version : "1.0.1",
adlib_version : "1.0.1",
adlib_aar_version: "1.0.0",
] ]
} }

Binary file not shown.

View File

@ -0,0 +1 @@
31ae7795386ce894b560e3e83ad09240

View File

@ -0,0 +1 @@
e13f8c834e5755f7dc3ed51a31e0589da862e6f0

View File

@ -0,0 +1 @@
ec8f2d1ad62d8fc249ce5e0d93bdb9fdcfde5e7ffe586a11ecef36f9cf3ee474

View File

@ -0,0 +1 @@
cab6e2ab2ada9ddf2eabaac188e79a93707ffd61e1e19c2a44132076238cdf972b121ca220273fe8c7cddd380080be4d1fd34173930b0e2e1d07de431ef8d9c8

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chuangketie.jk</groupId>
<artifactId>lib_ad</artifactId>
<version>1.0.1</version>
<packaging>aar</packaging>
</project>

View File

@ -0,0 +1 @@
cea8424a2b417f8369c6891ea7c9bedc

View File

@ -0,0 +1 @@
47ef11ee386b829e4ad4980feed3f5e7cdbfca24

View File

@ -0,0 +1 @@
af38deaf414b6e0fd8c64764447137f1ac96715367339049ebbec65387ce90e4

View File

@ -0,0 +1 @@
4077a9d44c56df5a37b6a64534d8a529779feec8334cd6c1b1e6c8520fe541780a904f14eb08c533136c2336a25c8698e8f7e48a573017cfc5c3d673e1be86f4

View File

@ -3,11 +3,12 @@
<groupId>com.chuangketie.jk</groupId> <groupId>com.chuangketie.jk</groupId>
<artifactId>lib_ad</artifactId> <artifactId>lib_ad</artifactId>
<versioning> <versioning>
<latest>1.0.0</latest> <latest>1.0.1</latest>
<release>1.0.0</release> <release>1.0.1</release>
<versions> <versions>
<version>1.0.0</version> <version>1.0.0</version>
<version>1.0.1</version>
</versions> </versions>
<lastUpdated>20250730090025</lastUpdated> <lastUpdated>20251120060848</lastUpdated>
</versioning> </versioning>
</metadata> </metadata>

View File

@ -1 +1 @@
e71cb21ebd83a2c863bc12bf7667b2ef 650335f76d652ed2202d121b43cd474d

View File

@ -1 +1 @@
986355e4ecb7cb36c396053ddf6deb1a21061494 0acbd9e4bbde3104ec5d7586b859227d87356a4e

View File

@ -1 +1 @@
963063148b916ba363def5786ed13cc0380e8c9eaf929fff9863085c50f14348 e446a896a442cce95dcfe4bb28dacfe039b0d7be62eef2cc87c16278ed22c98a

View File

@ -1 +1 @@
7ae0c5244ac54c1ac5aa4888e3f2a1b32cdb7e06bdcd0552c4739667a78f60ba52afc20ee9957b59d2312ca30e303ee3dc22764fda0f354038d5b489649915be 02d4a0cc0cb4d4930ad74529d5a9ee89e294b2524f74fd23b909e6033977ef86392eb6daf0cbd9fb9de5524930bb53187892a899f72ade61dfdaaddf5ed197ac

View File

@ -0,0 +1 @@
e05e23ece9281b5430c04a551ae1fdb0

View File

@ -0,0 +1 @@
8ee3f5d4f7e4e9d7d0dbac0a6400899e8f5493b5

View File

@ -0,0 +1 @@
4d9bcdfe16f8e575233e962a4ad3b8f198a0077b61a93fdb5a01758644094370

View File

@ -0,0 +1 @@
297865bd1a3f74adb38343391114062021b8cf20e8d43cff2c7cb81c2f39eaacd8137eed036a58aac1edd1f95f8fe270a7bc222c2223e92edf2ccb36b8babca5

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chuangketie.jk</groupId>
<artifactId>lib_base</artifactId>
<version>1.0.1</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>androidx.appcompat</groupId>
<artifactId>appcompat</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>com.google.android.material</groupId>
<artifactId>material</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>5.0.0-alpha.16</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>5.0.0-alpha.16</version>
</dependency>
<dependency>
<groupId>com.github.bumptech.glide</groupId>
<artifactId>glide</artifactId>
<version>4.16.0</version>
</dependency>
<dependency>
<groupId>jp.wasabeef</groupId>
<artifactId>glide-transformations</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.github.centerzx</groupId>
<artifactId>ShapeBlurView</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>com.github.getActivity</groupId>
<artifactId>Toaster</artifactId>
<version>12.8</version>
</dependency>
<dependency>
<groupId>com.github.getActivity</groupId>
<artifactId>XXPermissions</artifactId>
<version>26.5</version>
</dependency>
<dependency>
<groupId>com.github.CymChad</groupId>
<artifactId>BaseRecyclerViewAdapterHelper</artifactId>
<version>2.9.28</version>
</dependency>
<dependency>
<groupId>com.geyifeng.immersionbar</groupId>
<artifactId>immersionbar</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1 @@
7b477d49401ad1acb16c7fc9ea862943

View File

@ -0,0 +1 @@
2a29201607d25f031f9538fc4cd65ca9d1a21875

View File

@ -0,0 +1 @@
331fb260f57fed321157ca8e5731a507c8d2f360b34c1204a5749c1c0f59b2ca

View File

@ -0,0 +1 @@
4176f5db8e36d9e99543b4998127d0e893f08599c3b045ae3aeeef3a1c6f3dc007259311158abf8f5f06d2f57a4d65888726ee2e943d5f029e8871ec482e0ce5

View File

@ -3,11 +3,12 @@
<groupId>com.chuangketie.jk</groupId> <groupId>com.chuangketie.jk</groupId>
<artifactId>lib_base</artifactId> <artifactId>lib_base</artifactId>
<versioning> <versioning>
<latest>1.0.0</latest> <latest>1.0.1</latest>
<release>1.0.0</release> <release>1.0.1</release>
<versions> <versions>
<version>1.0.0</version> <version>1.0.0</version>
<version>1.0.1</version>
</versions> </versions>
<lastUpdated>20251119064448</lastUpdated> <lastUpdated>20251120060846</lastUpdated>
</versioning> </versioning>
</metadata> </metadata>

View File

@ -1 +1 @@
60d1aa120c1b5201f5df48fde432b5fb 1cd0bf6251b926a7ba05758f7a92d0dc

View File

@ -1 +1 @@
e2513918c42a8a57a3589f416c0a2109880a4598 822ab63ae308bff16409d179ff48db4c509db9ca

View File

@ -1 +1 @@
e64c8ca7470c03e17ceb73b89af8026a02b60fca38f32b915d55fd4c4c87deca c07078f400f601517dd429a72cac5d374a9016a2f0eeded2ec93f670b2ce8c9a

View File

@ -1 +1 @@
bc22992cb482599fde455ec63b9253558341f770fffb7429c7110dc8a39bbd3f46652646413afea4bfbd1adfbf8916ae390ab5b9ca8600778697505109b83b7b ec29bbc4fcc2e91b7cbeeeadc5b22d342dc46037aba617bd2c2f671322707554988abd94ab3ee73a22ddca0cdaee4799a64a219502201068859e89d801c086c2