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 @@ +