diff --git a/app/build.gradle b/app/build.gradle index 4e84a744..e11bcf0c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,9 +27,9 @@ android { versionCode versionProps['versionCode'].toInteger() versionName versionProps['versionName'] testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - //ndk { - // abiFilters 'armeabi-v7a', 'x86_64' - //} + ndk { + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'//, 'x86_64' + } } lintOptions { checkReleaseBuilds false @@ -67,11 +67,11 @@ android { abi { enable true reset() - include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + include 'armeabi-v7a', 'arm64-v8a', 'x86'//, 'x86_64' universalApk true } } - def abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4, 'universal': 5] + def abiCodes = ['universal': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'x86': 4, 'x86_64': 5] android.applicationVariants.all { variant -> // Assigns a different version code for each output APK. variant.outputs.each { @@ -79,7 +79,7 @@ android { def date = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08")) def abiName = output.getFilter(com.android.build.OutputFile.ABI) if (abiName == null) abiName = "universal" - output.versionCodeOverride = abiCodes.get(abiName, 0) * 10000 + variant.versionCode + output.versionCodeOverride = abiCodes.get(abiName, 0) * 100000 + variant.versionCode output.outputFileName = "SmsForwarder_${variant.name}_${versionName}_${output.versionCodeOverride}_${date}_${abiName}.apk" } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c2d85abf..cd89c021 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,6 +40,9 @@ + + + permissions, boolean all) { + if (all) { + Toast.makeText(getBaseContext(), R.string.toast_granted_all, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getBaseContext(), R.string.toast_granted_part, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(true); + } + + @Override + public void onDenied(List permissions, boolean never) { + if (never) { + Toast.makeText(getBaseContext(), R.string.toast_denied_never, Toast.LENGTH_SHORT).show(); + // 如果是被永久拒绝就跳转到应用权限系统设置页面 + XXPermissions.startPermissionActivity(AboutActivity.this, permissions); + } else { + Toast.makeText(getBaseContext(), R.string.toast_denied, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(false); + } + }); + final TextView version_now = findViewById(R.id.version_now); Button check_version_now = findViewById(R.id.check_version_now); try { diff --git a/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java b/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java index d9d78cd4..b9e772c4 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java +++ b/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java @@ -27,6 +27,9 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.hjq.permissions.OnPermissionCallback; +import com.hjq.permissions.Permission; +import com.hjq.permissions.XXPermissions; import com.idormy.sms.forwarder.adapter.LogAdapter; import com.idormy.sms.forwarder.model.vo.LogVo; import com.idormy.sms.forwarder.sender.HttpServer; @@ -140,6 +143,53 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I //检查权限是否获取 PackageManager pm = getPackageManager(); CommonUtil.CheckPermission(pm, this); + XXPermissions.with(this) + // 接收短信 + .permission(Permission.RECEIVE_SMS) + // 发送短信 + .permission(Permission.SEND_SMS) + // 读取短信 + .permission(Permission.READ_SMS) + // 读取电话状态 + .permission(Permission.READ_PHONE_STATE) + // 读取手机号码 + .permission(Permission.READ_PHONE_NUMBERS) + // 读取通话记录 + .permission(Permission.READ_CALL_LOG) + // 读取联系人 + .permission(Permission.READ_CONTACTS) + // 储存权限 + .permission(Permission.Group.STORAGE) + // 申请安装包权限 + //.permission(Permission.REQUEST_INSTALL_PACKAGES) + // 申请通知栏权限 + .permission(Permission.NOTIFICATION_SERVICE) + // 申请系统设置权限 + //.permission(Permission.WRITE_SETTINGS) + .request(new OnPermissionCallback() { + + @Override + public void onGranted(List permissions, boolean all) { + if (all) { + Toast.makeText(getBaseContext(), R.string.toast_granted_all, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getBaseContext(), R.string.toast_granted_part, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(true); + } + + @Override + public void onDenied(List permissions, boolean never) { + if (never) { + Toast.makeText(getBaseContext(), R.string.toast_denied_never, Toast.LENGTH_SHORT).show(); + // 如果是被永久拒绝就跳转到应用权限系统设置页面 + XXPermissions.startPermissionActivity(MainActivity.this, permissions); + } else { + Toast.makeText(getBaseContext(), R.string.toast_denied, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(false); + } + }); //计算浮动按钮位置 FloatingActionButton btnFloat = findViewById(R.id.btnCleanLog); diff --git a/app/src/main/java/com/idormy/sms/forwarder/MyApplication.java b/app/src/main/java/com/idormy/sms/forwarder/MyApplication.java index 804c6102..91adb2e7 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/MyApplication.java +++ b/app/src/main/java/com/idormy/sms/forwarder/MyApplication.java @@ -10,11 +10,15 @@ import android.content.SharedPreferences; import android.os.Build; import android.util.Log; +import com.hjq.permissions.XXPermissions; +import com.hjq.toast.ToastUtils; +import com.hjq.toast.style.WhiteToastStyle; import com.idormy.sms.forwarder.receiver.SimStateReceiver; import com.idormy.sms.forwarder.sender.SendHistory; import com.idormy.sms.forwarder.service.BatteryService; import com.idormy.sms.forwarder.service.FrontService; import com.idormy.sms.forwarder.utils.Define; +import com.idormy.sms.forwarder.utils.PermissionInterceptor; import com.idormy.sms.forwarder.utils.PhoneUtils; import com.idormy.sms.forwarder.utils.SettingUtil; import com.idormy.sms.forwarder.utils.SharedPreferencesHelper; @@ -51,6 +55,11 @@ public class MyApplication extends Application { CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(getApplicationContext()); + // 初始化吐司工具类 + ToastUtils.init(this, new WhiteToastStyle()); + // 设置权限申请拦截器(全局设置) + XXPermissions.setInterceptor(new PermissionInterceptor()); + //友盟统计 sharedPreferencesHelper = new SharedPreferencesHelper(this, "umeng"); //设置LOG开关,默认为false diff --git a/app/src/main/java/com/idormy/sms/forwarder/SettingActivity.java b/app/src/main/java/com/idormy/sms/forwarder/SettingActivity.java index 592257ee..542f4d0c 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/SettingActivity.java +++ b/app/src/main/java/com/idormy/sms/forwarder/SettingActivity.java @@ -28,6 +28,9 @@ import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import com.hjq.permissions.OnPermissionCallback; +import com.hjq.permissions.Permission; +import com.hjq.permissions.XXPermissions; import com.idormy.sms.forwarder.receiver.RebootBroadcastReceiver; import com.idormy.sms.forwarder.sender.HttpServer; import com.idormy.sms.forwarder.sender.SenderUtil; @@ -133,9 +136,45 @@ public class SettingActivity extends AppCompatActivity { switch_enable_sms.setChecked(SettingUtil.getSwitchEnableSms()); switch_enable_sms.setOnCheckedChangeListener((buttonView, isChecked) -> { - //TODO:校验使用短信转发必备的权限 - SettingUtil.switchEnableSms(isChecked); Log.d(TAG, "switchEnableSms:" + isChecked); + if (isChecked) { + //检查权限是否获取 + PackageManager pm = getPackageManager(); + CommonUtil.CheckPermission(pm, this); + XXPermissions.with(this) + // 接收短信 + .permission(Permission.RECEIVE_SMS) + // 发送短信 + .permission(Permission.SEND_SMS) + // 读取短信 + .permission(Permission.READ_SMS) + .request(new OnPermissionCallback() { + + @Override + public void onGranted(List permissions, boolean all) { + if (all) { + Toast.makeText(getBaseContext(), R.string.toast_granted_all, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getBaseContext(), R.string.toast_granted_part, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(true); + } + + @Override + public void onDenied(List permissions, boolean never) { + if (never) { + Toast.makeText(getBaseContext(), R.string.toast_denied_never, Toast.LENGTH_SHORT).show(); + // 如果是被永久拒绝就跳转到应用权限系统设置页面 + XXPermissions.startPermissionActivity(SettingActivity.this, permissions); + } else { + Toast.makeText(getBaseContext(), R.string.toast_denied, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(false); + } + }); + } else { + SettingUtil.switchEnableSms(false); + } }); } @@ -154,9 +193,47 @@ public class SettingActivity extends AppCompatActivity { return; } - //TODO:校验使用来电转发必备的权限 - SettingUtil.switchEnablePhone(isChecked); Log.d(TAG, "switchEnablePhone:" + isChecked); + if (isChecked) { + //检查权限是否获取 + PackageManager pm = getPackageManager(); + CommonUtil.CheckPermission(pm, this); + XXPermissions.with(this) + // 读取电话状态 + .permission(Permission.READ_PHONE_STATE) + // 读取手机号码 + .permission(Permission.READ_PHONE_NUMBERS) + // 读取通话记录 + .permission(Permission.READ_CALL_LOG) + // 读取联系人 + .permission(Permission.READ_CONTACTS) + .request(new OnPermissionCallback() { + + @Override + public void onGranted(List permissions, boolean all) { + if (all) { + Toast.makeText(getBaseContext(), R.string.toast_granted_all, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getBaseContext(), R.string.toast_granted_part, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(true); + } + + @Override + public void onDenied(List permissions, boolean never) { + if (never) { + Toast.makeText(getBaseContext(), R.string.toast_denied_never, Toast.LENGTH_SHORT).show(); + // 如果是被永久拒绝就跳转到应用权限系统设置页面 + XXPermissions.startPermissionActivity(SettingActivity.this, permissions); + } else { + Toast.makeText(getBaseContext(), R.string.toast_denied, Toast.LENGTH_SHORT).show(); + } + SettingUtil.switchEnableSms(false); + } + }); + } else { + SettingUtil.switchEnablePhone(false); + } }); check_box_call_type_1.setOnCheckedChangeListener((buttonView, isChecked) -> { diff --git a/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.java b/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.java index 7d0d382f..e0728fc2 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.java +++ b/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.java @@ -98,7 +98,7 @@ public class PhoneStateReceiver extends BroadcastReceiver { String viaNumber = callInfo.getViaNumber(); //来源号码 //卡槽判断:获取卡槽失败时,默认为卡槽1 - String simInfo = ""; + String simInfo; int simId = 1; Log.d(TAG, "getSubscriptionId = " + callInfo.getSubscriptionId()); //TODO:这里的SubscriptionId跟短信的不一样 if (callInfo.getSubscriptionId() != -1) { diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/PermissionInterceptor.java b/app/src/main/java/com/idormy/sms/forwarder/utils/PermissionInterceptor.java new file mode 100644 index 00000000..1bf5fe14 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/PermissionInterceptor.java @@ -0,0 +1,282 @@ +package com.idormy.sms.forwarder.utils; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; + +import androidx.appcompat.app.AlertDialog; + +import com.hjq.permissions.IPermissionInterceptor; +import com.hjq.permissions.OnPermissionCallback; +import com.hjq.permissions.Permission; +import com.hjq.permissions.XXPermissions; +import com.hjq.toast.ToastUtils; +import com.idormy.sms.forwarder.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/XXPermissions + * time : 2021/01/04 + * desc : 权限申请拦截器 + */ +@SuppressWarnings({"deprecation", "CommentedOutCode"}) +public final class PermissionInterceptor implements IPermissionInterceptor { + +// @Override +// public void requestPermissions(Activity activity, OnPermissionCallback callback, List allPermissions) { +// // 这里的 Dialog 只是示例,没有用 DialogFragment 来处理 Dialog 生命周期 +// new AlertDialog.Builder(activity) +// .setTitle(R.string.common_permission_hint) +// .setMessage(R.string.common_permission_message) +// .setPositiveButton(R.string.common_permission_granted, new DialogInterface.OnClickListener() { +// +// @Override +// public void onClick(DialogInterface dialog, int which) { +// dialog.dismiss(); +// PermissionFragment.beginRequest(activity, new ArrayList<>(allPermissions), PermissionInterceptor.this, callback); +// } +// }) +// .setNegativeButton(R.string.common_permission_denied, new DialogInterface.OnClickListener() { +// +// @Override +// public void onClick(DialogInterface dialog, int which) { +// dialog.dismiss(); +// } +// }) +// .show(); +// } + + @Override + public void grantedPermissions(Activity activity, List allPermissions, List grantedPermissions, + boolean all, OnPermissionCallback callback) { + if (callback != null) { + callback.onGranted(grantedPermissions, all); + } + } + + @SuppressWarnings("ConstantConditions") + @Override + public void deniedPermissions(Activity activity, List allPermissions, List deniedPermissions, + boolean never, OnPermissionCallback callback) { + if (callback != null) { + callback.onDenied(deniedPermissions, never); + } + + if (never) { + showPermissionDialog(activity, deniedPermissions); + return; + } + + if (deniedPermissions.size() == 1 && Permission.ACCESS_BACKGROUND_LOCATION.equals(deniedPermissions.get(0))) { + ToastUtils.show(R.string.common_permission_fail_4); + return; + } + + ToastUtils.show(R.string.common_permission_fail_1); + + if (callback == null) { + return; + } + callback.onDenied(deniedPermissions, never); + } + + /** + * 显示授权对话框 + */ + protected void showPermissionDialog(Activity activity, List permissions) { + // 这里的 Dialog 只是示例,没有用 DialogFragment 来处理 Dialog 生命周期 + new AlertDialog.Builder(activity) + .setTitle(R.string.common_permission_alert) + .setCancelable(false) + .setMessage(getPermissionHint(activity, permissions)) + .setPositiveButton(R.string.common_permission_goto, (dialog, which) -> { + dialog.dismiss(); + XXPermissions.startPermissionActivity(activity, permissions); + }) + .show(); + } + + /** + * 根据权限获取提示 + */ + protected String getPermissionHint(Context context, List permissions) { + if (permissions == null || permissions.isEmpty()) { + return context.getString(R.string.common_permission_fail_2); + } + + List hints = new ArrayList<>(); + for (String permission : permissions) { + switch (permission) { + case Permission.READ_EXTERNAL_STORAGE: + case Permission.WRITE_EXTERNAL_STORAGE: + case Permission.MANAGE_EXTERNAL_STORAGE: { + String hint = context.getString(R.string.common_permission_storage); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.CAMERA: { + String hint = context.getString(R.string.common_permission_camera); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.RECORD_AUDIO: { + String hint = context.getString(R.string.common_permission_microphone); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.ACCESS_FINE_LOCATION: + case Permission.ACCESS_COARSE_LOCATION: + case Permission.ACCESS_BACKGROUND_LOCATION: { + String hint; + if (!permissions.contains(Permission.ACCESS_FINE_LOCATION) && + !permissions.contains(Permission.ACCESS_COARSE_LOCATION)) { + hint = context.getString(R.string.common_permission_location_background); + } else { + hint = context.getString(R.string.common_permission_location); + } + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.BLUETOOTH_SCAN: + case Permission.BLUETOOTH_CONNECT: + case Permission.BLUETOOTH_ADVERTISE: { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + String hint = context.getString(R.string.common_permission_bluetooth); + if (!hints.contains(hint)) { + hints.add(hint); + } + } + break; + } + case Permission.READ_PHONE_STATE: + case Permission.CALL_PHONE: + case Permission.ADD_VOICEMAIL: + case Permission.USE_SIP: + case Permission.READ_PHONE_NUMBERS: + case Permission.ANSWER_PHONE_CALLS: { + String hint = context.getString(R.string.common_permission_phone); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.GET_ACCOUNTS: + case Permission.READ_CONTACTS: + case Permission.WRITE_CONTACTS: { + String hint = context.getString(R.string.common_permission_contacts); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.READ_CALENDAR: + case Permission.WRITE_CALENDAR: { + String hint = context.getString(R.string.common_permission_calendar); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.READ_CALL_LOG: + case Permission.WRITE_CALL_LOG: + case Permission.PROCESS_OUTGOING_CALLS: { + String hint = context.getString(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? + R.string.common_permission_call_log : R.string.common_permission_phone); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.BODY_SENSORS: { + String hint = context.getString(R.string.common_permission_sensors); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.ACTIVITY_RECOGNITION: { + String hint = context.getString(R.string.common_permission_activity_recognition); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.SEND_SMS: + case Permission.RECEIVE_SMS: + case Permission.READ_SMS: + case Permission.RECEIVE_WAP_PUSH: + case Permission.RECEIVE_MMS: { + String hint = context.getString(R.string.common_permission_sms); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.REQUEST_INSTALL_PACKAGES: { + String hint = context.getString(R.string.common_permission_install); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.SYSTEM_ALERT_WINDOW: { + String hint = context.getString(R.string.common_permission_window); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.WRITE_SETTINGS: { + String hint = context.getString(R.string.common_permission_setting); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.NOTIFICATION_SERVICE: { + String hint = context.getString(R.string.common_permission_notification); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + case Permission.PACKAGE_USAGE_STATS: { + String hint = context.getString(R.string.common_permission_task); + if (!hints.contains(hint)) { + hints.add(hint); + } + break; + } + default: + break; + } + } + + if (!hints.isEmpty()) { + StringBuilder builder = new StringBuilder(); + for (String text : hints) { + if (builder.length() == 0) { + builder.append(text); + } else { + builder.append("、") + .append(text); + } + } + builder.append(" "); + return context.getString(R.string.common_permission_fail_3, builder.toString()); + } + + return context.getString(R.string.common_permission_fail_2); + } +} \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 5a4c3b2c..51a1e27f 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -424,4 +424,8 @@ 系统设置权限 通知栏权限 查看使用情况权限 + 获取所有必需的权限成功! + 获取部分权限成功,但部分权限未正常授予,APP部分功能可能受限! + 被永久拒绝授权,请前往系统设置手动授予权限! + 获取必需的权限失败,APP功能可能受限! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f35cb212..20b3d843 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -423,4 +423,8 @@ 系统设置权限 通知栏权限 查看使用情况权限 + 获取所有必需的权限成功! + 获取部分权限成功,但部分权限未正常授予,APP部分功能可能受限! + 被永久拒绝授权,请前往系统设置手动授予权限! + 获取必需的权限失败,APP功能可能受限!