id 'com.android.application'
}
+// APK 名称
+static def getApkBaseName() {
+ return "ICSOS"
+}
+
+
android {
compileSdkVersion 31
buildToolsVersion "30.0.3"
+ def currentVersionCode = getVersionCode()
+ def currentVersionName = releaseTime()
+
defaultConfig {
applicationId "com.ic.sos"
minSdkVersion 19
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+
+ signingConfigs {
+ key {
+ storeFile file("EQC_G5.jks")
+ storePassword "123456"
+ keyAlias "watch_g5"
+ keyPassword "123456"
+ }
+ debug {
+ storeFile file('EQC_G5.jks')
+ storePassword '123456'
+ keyAlias = 'watch_g5'
+ keyPassword '123456'
+ }
+
+ buildTypes {
+ release {
+ zipAlignEnabled true // zip 对齐优化
+ shrinkResources true // 移除未使用的资源文件
+ signingConfig signingConfigs.key
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ renderscriptDebuggable = false
+ debuggable = false
+ jniDebuggable = false
+ manifestPlaceholders = [:]
+ }
+
+ debug {
+ signingConfig signingConfigs.key
+ // 启用 Android 插件压缩器
+// minifyEnabled true
+// useProguard false
+ }
+ // 适用 gradle 3.0 及以上版本
+ android.applicationVariants.all { variant ->
+ variant.outputs.all {
+ if (variant.buildType.name == "release") {
+ outputFileName = "${getApkBaseName()}_V${currentVersionCode}_${currentVersionName}_release.apk"
+ } else {
+ outputFileName = "${getApkBaseName()}_V${currentVersionCode}_${currentVersionName}_debug.apk"
+ }
+ }
+ }
+ }
+ }
+}
+
+def getVersionCode() {
+ def versionCodeFile = file('version_code.properties')
+ if (versionCodeFile.canRead()) {
+ Properties properties = new Properties()
+ properties.load(new FileInputStream(versionCodeFile))
+ def versionCode = properties['VERSION_CODE'].toInteger()
+//读取version_code.properties文件存放的版本号。
+ def runTasks = gradle.startParameter.taskNames
+ def task = ':app:assembleRelease'
+ if (task in runTasks) {
+ properties['VERSION_CODE'] = (++versionCode).toString()
+ properties.store(versionCodeFile.newWriter(), null)
+ }
+ return versionCode
+ } else {
+ throw new GradleException("无法读取version_code.properties文件!")
+ }
}
+def releaseTime() {
+
+ return new Date().format("yyMMddHHmm", TimeZone.getTimeZone("GMT+08:00"))
+
+}
+
+
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
+#-renamesourcefileattribute SourceFile
+
+
+-keep class com.ic.iclooger.CrashHandler
+-keep class com.ic.iclooger.ICLogger
+
+-keepclassmembers class com.ic.iclooger.StorageUtils {
+ public static <fields>;
+ public static <methods>;
+ public <methods>;
+}
+
+-keepclassmembers class com.ic.iclooger.SystemUtils {
+ public static <fields>;
+ public static <methods>;
+ public <methods>;
+}
+
+-keep class com.facebook.** { *; }
+-keep class com.mediatek.** { *; }
+-keep class org.apache.** { *; }
+-keep class okio.Okio.** { *; }
+-keep class java.nio.** { *; }
+-keep class com.android.** { *; }
+-keep class android.** { *; }
+-keep class android.ddm.** { *; }
+-keep class com.alibaba.fastjson.** { *; }
+-keep class com.eqc.watch.launcher.WatchApplication { *; }
+#-keep class com.enqualcomm.support.EqcAlgorithmNative { *; }
+
+-dontwarn com.facebook.**
+-dontwarn com.mediatek.**
+-dontwarn java.nio.**
+-dontwarn com.alibaba.fastjson.**
+-dontwarn android.**
+-dontwarn com.android.**
+-dontwarn rx.internal.**
+
+-keep class com.ic.** { *; }
\ No newline at end of file
package com.ic.sos;
import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.net.wifi.aware.IdentityChangedListener;
+import android.nfc.cardemulation.HostApduService;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
+import com.android.internal.telephony.ITelephony;
import com.ic.iclooger.ICLogger;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
+
public class SOSPresenter {
public static final String PATH = "members";
public static final String MEMBERS = "content://" + AUTHORITY + "/" + PATH;
private final Context mContext;
- private final ExecutorService executorService;
//每组号码轮询的次数
private int loopTime;
//这个值有三种,请看获取值的方法说明
private String callWay;
//当这个值为-1的时候,拨通了是可以接听通话的,否则到时就会挂断
private int callDuration;
+ private final Handler handler;
+ private final TelephonyManager telephonyManager;
+ private final PhoneStateReceiver stateReceiver;
public SOSPresenter(Context context) {
this.mContext = context;
- executorService = Executors.newSingleThreadExecutor();
+ telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction("com.ic.action.PHONE_STATE");
+ stateReceiver = new PhoneStateReceiver();
+ mContext.registerReceiver(stateReceiver, intentFilter);
+ HandlerThread handlerThread = new HandlerThread("IC:SOS");
+ handlerThread.start();
+ handler = new Handler(handlerThread.getLooper());
//获取配置数据
initLoopTime();
initCallWays();
initCallDuration();
ICLogger.d("loopTime ==> " + loopTime + " callWay ==> " + callWay + " callDuration ==> " + callDuration);
- executorService.execute(this::sosTask);
+ handler.post(this::sosTask);
}
+
+ //是否有接听
+ private boolean isActive = false;
+
+ //通话结束
+ public static final int STATE_END = 9;
+ //正在挂断
+ public static final int STATE_HAND_UP_ING = 8;
+ //正在拨号
+ public static final int STATE_CALLING = 5;
+ //正在通话
+ public static final int STATE_TALKING = 2;
+ //来电
+ public static final int STATE_INCOME = 3;
+
+
+ private class PhoneStateReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state = intent.getIntExtra("state", -1);
+ String callStateLabel = intent.getStringExtra("callStateLabel");
+ ICLogger.d("callStateLabel ==> " + callStateLabel);
+ ICLogger.d("state ==> " + state);
+ //判断case
+ switch (state) {
+ case STATE_END:
+ ICLogger.d("通话结束");
+ break;
+ case STATE_HAND_UP_ING:
+ ICLogger.d("正在挂断");
+ break;
+ case STATE_TALKING:
+ ICLogger.d("正在通话中...");
+ break;
+ case STATE_CALLING:
+ ICLogger.d("正在呼叫");
+ break;
+ case STATE_INCOME:
+ ICLogger.d("来电...");
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * 根据条件进行下一轮拨号
+ */
+ private void checkNextCall() {
+
+ }
+
+
/**
* 以下内容都在子线程执行了,注意安全
*/
public boolean isHasSimCard() {
- TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
int simState = telephonyManager.getSimState();
boolean result = true;
switch (simState) {
}
}
+
+ //这个拨号到哪里的下标,从第1个开始
+ private int currentCallIndex = 0;
+
+ private int currentLoopGroupTime = 0;
+
/**
* 根据策略去拨号
*/
private void doSosCall() {
+ //策略因素有:
+ //轮询组的次数(判断在此次通话结束以后)
+ //拨号策略(判断在此次通话结束以后,不管是挂断还是超时)
+ //拨号超时时长(完成)
+ //先监听电话的状态,这个在类创建的时候就已经监听的了
+ if (phoneNumList.size() > currentCallIndex) {
+ String targetPhoneNum = phoneNumList.get(currentCallIndex);
+ callPhone(targetPhoneNum);
+ //开始准备超时
+ if (callDuration != -1) {
+ //需要按时挂断
+ handler.removeCallbacks(endCallTask);
+ handler.postDelayed(endCallTask, callDuration * 1000);
+ }
+ }
+ if (currentCallIndex == phoneNumList.size() - 1) {
+ //拨号当前组最后一个号码了
+ currentLoopGroupTime++;
+ }
+ }
+ //挂断电话,在挂断电话回调的地方,判断有没有必要进行下一次轮询
+ private final Runnable endCallTask = this::endCall;
+
+ private void endCall() {
+ try {
+ Method method = Class.forName("android.os.ServiceManager")
+ .getMethod("getService", String.class);
+ IBinder binder = (IBinder) method.invoke(null, new Object[]{Context.TELEPHONY_SERVICE});
+ ITelephony telephony = ITelephony.Stub.asInterface(binder);
+ telephony.endCall();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void callPhone(String phone) {
+ if (telephonyManager.getCallState() != CALL_STATE_IDLE) {
+ return;
+ }
+ if (TextUtils.isEmpty(phone) || phone.length() <= 1) {
+ return;
+ }
+ if (!PhoneNumberUtils.isEmergencyNumber(phone)) {
+ if (!isHasSimCard()) {
+ return;
+ }
+ }
+ Uri callUri = Uri.parse("tel://" + phone);
+ Intent callIntent = new Intent("android.intent.action.CALL_PRIVILEGED", callUri);
+ callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ mContext.startActivity(callIntent);
}
/**
public void cleared() {
- executorService.shutdown();
phoneNumList.clear();
+ mContext.unregisterReceiver(stateReceiver);
}
}