diff --git a/app/build.gradle b/app/build.gradle
index e04528c..440fdc7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,8 +15,8 @@ android {
applicationId "com.uiuios.aios"
minSdkVersion 24
targetSdkVersion 29
- versionCode 47
- versionName "5.6"
+ versionCode 48
+ versionName "5.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f6c84c4..e19195f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -130,15 +130,15 @@
android:resumeWhilePausing="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan">
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/uiuios/aios/activity/callwechat/CallWechatActivity.java b/app/src/main/java/com/uiuios/aios/activity/callwechat/CallWechatActivity.java
new file mode 100644
index 0000000..71a6964
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/activity/callwechat/CallWechatActivity.java
@@ -0,0 +1,85 @@
+package com.uiuios.aios.activity.callwechat;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.view.Gravity;
+import android.view.View;
+
+import com.uiuios.aios.R;
+import com.uiuios.aios.base.mvvm.BaseMvvmActivity;
+import com.uiuios.aios.bean.WechatInfo;
+import com.uiuios.aios.databinding.ActivityWechatCallBinding;
+import com.uiuios.aios.service.WeAccessibilityService;
+
+public class CallWechatActivity extends BaseMvvmActivity {
+
+ private WechatInfo mWechatInfo;
+
+ @Override
+ public boolean setfitWindow() {
+ return true;
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_wechat_call;
+ }
+
+ @Override
+ protected void initDataBinding() {
+ mViewModel.setCtx(this);
+ mViewModel.setVDBinding(mViewDataBinding);
+ mViewModel.setLifecycle(getLifecycleSubject());
+ mViewDataBinding.setClick(new BtnClick());
+ }
+
+ @Override
+ protected void initView() {
+ getWindow().setGravity(Gravity.BOTTOM);
+ }
+
+ @Override
+ protected void initData() {
+ Intent intent = getIntent();
+ if (intent != null) {
+ WechatInfo wechatInfo = (WechatInfo) intent.getSerializableExtra("WechatInfo");
+ if (wechatInfo != null) {
+ mWechatInfo = wechatInfo;
+ mViewDataBinding.setWehchatInfo(wechatInfo);
+ }
+ }
+ }
+
+
+
+ public class BtnClick {
+ public void callPhone(View view) {
+ Intent dialIntent = new Intent(Intent.ACTION_CALL);
+ String phone = mWechatInfo.getMobile();
+ Uri data = Uri.parse("tel:" + phone);
+ dialIntent.setData(data);
+ startActivity(dialIntent);
+ finish();
+ }
+
+ public void callWechatVideo(View view) {
+ Intent intent = new Intent(CallWechatActivity.this, WeAccessibilityService.class);
+ intent.putExtra("WechatInfo", mWechatInfo);
+ intent.putExtra("call_type", WeAccessibilityService.TYPE_VIDEO);
+ startService(intent);
+ finish();
+ }
+
+ public void callWechatVoice(View view) {
+ Intent intent = new Intent(CallWechatActivity.this, WeAccessibilityService.class);
+ intent.putExtra("WechatInfo", mWechatInfo);
+ intent.putExtra("call_type", WeAccessibilityService.TYPE_VOICE);
+ startService(intent);
+ finish();
+ }
+
+ public void exit(View view) {
+ finish();
+ }
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/activity/callwechat/CallWechatViewModel.java b/app/src/main/java/com/uiuios/aios/activity/callwechat/CallWechatViewModel.java
new file mode 100644
index 0000000..fc11135
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/activity/callwechat/CallWechatViewModel.java
@@ -0,0 +1,18 @@
+package com.uiuios.aios.activity.callwechat;
+
+import com.trello.rxlifecycle4.android.ActivityEvent;
+import com.uiuios.aios.base.mvvm.BaseViewModel;
+import com.uiuios.aios.databinding.ActivityWechatCallBinding;
+
+public class CallWechatViewModel extends BaseViewModel {
+
+ @Override
+ public ActivityWechatCallBinding getVDBinding() {
+ return binding;
+ }
+
+ @Override
+ public void onDestroy() {
+
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/activity/wecontact/AddWechatContactActivity.java b/app/src/main/java/com/uiuios/aios/activity/wecontact/AddWechatContactActivity.java
new file mode 100644
index 0000000..1b29394
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/activity/wecontact/AddWechatContactActivity.java
@@ -0,0 +1,130 @@
+package com.uiuios.aios.activity.wecontact;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.RequestOptions;
+import com.hjq.toast.Toaster;
+import com.luck.picture.lib.basic.PictureSelector;
+import com.luck.picture.lib.config.SelectMimeType;
+import com.luck.picture.lib.entity.LocalMedia;
+import com.luck.picture.lib.interfaces.OnResultCallbackListener;
+import com.uiuios.aios.R;
+import com.uiuios.aios.base.GlideEngine;
+import com.uiuios.aios.base.mvvm.BaseMvvmActivity;
+import com.uiuios.aios.bean.WechatInfo;
+import com.uiuios.aios.databinding.ActivityAddWechatContactBinding;
+import com.uiuios.aios.db.ContactManager;
+import com.uiuios.aios.utils.ScreenUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+
+public class AddWechatContactActivity extends BaseMvvmActivity {
+ private static final String TAG = AddWechatContactActivity.class.getSimpleName();
+
+ private String mPictrueFilePath;
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_add_wechat_contact;
+ }
+
+ @Override
+ protected void initDataBinding() {
+ mViewModel.setCtx(this);
+ mViewModel.setVDBinding(mViewDataBinding);
+ mViewModel.setLifecycle(getLifecycleSubject());
+ mViewDataBinding.setClick(new BtnClick());
+ }
+
+ @Override
+ protected void initView() {
+
+ }
+
+ @Override
+ protected void initData() {
+
+ }
+
+
+ private void openSelector() {
+ PictureSelector.create(AddWechatContactActivity.this)
+ .openGallery(SelectMimeType.ofAll())
+ .setSelectionMode(1)
+ .setImageEngine(GlideEngine.createGlideEngine())
+ .forResult(new OnResultCallbackListener() {
+ @Override
+ public void onResult(ArrayList result) {
+ mPictrueFilePath = result.get(0).getRealPath();
+ File file = new File(mPictrueFilePath);
+ if (file.exists()) {
+ RequestOptions options = new RequestOptions().transform(new RoundedCorners(ScreenUtil.dip2px(AddWechatContactActivity.this, 8F)));
+ Glide.with(mViewDataBinding.nvAvatar).load(file).apply(options).into(mViewDataBinding.nvAvatar);
+ } else {
+ mPictrueFilePath = "";
+ }
+ }
+
+ @Override
+ public void onCancel() {
+ Log.e(TAG, "onCancel: ");
+ }
+ });
+ }
+
+ public class BtnClick {
+ public void selectPic(View view){
+ openSelector();
+ }
+
+ public void exit(View view){
+ finish();
+ }
+
+ public void addContact(View view) {
+ if (TextUtils.isEmpty(mViewDataBinding.etName.getText())) {
+ Toaster.show("请输入微信备注");
+ return;
+ }
+ if (TextUtils.isEmpty(mViewDataBinding.etGroup.getText())) {
+ Toaster.show("请输入微信群组标签");
+ return;
+ }
+ if (TextUtils.isEmpty(mViewDataBinding.etPhone.getText())) {
+ Toaster.show("请输入手机号码");
+ return;
+ }
+// if (TextUtils.isEmpty(mPictrueFilePath)) {
+// Toaster.show("请选择图片");
+// return;
+// }
+ WechatInfo wechatInfo = new WechatInfo();
+ if (!TextUtils.isEmpty(mViewDataBinding.etName.getText())) {
+ wechatInfo.setNickName(mViewDataBinding.etName.getText().toString());
+ }
+ if (!TextUtils.isEmpty(mViewDataBinding.etGroup.getText())) {
+ wechatInfo.setGroupTag(mViewDataBinding.etGroup.getText().toString());
+ }
+ if (!TextUtils.isEmpty(mViewDataBinding.etWechatId.getText())) {
+ wechatInfo.setWechatId(mViewDataBinding.etWechatId.getText().toString());
+ }
+ if (!TextUtils.isEmpty(mViewDataBinding.etPhone.getText())) {
+ wechatInfo.setMobile(mViewDataBinding.etPhone.getText().toString());
+ }
+ if (!TextUtils.isEmpty(mPictrueFilePath)) {
+ wechatInfo.setAvatarPath(mPictrueFilePath);
+ }
+ if (ContactManager.getInstance().addWechatInfo(wechatInfo)) {
+ Toaster.show("添加成功");
+ finish();
+ }
+
+ }
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/activity/wecontact/AddWechatContactViewModel.java b/app/src/main/java/com/uiuios/aios/activity/wecontact/AddWechatContactViewModel.java
new file mode 100644
index 0000000..736b6eb
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/activity/wecontact/AddWechatContactViewModel.java
@@ -0,0 +1,17 @@
+package com.uiuios.aios.activity.wecontact;
+
+import com.trello.rxlifecycle4.android.ActivityEvent;
+import com.uiuios.aios.base.mvvm.BaseViewModel;
+import com.uiuios.aios.databinding.ActivityAddWechatContactBinding;
+
+public class AddWechatContactViewModel extends BaseViewModel {
+ @Override
+ public ActivityAddWechatContactBinding getVDBinding() {
+ return binding;
+ }
+
+ @Override
+ public void onDestroy() {
+
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/adapter/WechatContactAdapter.java b/app/src/main/java/com/uiuios/aios/adapter/WechatContactAdapter.java
new file mode 100644
index 0000000..9154fdf
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/adapter/WechatContactAdapter.java
@@ -0,0 +1,124 @@
+package com.uiuios.aios.adapter;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.hjq.toast.Toaster;
+import com.shehuan.niv.NiceImageView;
+import com.uiuios.aios.R;
+import com.uiuios.aios.activity.callwechat.CallWechatActivity;
+import com.uiuios.aios.alarm.AlarmClockData;
+import com.uiuios.aios.bean.WechatInfo;
+import com.uiuios.aios.db.ContactManager;
+import com.uiuios.aios.dialog.DeleteContactDialog;
+import com.uiuios.aios.dialog.DeleteDialog;
+import com.uiuios.aios.service.WeAccessibilityService;
+import com.uiuios.aios.utils.AccessibilityUtils;
+
+import java.util.List;
+
+public class WechatContactAdapter extends RecyclerView.Adapter {
+ private static final String TAG = WechatContactAdapter.class.getSimpleName();
+
+ private List mContactList;
+ private Context mContext;
+
+ public static final String DIALER_PACKAGE = "com.android.dialer";
+ public static final String DIALER_ADD_CONTACT = "com.uiui.aios.contact.add";
+
+ public void setContactList(List contactList) {
+ this.mContactList = contactList;
+ notifyDataSetChanged();
+ }
+
+ private OnLongClick mOnLongClick;
+
+ public void setOnLongClick(OnLongClick onLongClick) {
+ mOnLongClick = onLongClick;
+ }
+
+ public interface OnLongClick {
+ void setOnLongClickListener(WechatInfo wechatInfo);
+ }
+
+ @NonNull
+ @Override
+ public ContactHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ mContext = parent.getContext();
+ return new ContactHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contact_wechat, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ContactHolder contactHolder, int position) {
+ WechatInfo contact = mContactList.get(position);
+ contactHolder.tv_name.setText(contact.getNickName());
+ contactHolder.tv_phone.setText(contact.getMobile());
+ Glide.with(contactHolder.iv_head).load(contact.getAvatarPath()).error(R.drawable.default_avatar).into(contactHolder.iv_head);
+ contactHolder.root.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (AccessibilityUtils.isAccessibilitySettingsOn(mContext)) {
+ Intent intent = new Intent(mContext, CallWechatActivity.class);
+ intent.putExtra("WechatInfo", contact);
+ mContext.startActivity(intent);
+ } else {
+ Toast.makeText(mContext, "请在无障碍服务中打开 - 关爱守护快捷服务", Toast.LENGTH_LONG).show();
+ mContext.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
+ }
+ }
+ });
+ contactHolder.root.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (mOnLongClick != null) {
+ mOnLongClick.setOnLongClickListener(contact);
+ }
+ return false;
+ }
+ });
+ }
+
+
+ private void checkAccessibility() {
+ if (!AccessibilityUtils.isAccessibilitySettingsOn(mContext)) {
+ Toast.makeText(mContext, "请在无障碍服务中打开 - 关爱守护快捷服务", Toast.LENGTH_LONG).show();
+ mContext.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
+ } else {
+ Log.e(TAG, "checkAccessibility: 无障碍服务已打开");
+ }
+ }
+
+
+ @Override
+ public int getItemCount() {
+ return mContactList == null ? 0 : mContactList.size();
+ }
+
+ static class ContactHolder extends RecyclerView.ViewHolder {
+ ConstraintLayout root;
+ NiceImageView iv_head;
+ TextView tv_name;
+ TextView tv_phone;
+
+ public ContactHolder(@NonNull View itemView) {
+ super(itemView);
+ root = itemView.findViewById(R.id.root);
+ iv_head = itemView.findViewById(R.id.iv_head);
+ tv_name = itemView.findViewById(R.id.tv_name);
+ tv_phone = itemView.findViewById(R.id.tv_phone);
+ }
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/base/BaseApplication.java b/app/src/main/java/com/uiuios/aios/base/BaseApplication.java
index efa056b..3f3b223 100644
--- a/app/src/main/java/com/uiuios/aios/base/BaseApplication.java
+++ b/app/src/main/java/com/uiuios/aios/base/BaseApplication.java
@@ -17,6 +17,7 @@ import com.tencent.bugly.crashreport.CrashReport;
import com.tencent.mmkv.MMKV;
import com.uiuios.aios.BuildConfig;
import com.uiuios.aios.alarm.AlarmUtils;
+import com.uiuios.aios.db.ContactManager;
import com.uiuios.aios.manager.AmapManager;
import com.uiuios.aios.manager.AppManager;
import com.uiuios.aios.manager.AppStatusManager;
@@ -75,6 +76,7 @@ public class BaseApplication extends Application {
} catch (Exception e) {
Log.e(TAG, "onCreate: " + e.getMessage());
}
+ ContactManager.init(this);
}
private void aliyunPushInit() {
diff --git a/app/src/main/java/com/uiuios/aios/bean/WechatInfo.java b/app/src/main/java/com/uiuios/aios/bean/WechatInfo.java
new file mode 100644
index 0000000..4fba7b2
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/bean/WechatInfo.java
@@ -0,0 +1,73 @@
+package com.uiuios.aios.bean;
+
+import androidx.annotation.NonNull;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+
+import java.io.Serializable;
+
+public class WechatInfo implements Serializable {
+ private static final long serialVersionUID = -5241133870198591043L;
+
+ int id ;
+ String nickName;
+ String groupTag;
+ String wechatId;
+ String mobile;
+ String avatarPath;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getNickName() {
+ return nickName;
+ }
+
+ public void setNickName(String nickName) {
+ this.nickName = nickName;
+ }
+
+ public String getGroupTag() {
+ return groupTag;
+ }
+
+ public void setGroupTag(String groupTag) {
+ this.groupTag = groupTag;
+ }
+
+ public String getWechatId() {
+ return wechatId;
+ }
+
+ public void setWechatId(String wechatId) {
+ this.wechatId = wechatId;
+ }
+
+ public String getMobile() {
+ return mobile;
+ }
+
+ public void setMobile(String mobile) {
+ this.mobile = mobile;
+ }
+
+ public String getAvatarPath() {
+ return avatarPath;
+ }
+
+ public void setAvatarPath(String avatarPath) {
+ this.avatarPath = avatarPath;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/config/CommonConfig.java b/app/src/main/java/com/uiuios/aios/config/CommonConfig.java
index a53fa3c..8f614a5 100644
--- a/app/src/main/java/com/uiuios/aios/config/CommonConfig.java
+++ b/app/src/main/java/com/uiuios/aios/config/CommonConfig.java
@@ -64,4 +64,8 @@ public class CommonConfig {
public static final String WEATHER_NOW_KEY = "WEATHER_NOW_JSON_STRING";
public static final String WEATHER_DAILY_KEY = "WEATHER_DAILY_JSON_STRING";
+
+
+ public static final String SETTING_CALL_TYPE_KEY = "setting_call_type_key";
+ public static final String SETTING_AUTOMATIC_ANSWER_KEY = "setting_automatic_answer_key";
}
diff --git a/app/src/main/java/com/uiuios/aios/db/ContactDbHelper.java b/app/src/main/java/com/uiuios/aios/db/ContactDbHelper.java
new file mode 100644
index 0000000..e4d3994
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/db/ContactDbHelper.java
@@ -0,0 +1,54 @@
+package com.uiuios.aios.db;
+
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Environment;
+
+import androidx.annotation.Nullable;
+
+import java.io.File;
+
+public class ContactDbHelper extends SQLiteOpenHelper {
+ public static final String DATABASE_NAME = "contactList.db";
+ public static final String TABLE_NAME = "contact_table";
+
+ private static final int DATABASE_VERSION = 1;
+
+ public ContactDbHelper(Context context) {
+// super(context, DATABASE_FILE_NAME, null, DATABASE_VERSION);
+ super(context, context.getExternalCacheDir() + File.separator + DATABASE_NAME, null, DATABASE_VERSION);
+// super(context, Environment.getExternalStorageDirectory().getPath() + File.separator + DATABASE_FILE_NAME, null, DATABASE_VERSION);
+ }
+
+ public ContactDbHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
+ super(context, name, factory, version);
+ }
+
+ public ContactDbHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version, @Nullable DatabaseErrorHandler errorHandler) {
+ super(context, name, factory, version, errorHandler);
+ }
+
+// public ContactDbHelper(@Nullable Context context, @Nullable String name, int version, @NonNull SQLiteDatabase.OpenParams openParams) {
+// super(context, name, version, openParams);
+// }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
+ "_id integer primary key autoincrement " +
+ ",nick_name varchar unique not null" +
+ ",group_tag varchar not null" +
+ ",wechatid varchar" +
+ ",mobile varchar" +
+ ",avatar_path varchar" +
+ ")"
+ );
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/db/ContactManager.java b/app/src/main/java/com/uiuios/aios/db/ContactManager.java
new file mode 100644
index 0000000..3817d34
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/db/ContactManager.java
@@ -0,0 +1,127 @@
+package com.uiuios.aios.db;
+
+import android.annotation.SuppressLint;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import com.uiuios.aios.bean.WechatInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContactManager {
+ private static String TAG = ContactManager.class.getSimpleName();
+
+ @SuppressLint("StaticFieldLeak")
+ private static ContactManager sInstance;
+ private Context mContext;
+ private ContactDbHelper mDBHelper;
+
+
+ private ContactManager(Context context) {
+ if (context == null) {
+ throw new RuntimeException("Context is NULL");
+ }
+ mContext = context;
+ mDBHelper = new ContactDbHelper(context);
+ }
+
+ public static void init(Context context) {
+ if (sInstance == null) {
+ sInstance = new ContactManager(context);
+ }
+ }
+
+ public static ContactManager getInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("You must be init ContactManager first");
+ }
+ return sInstance;
+ }
+
+ public ContactDbHelper getDBHelper() {
+ if (mDBHelper == null) {
+ mDBHelper = new ContactDbHelper(mContext);
+ }
+ return mDBHelper;
+ }
+
+ public boolean addWechatInfo(WechatInfo wechatInfo) {
+ SQLiteDatabase db = mDBHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put("nick_name", wechatInfo.getNickName());
+ values.put("group_tag", wechatInfo.getGroupTag());
+ values.put("wechatid", wechatInfo.getWechatId());
+ values.put("mobile", wechatInfo.getMobile());
+ values.put("avatar_path", wechatInfo.getAvatarPath());
+// long id = db.insert(ContactDbHelper.TABLE_NAME, null, values);
+ long id = -1;
+ db.beginTransaction();
+ try {
+ id = db.insertWithOnConflict(ContactDbHelper.TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ Log.e(TAG, "addWechatInfo: " + e.getMessage());
+ } finally {
+ db.endTransaction();
+ }
+ return id > 0;
+ }
+
+ public boolean delete(Integer sqlId) {
+ SQLiteDatabase db = mDBHelper.getWritableDatabase();
+ long id = db.delete(ContactDbHelper.TABLE_NAME, "_id=?", new String[]{sqlId.toString()});
+ return id > 0;
+ }
+
+ public boolean deleteAll() {
+ SQLiteDatabase db = mDBHelper.getWritableDatabase();
+ long id = db.delete(ContactDbHelper.TABLE_NAME, "", null);
+ return id > 0;
+ }
+
+ public boolean updateWechatInfo(WechatInfo wechatInfo) {
+ SQLiteDatabase db = mDBHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put("nick_name", wechatInfo.getNickName());
+ values.put("group_tag", wechatInfo.getGroupTag());
+ values.put("wechatid", wechatInfo.getWechatId());
+ values.put("mobile", wechatInfo.getMobile());
+ values.put("avatar_path", wechatInfo.getAvatarPath());
+ long id = db.update(ContactDbHelper.TABLE_NAME, values, "_id=?", new String[]{String.valueOf(wechatInfo.getId())});
+ return id > 0;
+ }
+
+ public List getWechatInfoList() {
+ List wechatInfoList = new ArrayList<>();
+ SQLiteDatabase db = mDBHelper.getReadableDatabase();
+ long time = System.currentTimeMillis();
+ String sql = "select * from " + ContactDbHelper.TABLE_NAME;
+ Cursor cursor = null;
+ try {
+ cursor = db.rawQuery(sql, null);
+ while (cursor.moveToNext()) {
+ WechatInfo wechatInfo = new WechatInfo();
+ wechatInfo.setId(cursor.getInt(cursor.getColumnIndex("_id")));
+ wechatInfo.setNickName(cursor.getString(cursor.getColumnIndex("nick_name")));
+ wechatInfo.setGroupTag(cursor.getString(cursor.getColumnIndex("group_tag")));
+ wechatInfo.setWechatId(cursor.getString(cursor.getColumnIndex("wechatid")));
+ wechatInfo.setMobile(cursor.getString(cursor.getColumnIndex("mobile")));
+ wechatInfo.setAvatarPath(cursor.getString(cursor.getColumnIndex("avatar_path")));
+ wechatInfoList.add(wechatInfo);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ db.close();
+ }
+ Log.e(TAG, "getWechatInfoList: time = " + (System.currentTimeMillis() - time));
+ return wechatInfoList;
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/dialog/DeleteContactDialog.java b/app/src/main/java/com/uiuios/aios/dialog/DeleteContactDialog.java
new file mode 100644
index 0000000..760a2ee
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/dialog/DeleteContactDialog.java
@@ -0,0 +1,251 @@
+package com.uiuios.aios.dialog;
+
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.uiuios.aios.R;
+
+
+/**
+ * description:自定义dialog
+ */
+
+public class DeleteContactDialog extends AlertDialog {
+ /**
+ * 显示的图片
+ */
+ private ImageView imageIv;
+
+ /**
+ * 显示的标题
+ */
+ private TextView titleTv;
+
+ /**
+ * 显示的消息
+ */
+ private TextView messageTv;
+
+ /**
+ * 确认和取消按钮
+ */
+ private TextView positiveBn;
+ private TextView negtiveBn;
+
+ /**
+ * 按钮之间的分割线
+ */
+// private View columnLineView;
+
+ private Context mContext;
+
+ public DeleteContactDialog(Context context) {
+ super(context, R.style.CustomDialog);
+ this.mContext = context;
+ }
+
+ /**
+ * 都是内容数据
+ */
+ private String message;
+ private String title;
+ private String positive, negtive;
+ private int imageResId = -1;
+
+ /**
+ * 底部是否只有一个按钮
+ */
+ private boolean isSingle = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.dialog_delete_contact);
+ //按空白处不能取消动画
+ setCanceledOnTouchOutside(false);
+ //初始化界面控件
+ initView();
+ //初始化界面数据
+ refreshView();
+ //初始化界面控件的事件
+ initEvent();
+ }
+
+ /**
+ * 初始化界面的确定和取消监听器
+ */
+ private void initEvent() {
+ //设置确定按钮被点击后,向外界提供监听
+ positiveBn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (onClickBottomListener != null) {
+ onClickBottomListener.onPositiveClick();
+ }
+ }
+ });
+ //设置取消按钮被点击后,向外界提供监听
+ negtiveBn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (onClickBottomListener != null) {
+ onClickBottomListener.onNegtiveClick();
+ }
+ }
+ });
+ }
+
+ /**
+ * 初始化界面控件的显示数据
+ */
+ private void refreshView() {
+ //如果用户自定了title和message
+ if (!TextUtils.isEmpty(title)) {
+ titleTv.setText(title);
+ titleTv.setVisibility(View.VISIBLE);
+ } else {
+ titleTv.setVisibility(View.GONE);
+ }
+ if (!TextUtils.isEmpty(message)) {
+ messageTv.setText(message);
+ }
+ //如果设置按钮的文字
+ if (!TextUtils.isEmpty(positive)) {
+ positiveBn.setText(positive);
+ } else {
+ positiveBn.setText("确定");
+ }
+ if (!TextUtils.isEmpty(negtive)) {
+ negtiveBn.setText(negtive);
+ } else {
+ negtiveBn.setText("取消");
+ }
+
+ if (imageResId != -1) {
+ imageIv.setImageResource(imageResId);
+ imageIv.setVisibility(View.VISIBLE);
+ } else {
+ imageIv.setVisibility(View.GONE);
+ }
+ /**
+ * 只显示一个按钮的时候隐藏取消按钮,回掉只执行确定的事件
+ */
+// if (isSingle) {
+//// columnLineView.setVisibility(View.GONE);
+// negtiveBn.setVisibility(View.GONE);
+// } else {
+// negtiveBn.setVisibility(View.VISIBLE);
+//// columnLineView.setVisibility(View.VISIBLE);
+// }
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ refreshView();
+ }
+
+ /**
+ * 初始化界面控件
+ */
+ private void initView() {
+ negtiveBn = findViewById(R.id.negtive);
+ positiveBn = findViewById(R.id.positive);
+ titleTv = findViewById(R.id.title);
+ messageTv = findViewById(R.id.message);
+ imageIv = findViewById(R.id.image);
+// columnLineView = findViewById(R.id.column_line);
+ }
+
+ /**
+ * 设置确定取消按钮的回调
+ */
+ private OnClickBottomListener onClickBottomListener;
+
+ public void setOnClickBottomListener(OnClickBottomListener onClickBottomListener) {
+ this.onClickBottomListener = onClickBottomListener;
+ }
+
+ public interface OnClickBottomListener {
+ /**
+ * 点击确定按钮事件
+ */
+ void onPositiveClick();
+
+ /**
+ * 点击取消按钮事件
+ */
+ void onNegtiveClick();
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public DeleteContactDialog setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public DeleteContactDialog setTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ public String getPositive() {
+ return positive;
+ }
+
+ public DeleteContactDialog setPositive(String positive) {
+ this.positive = positive;
+ return this;
+ }
+
+ public String getNegtive() {
+ return negtive;
+ }
+
+ public DeleteContactDialog setNegtive(String negtive) {
+ this.negtive = negtive;
+ return this;
+ }
+
+ public DeleteContactDialog setNegtiveText(String negtive) {
+ negtiveBn.setText(negtive);
+ return this;
+ }
+
+ public int getImageResId() {
+ return imageResId;
+ }
+
+ public boolean isSingle() {
+ return isSingle;
+ }
+
+ public DeleteContactDialog setSingle(boolean single) {
+ isSingle = single;
+ return this;
+ }
+
+ public DeleteContactDialog setImageResId(int imageResId) {
+ this.imageResId = imageResId;
+ return this;
+ }
+
+ @Override
+ public void dismiss() {
+ super.dismiss();
+ }
+}
diff --git a/app/src/main/java/com/uiuios/aios/fragment/contact/ContactFragment.java b/app/src/main/java/com/uiuios/aios/fragment/contact/ContactFragment.java
index 6a28ca0..e1ad5cb 100644
--- a/app/src/main/java/com/uiuios/aios/fragment/contact/ContactFragment.java
+++ b/app/src/main/java/com/uiuios/aios/fragment/contact/ContactFragment.java
@@ -9,12 +9,15 @@ import android.view.View;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.GridLayoutManager;
+import com.hjq.toast.Toaster;
import com.uiuios.aios.R;
-import com.uiuios.aios.activity.contact.AddContactActivity;
-import com.uiuios.aios.adapter.HomeContactAdapter;
+import com.uiuios.aios.activity.wecontact.AddWechatContactActivity;
+import com.uiuios.aios.adapter.WechatContactAdapter;
import com.uiuios.aios.base.mvvm.fragment.BaseMvvmFragment;
-import com.uiuios.aios.bean.Contact;
+import com.uiuios.aios.bean.WechatInfo;
import com.uiuios.aios.databinding.FragmentContactHomeBinding;
+import com.uiuios.aios.db.ContactManager;
+import com.uiuios.aios.dialog.DeleteContactDialog;
import java.util.List;
@@ -22,7 +25,7 @@ public class ContactFragment extends BaseMvvmFragment>() {
+ mViewModel.getContactList().observe(this, new Observer>() {
@Override
- public void onChanged(List contacts) {
+ public void onChanged(List contacts) {
if (contacts == null || contacts.size() == 0) {
-// mViewDataBinding.tvPeople.setText("暂无数据");
+ mViewDataBinding.tvNoContact.setVisibility(View.VISIBLE);
+ mViewDataBinding.rvContact.setVisibility(View.GONE);
} else {
-// mViewDataBinding.tvPeople.setText(contacts.size() + "人");
+ mViewDataBinding.tvNoContact.setVisibility(View.GONE);
+ mViewDataBinding.rvContact.setVisibility(View.VISIBLE);
}
-// Contact contact = new Contact();
-// contact.setName("拨号");
-// contact.setMobile(OldContactAdapter.DIALER_PACKAGE);
-// contacts.add(0, contact);
mContactAdapter.setContactList(contacts);
}
});
@@ -73,10 +80,42 @@ public class ContactFragment extends BaseMvvmFragment {
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
@@ -39,54 +46,87 @@ public class ContactViewModel extends BaseViewModel> mContactList = new MutableLiveData<>();
+ private MutableLiveData> mContactList = new MutableLiveData<>();
- public MutableLiveData> getContactList() {
+ public MutableLiveData> getContactList() {
return mContactList;
}
public void getContact() {
- NetInterfaceManager.getInstance()
- .getContactListObservable()
+ Observable.create(new ObservableOnSubscribe>() {
+ @Override
+ public void subscribe(@NonNull ObservableEmitter> emitter) throws Throwable {
+ emitter.onNext(ContactManager.getInstance().getWechatInfoList());
+ }
+ })
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))
- .subscribe(new Observer>>() {
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Observer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
- Log.e("getContactList", "onSubscribe: ");
+ Log.e("getWechatInfoList", "onSubscribe: ");
}
@Override
- public void onNext(@NonNull BaseResponse> listBaseResponse) {
- Log.e("getContactList", "onNext: " + listBaseResponse);
- if (listBaseResponse.code == 200) {
- mMMKV.putString(UrlAddress.GET_MAIL_LIST, GsonUtils.toJSONString(listBaseResponse.data));
- mContactList.setValue(listBaseResponse.data);
- } else {
- mMMKV.putString(UrlAddress.GET_MAIL_LIST, "");
- mContactList.setValue(new ArrayList<>());
- }
+ public void onNext(@NonNull List wechatInfos) {
+ Log.e("getWechatInfoList", "onNext: " + wechatInfos);
+ mContactList.setValue(wechatInfos);
}
@Override
public void onError(@NonNull Throwable e) {
- Log.e("getContactList", "onError: " + e.getMessage());
- String jsonString = mMMKV.getString(UrlAddress.GET_MAIL_LIST, null);
- Gson gson = new Gson();
- Type type = new TypeToken>() {
- }.getType();
- List contacts = gson.fromJson(jsonString, type);
- if (contacts == null) {
- mContactList.setValue(new ArrayList<>());
- } else {
- mContactList.setValue(contacts);
- }
- onComplete();
+ Log.e("getWechatInfoList", "onError: " + e.getMessage());
+ mContactList.setValue(null);
}
@Override
public void onComplete() {
- Log.e("getContactList", "onComplete: ");
+ Log.e("getWechatInfoList", "onComplete: ");
}
});
+
+// NetInterfaceManager.getInstance()
+// .getContactListObservable()
+// .compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))
+// .subscribe(new Observer>>() {
+// @Override
+// public void onSubscribe(@NonNull Disposable d) {
+// Log.e("getContactList", "onSubscribe: ");
+// }
+//
+// @Override
+// public void onNext(@NonNull BaseResponse> listBaseResponse) {
+// Log.e("getContactList", "onNext: " + listBaseResponse);
+// if (listBaseResponse.code == 200) {
+// mMMKV.putString(UrlAddress.GET_MAIL_LIST, GsonUtils.toJSONString(listBaseResponse.data));
+// mContactList.setValue(listBaseResponse.data);
+// } else {
+// mMMKV.putString(UrlAddress.GET_MAIL_LIST, "");
+// mContactList.setValue(new ArrayList<>());
+// }
+// }
+//
+// @Override
+// public void onError(@NonNull Throwable e) {
+// Log.e("getContactList", "onError: " + e.getMessage());
+// String jsonString = mMMKV.getString(UrlAddress.GET_MAIL_LIST, null);
+// Gson gson = new Gson();
+// Type type = new TypeToken>() {
+// }.getType();
+// List contacts = gson.fromJson(jsonString, type);
+// if (contacts == null) {
+// mContactList.setValue(new ArrayList<>());
+// } else {
+// mContactList.setValue(contacts);
+// }
+// onComplete();
+// }
+//
+// @Override
+// public void onComplete() {
+// Log.e("getContactList", "onComplete: ");
+// }
+// });
}
}
diff --git a/app/src/main/java/com/uiuios/aios/network/NetInterfaceManager.java b/app/src/main/java/com/uiuios/aios/network/NetInterfaceManager.java
index 896e5b5..ea6d534 100644
--- a/app/src/main/java/com/uiuios/aios/network/NetInterfaceManager.java
+++ b/app/src/main/java/com/uiuios/aios/network/NetInterfaceManager.java
@@ -2,6 +2,7 @@ package com.uiuios.aios.network;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.os.Environment;
import android.util.Log;
import com.google.gson.Gson;
@@ -211,6 +212,23 @@ public class NetInterfaceManager {
Log.e("OKhttp ", " 打印HTTP请求完成 Headers \n");
}
+ private String getCacheDir() {
+ String cachePath;
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
+ || !Environment.isExternalStorageRemovable()) {
+ if (mContext.getExternalCacheDir() != null) {
+ cachePath = mContext.getExternalCacheDir().getPath();
+ } else if (mContext.getExternalFilesDir("cache") != null) {
+ cachePath = mContext.getExternalFilesDir("cache").getPath();
+ } else {
+ cachePath = mContext.getCacheDir().getPath();
+ }
+ } else {
+ cachePath = mContext.getCacheDir().getPath();
+ }
+ return cachePath;
+ }
+
public static void init(Context context) {
if (INSTANCE == null) {
INSTANCE = new NetInterfaceManager(context);
diff --git a/app/src/main/java/com/uiuios/aios/service/WeAccessibilityService.java b/app/src/main/java/com/uiuios/aios/service/WeAccessibilityService.java
new file mode 100644
index 0000000..47a9852
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/service/WeAccessibilityService.java
@@ -0,0 +1,469 @@
+package com.uiuios.aios.service;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.GestureDescription;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Toast;
+
+import com.tencent.mmkv.MMKV;
+import com.uiuios.aios.bean.WechatInfo;
+import com.uiuios.aios.config.CommonConfig;
+
+public class WeAccessibilityService extends AccessibilityService {
+ private static final String TAG = WeAccessibilityService.class.getSimpleName();
+
+ private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
+
+
+ private static final String DIALER_TEXT = "音视频通话";
+ private static final String CONTACT_TEXT = "通讯录";
+ private static final String TAG_TEXT = "标签";
+ private static final String TAG_NAME = "爱来伴";
+ private static final String MORE_NAME = "更多功能按钮,已折叠";
+ private static final String PARENT_VIDEO_TEXT = "视频通话";
+
+ private static final String VIDEO_TEXT = "视频通话";
+ private static final String CALL_TEXT = "语音通话";
+
+ private static final String RECEIVE_DESCRIPTION = "接听";
+
+ public static final int TYPE_VOICE = 0;
+ public static final int TYPE_VIDEO = 1;
+
+ private static final int WAIT_TIME = 500;
+
+ private int mCallType = TYPE_VOICE;
+
+ private WechatInfo mWechatInfo;
+ private Step mCurrentStep = Step.WAITING;
+ private String mName = "";//微信昵称
+ private String mTagName = "";//微信联系人标签名
+ private boolean mAutoAccept = false;
+ private boolean finished = true;
+
+ private Handler handler = null;
+ private AccessibilityEvent input = null;
+ private Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ _onAccessibilityEvent(input);
+ finished = true;
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.e(TAG, "onCreate: ");
+ registerSettingReceiver();
+ handler = new Handler();
+
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.e(TAG, "onStartCommand: ");
+ if (intent != null) {
+ mWechatInfo = (WechatInfo) intent.getSerializableExtra("WechatInfo");
+ Log.e(TAG, "onStartCommand: wechatInfo = " + mWechatInfo);
+ mCallType = intent.getIntExtra("call_type", TYPE_VOICE);
+ mName = mWechatInfo.getNickName();
+ String groupTag = mWechatInfo.getGroupTag();
+ if (TextUtils.isEmpty(groupTag)) {
+ mTagName = TAG_NAME;
+ } else {
+ mTagName = groupTag;
+ }
+ mCurrentStep = Step.CLICK_CONTACT;
+ launchWeChat();
+ }
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.e(TAG, "onDestroy: ");
+ if (mSettingBroadcastReceiver != null) {
+ unregisterReceiver(mSettingBroadcastReceiver);
+ }
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ Log.e(TAG, "onAccessibilityEvent: ");
+ Log.v(TAG, "onAccessibilityEvent: event = " + event.toString());
+ if (finished) {
+ finished = false;
+ } else {
+ Log.v(TAG, "bounce");
+ handler.removeCallbacks(runnable);
+ }
+ input = event;
+ handler.postDelayed(runnable, WAIT_TIME);
+ }
+
+
+ /**
+ * 1.在微信页面直接找到联系人拨打电话
+ * 2.在联系人页面找到并拨打
+ * 3.通过进入联系人-标签找到并拨打
+ *
+ * @param event
+ */
+ private void _onAccessibilityEvent(AccessibilityEvent event) {
+ Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
+ switch (mCurrentStep) {
+ case WAITING:
+ if (!mAutoAccept) return;
+ if (step(Property.DESCRIPTION, RECEIVE_DESCRIPTION, Step.WAITING)) {
+ mCurrentStep = Step.WAITING;
+ Toast.makeText(this, "自动接听视频/语音聊天", Toast.LENGTH_LONG).show();
+ }
+ break;
+ case CLICK_HOME://主页能找到直接点击进去更多
+ stepHome(Property.TEXT, mName);
+ break;
+ case CLICK_QUICK_WECHAT_CALL://点击更多页面
+ step(Property.DESCRIPTION, MORE_NAME, Step.CLICK_TARGET);
+ break;
+ case CLICK_TARGET://点击视频通话
+ stepCall(Property.TEXT, PARENT_VIDEO_TEXT);
+ break;
+
+ case CLICK_CONTACT://进入通讯录界面
+ step(Property.TEXT, CONTACT_TEXT, Step.FIND_TAG);
+ break;
+ case FIND_CONTACT://模拟滑动找到联系人
+ findContact(Property.TEXT, mName, Step.CLICK_NAME);
+ case FIND_TAG:
+ step(Property.TEXT, TAG_TEXT, Step.CLICK_TAG);
+ break;
+ case CLICK_TAG:
+ step(Property.TEXT, mTagName, Step.CLICK_NAME);
+
+ break;
+ case CLICK_NAME://点击item
+ step(Property.TEXT, mName, Step.CLICK_INFO);
+ break;
+ case CLICK_INFO://进入个人信息页面
+ step(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL);
+ break;
+
+ case CLICK_CALL://打视频或者电话
+ if (mCallType == TYPE_VIDEO) {
+ step(Property.TEXT, VIDEO_TEXT, Step.WAITING);
+ } else if (mCallType == TYPE_VOICE) {
+ step(Property.TEXT, CALL_TEXT, Step.WAITING);
+ }
+ break;
+// case CLICK_VIDEO_CALL:
+// if (step(Property.TEXT, VIDEO_TEXT)) {
+// Log.d(TAG, "finish, now: " + mCurrentStep);
+// Toast.makeText(this, "成功发起视频聊天", Toast.LENGTH_LONG).show();
+// }
+// break;
+ default:
+ }
+ }
+
+ @Override
+ public void onInterrupt() {
+ Log.e(TAG, "onInterrupt: ");
+
+ }
+
+ @Override
+ protected void onServiceConnected() {
+ super.onServiceConnected();
+ Log.e(TAG, "onServiceConnected: ");
+ }
+
+ private void launchWeChat() {
+ Intent intent = new Intent();
+ ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.setComponent(cmp);
+ startActivity(intent);
+ }
+
+ private boolean step(Property type, String text, Step nextStep) {
+ AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
+ if (node != null) {
+ clickNode(node);
+ Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
+ mCurrentStep = nextStep;
+ Log.e(TAG, "step: next: " + mCurrentStep);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean stepHome(Property type, String text) {
+ AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
+ if (node != null) {
+ clickNode(node);
+ Log.e(TAG, "stepHome: mCurrentStep: " + mCurrentStep + " done");
+ mCurrentStep = mCurrentStep.next();
+ Log.e(TAG, "stepHome: next: " + mCurrentStep);
+ return true;
+ } else {
+ mCurrentStep = Step.CLICK_CONTACT;
+ return false;
+ }
+ }
+
+ private int mFindCount = 0;
+ private int mMaxCount = 5;
+
+
+ private boolean findContact(Property type, String text, Step nextStep) {
+ AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
+ if (node != null) {
+ clickNode(node);
+ Log.e("findContact", "mCurrentStep: " + mCurrentStep + " done");
+ mCurrentStep = nextStep;
+ Log.e("findContact", "next: " + mCurrentStep);
+ mFindCount = 0;
+ return true;
+ } else {
+ if (mFindCount == mMaxCount) {
+ Log.e("findContact", "mCurrentStep: max");
+ Toast.makeText(this, "没有找到联系人", Toast.LENGTH_LONG).show();
+ mCurrentStep = Step.WAITING;
+ mFindCount = 0;
+ return false;
+ } else {
+ Log.e("findContact", "mCurrentStep: not found");
+ mFindCount++;
+ Log.e("findContact", "mCurrentStep: mFindCount = " + mFindCount);
+ scrolDown();
+ return false;
+ }
+ }
+ }
+
+
+ private AccessibilityNodeInfo findNode(AccessibilityNodeInfo root, Property type, String text) {
+ if (root == null) return null;
+// Log.v(TAG, "findNode: getPackageName = " + root.getPackageName());
+ Log.v(TAG, "findNode: getText = " + root.getText());
+ Log.v(TAG, "findNode: getClassName = " + root.getClassName());
+ Log.v(TAG, "findNode: getContentDescription = " + root.getContentDescription());
+ boolean satisfied = false;
+ switch (type) {
+ case TEXT:
+ satisfied = root.getText() != null && text.contentEquals(root.getText());
+ break;
+ case CLASS_NAME:
+ satisfied = root.getClassName() != null && text.contentEquals(root.getClassName());
+ break;
+ case DESCRIPTION:
+ satisfied = root.getContentDescription() != null && text.contentEquals(root.getContentDescription());
+ break;
+ default:
+ }
+ if (satisfied) {
+ return root;
+ } else {
+ for (int i = 0; i < root.getChildCount(); i++) {
+ AccessibilityNodeInfo result = findNode(root.getChild(i), type, text);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ root.recycle();
+ return null;
+ }
+
+ private void clickNode(AccessibilityNodeInfo node) {
+ if (node.isClickable()) {
+ node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+ node.recycle();
+ } else {
+ AccessibilityNodeInfo parent = node.getParent();
+ node.recycle();
+ clickNode(parent);
+ }
+ }
+
+ private boolean stepCall(Property type, String text) {
+ AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
+ if (node != null) {
+ Point point = getPointtByNode(node);
+ Log.e(TAG, "stepCall: " + point);
+ clickByNode(point.x, point.y + 30);
+// clickNode(node);
+ Log.e(TAG, "stepCall: mCurrentStep " + mCurrentStep + " done");
+ mCurrentStep = Step.CLICK_CALL;
+ Log.e(TAG, "stepCall: next " + mCurrentStep);
+ return true;
+ } else {
+ Log.e(TAG, "stepCall: not found");
+ return false;
+ }
+ }
+
+ //根据节点信息可获得对应的x,y坐标
+ static Point getPointtByNode(AccessibilityNodeInfo node) {
+ if (node == null) {
+ return new Point(0, 0);
+ }
+ Rect rect = new Rect();
+ node.getBoundsInScreen(rect);
+ Point point = new Point(rect.centerX(), rect.centerY());
+ return point;
+ }
+
+
+ //实现对(x,y)坐标进行点击操作。
+ private boolean clickByNode(int x, int y) {
+ Point point = new Point(x, y);
+ Path path = new Path();
+ path.moveTo(point.x, point.y);
+ GestureDescription.Builder builder = new GestureDescription.Builder();
+ builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200));
+ GestureDescription gesture = builder.build();
+ boolean isDispatched = dispatchGesture(gesture, new GestureResultCallback() {
+ @Override
+ public void onCompleted(GestureDescription gestureDescription) {
+ super.onCompleted(gestureDescription);
+ Log.e("clickByNode", "onCompleted: ");
+ }
+
+ @Override
+ public void onCancelled(GestureDescription gestureDescription) {
+ super.onCancelled(gestureDescription);
+ Log.e("clickByNode", "onCompleted: ");
+ }
+ }, null);
+ return isDispatched;
+ }
+
+ private boolean scrolDown() {
+ WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ DisplayMetrics dm = new DisplayMetrics();
+ wm.getDefaultDisplay().getRealMetrics(dm);
+ int width = dm.widthPixels; // 屏幕宽度(像素)
+ int height = dm.heightPixels; // 屏幕高度(像素)
+ float density = dm.density; // 屏幕密度(0.75 / 1.0 / 1.5)
+ int densityDpi = dm.densityDpi; // 屏幕密度dpi(120 / 160 / 240)
+ // 屏幕宽度算法:屏幕宽度(像素)/屏幕密度
+// int screenWidth = (int) (width / density); // 屏幕宽度(dp)
+// int screenHeight = (int) (height / density);// 屏幕高度(dp)
+ Log.e(TAG, "scrolDown: screenWidth = " + width);
+ Log.e(TAG, "scrolDown: screenHeight = " + height);
+ int center_X = width / 2;
+ int center_Y = height / 2;
+ Log.e("scrolDown", "center position:" + "(" + center_X + "," + center_Y + ")");
+ Path path = new Path();
+ path.moveTo(center_X, (int) (center_Y * 1.5)); //起点坐标。
+ path.lineTo(center_X, (int) (center_Y * 0.5)); //终点坐标。
+ GestureDescription.Builder builder = new GestureDescription.Builder();
+ GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200)).build();
+ boolean isDispatched = dispatchGesture(gestureDescription, new GestureResultCallback() {
+ @Override
+ public void onCompleted(GestureDescription gestureDescription) {
+ super.onCompleted(gestureDescription);
+ Log.d("scrolDown", "dispatchGesture ScrollUp onCompleted.");
+ path.close();
+ }
+
+ @Override
+ public void onCancelled(GestureDescription gestureDescription) {
+ super.onCancelled(gestureDescription);
+ Log.d("scrolDown", "dispatchGesture ScrollUp cancel.");
+ }
+ }, null);
+
+ return isDispatched;
+ }
+
+
+ private enum Step {
+ WAITING,
+ CLICK_HOME,
+ CLICK_QUICK_WECHAT_CALL,
+ CLICK_TARGET,
+
+ CLICK_CONTACT,
+ FIND_CONTACT,
+ CLICK_TAG,
+ FIND_TAG,
+
+ CLICK_NAME,
+ CLICK_INFO,
+
+ CLICK_CALL,
+ CLICK_VIDEO_CALL;
+
+ private Step next() {
+ return values()[(this.ordinal() + 1) % values().length];
+ }
+ }
+
+ private enum Property {
+ TEXT,
+ CLASS_NAME,
+ DESCRIPTION
+ }
+
+ public static final String SETTING_CALL_TYPE_ACTION = "setting_call_type_action";
+ public static final String SETTING_AUTOMATIC_ANSWER_ACTION = "setting_automatic_answer_action";
+
+ private SettingBroadcastReceiver mSettingBroadcastReceiver;
+
+ private void registerSettingReceiver() {
+ if (mSettingBroadcastReceiver == null) {
+ mSettingBroadcastReceiver = new SettingBroadcastReceiver();
+ }
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(SETTING_CALL_TYPE_ACTION);
+ filter.addAction(SETTING_AUTOMATIC_ANSWER_ACTION);
+ registerReceiver(mSettingBroadcastReceiver, filter);
+ }
+
+ private class SettingBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.e("SettingBroadcastReceiver", "onReceive: " + action);
+ if (TextUtils.isEmpty(action)) return;
+ switch (action) {
+ case SETTING_CALL_TYPE_ACTION:
+ int callType = intent.getIntExtra("call_type", TYPE_VOICE);
+ mCallType = callType;
+ Log.e("SettingBroadcastReceiver", "onReceive: callType = " + callType);
+ break;
+ case SETTING_AUTOMATIC_ANSWER_ACTION:
+ boolean autoAnswer = intent.getBooleanExtra("auto_answer", false);
+ mAutoAccept = autoAnswer;
+ Log.e("SettingBroadcastReceiver", "onReceive: autoAnswer = " + autoAnswer);
+ break;
+ default:
+ }
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/com/uiuios/aios/utils/AccessibilityUtils.java b/app/src/main/java/com/uiuios/aios/utils/AccessibilityUtils.java
new file mode 100644
index 0000000..18e3367
--- /dev/null
+++ b/app/src/main/java/com/uiuios/aios/utils/AccessibilityUtils.java
@@ -0,0 +1,43 @@
+package com.uiuios.aios.utils;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.uiuios.aios.service.WeAccessibilityService;
+
+public class AccessibilityUtils {
+ private static final String TAG = AccessibilityUtils.class.getSimpleName();
+
+ public static boolean isAccessibilitySettingsOn(Context context) {
+ int accessibilityEnabled = 0;
+ final String service = context.getPackageName() + "/" + WeAccessibilityService.class.getCanonicalName();
+ try {
+ accessibilityEnabled = Settings.Secure.getInt(
+ context.getApplicationContext().getContentResolver(),
+ android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
+ } catch (Settings.SettingNotFoundException e) {
+ Log.e(TAG, "Error finding setting, default accessibility to not found: " + e.getMessage());
+ }
+ TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
+
+ if (accessibilityEnabled == 1) {
+ String settingValue = Settings.Secure.getString(
+ context.getApplicationContext().getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ if (settingValue != null) {
+ mStringColonSplitter.setString(settingValue);
+ while (mStringColonSplitter.hasNext()) {
+ String accessibilityService = mStringColonSplitter.next();
+ if (accessibilityService.equalsIgnoreCase(service)) {
+ Log.v(TAG, "***ACCESSIBILITY IS ENABLED*** -----------------");
+ return true;
+ }
+ }
+ }
+ }
+ Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
+ return false;
+ }
+}
diff --git a/app/src/main/res/drawable-hdpi/wechat_call_phone.png b/app/src/main/res/drawable-hdpi/wechat_call_phone.png
new file mode 100644
index 0000000..fb4f9d0
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/wechat_call_phone.png differ
diff --git a/app/src/main/res/drawable-hdpi/wechat_call_video.png b/app/src/main/res/drawable-hdpi/wechat_call_video.png
new file mode 100644
index 0000000..999dc45
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/wechat_call_video.png differ
diff --git a/app/src/main/res/drawable-hdpi/wechat_call_voice.png b/app/src/main/res/drawable-hdpi/wechat_call_voice.png
new file mode 100644
index 0000000..9cdf44d
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/wechat_call_voice.png differ
diff --git a/app/src/main/res/drawable/wechat_call_background.xml b/app/src/main/res/drawable/wechat_call_background.xml
new file mode 100644
index 0000000..4c41516
--- /dev/null
+++ b/app/src/main/res/drawable/wechat_call_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_wechat_contact.xml b/app/src/main/res/layout/activity_add_wechat_contact.xml
new file mode 100644
index 0000000..d7ae28e
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_wechat_contact.xml
@@ -0,0 +1,273 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_wechat_call.xml b/app/src/main/res/layout/activity_wechat_call.xml
new file mode 100644
index 0000000..b6fe843
--- /dev/null
+++ b/app/src/main/res/layout/activity_wechat_call.xml
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_delete_contact.xml b/app/src/main/res/layout/dialog_delete_contact.xml
new file mode 100644
index 0000000..b09c50e
--- /dev/null
+++ b/app/src/main/res/layout/dialog_delete_contact.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_contact_home.xml b/app/src/main/res/layout/fragment_contact_home.xml
index abe0feb..7274186 100644
--- a/app/src/main/res/layout/fragment_contact_home.xml
+++ b/app/src/main/res/layout/fragment_contact_home.xml
@@ -28,6 +28,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4deee1a..f14b297 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -11,4 +11,6 @@
"安装快捷方式"
"添加到主屏幕"
+ 关爱守护快捷服务
+ 关爱守护一键拨打视频
diff --git a/app/src/main/res/xml/accessibility_service_config.xml b/app/src/main/res/xml/accessibility_service_config.xml
new file mode 100644
index 0000000..b45611f
--- /dev/null
+++ b/app/src/main/res/xml/accessibility_service_config.xml
@@ -0,0 +1,9 @@
+