添加了电话状态监听.
authorTrillGates <williamtrillgates@gmail.com>
Thu, 21 Oct 2021 11:56:11 +0000 (19:56 +0800)
committerTrillGates <williamtrillgates@gmail.com>
Thu, 21 Oct 2021 11:56:11 +0000 (19:56 +0800)
.idea/vcs.xml [new file with mode: 0644]
app/EQC_G5.jks [new file with mode: 0644]
app/build.gradle
app/proguard-rules.pro
app/src/main/AndroidManifest.xml
app/src/main/aidl/com/android/internal/telephony/ITelephony.aidl [new file with mode: 0644]
app/src/main/java/com/ic/sos/SOSPresenter.java
app/version_code.properties [new file with mode: 0644]

diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644 (file)
index 0000000..94a25f7
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/EQC_G5.jks b/app/EQC_G5.jks
new file mode 100644 (file)
index 0000000..2229d46
Binary files /dev/null and b/app/EQC_G5.jks differ
index f8c7a38..9eb6386 100644 (file)
@@ -2,10 +2,19 @@ plugins {
     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
@@ -26,8 +35,80 @@ android {
         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'
index 481bb43..ee7a614 100644 (file)
 
 # 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
index 8f8ff8f..98d6a3b 100644 (file)
@@ -2,7 +2,15 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.ic.sos">
 
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
+
     <application
+        android:name=".App"
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
diff --git a/app/src/main/aidl/com/android/internal/telephony/ITelephony.aidl b/app/src/main/aidl/com/android/internal/telephony/ITelephony.aidl
new file mode 100644 (file)
index 0000000..ddccf53
--- /dev/null
@@ -0,0 +1,5 @@
+package com.android.internal.telephony;
+interface ITelephony{
+    boolean endCall();
+    void answerRingingCall();
+}
\ No newline at end of file
index 0b4541c..525bad7 100644 (file)
@@ -1,23 +1,37 @@
 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";
@@ -26,25 +40,88 @@ public class SOSPresenter {
     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() {
+
+    }
+
+
     /**
      * 以下内容都在子线程执行了,注意安全
      */
@@ -72,7 +149,6 @@ public class SOSPresenter {
 
 
     public boolean isHasSimCard() {
-        TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         int simState = telephonyManager.getSimState();
         boolean result = true;
         switch (simState) {
@@ -105,11 +181,68 @@ public class SOSPresenter {
         }
     }
 
+
+    //这个拨号到哪里的下标,从第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);
     }
 
     /**
@@ -158,7 +291,7 @@ public class SOSPresenter {
 
 
     public void cleared() {
-        executorService.shutdown();
         phoneNumList.clear();
+        mContext.unregisterReceiver(stateReceiver);
     }
 }
diff --git a/app/version_code.properties b/app/version_code.properties
new file mode 100644 (file)
index 0000000..d8f639d
--- /dev/null
@@ -0,0 +1,2 @@
+#Thu Jul 30 17:39:35 CST 2020
+VERSION_CODE=41