diff --git a/app/build.gradle b/app/build.gradle
index d76c0e5..98d686f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,9 @@ android {
defaultConfig {
applicationId "com.ttstd.dialer"
- minSdkVersion 24
+
+ //There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
+ minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
@@ -20,7 +22,7 @@ android {
ndk {
//根据需要 自行选择添加的对应cpu类型的.so库。
- abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
+ abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
// 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
}
@@ -117,7 +119,7 @@ dependencies {
//MMKV
implementation 'com.tencent:mmkv-static:1.2.14'
//bugly
- implementation 'com.tencent.bugly:crashreport:4.1.9.2'
+ implementation 'com.tencent.bugly:crashreport:4.1.9.3'
implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
// 替换成最新版本, 需要注意的是api
// 要与compiler匹配使用,均使用最新版可以保证兼容
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8af9375..2f71479 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,8 @@
package="com.ttstd.dialer">
+
+
{
private static final String TAG = "ContactListActivity";
+ private static final int REQUEST_CODE_CALL = 7897;
private ContactInfoAdapter mContactInfoAdapter;
@@ -72,6 +79,12 @@ public class ContactListActivity extends BaseMvvmActivity {
+ private static final String TAG = "ContactInfoAdapter";
+
+ private FragmentActivity mContext;
+
+ private List mContacts;
+
+ public void setContacts(List contacts) {
+ mContacts = contacts;
+ notifyDataSetChanged();
+ }
+
+ public void setItemMoveCallback(ItemMoveCallback itemMoveCallback) {
+ mItemMoveCallback = itemMoveCallback;
+ }
+
+ private ItemMoveCallback mItemMoveCallback;
+
+ public interface ItemMoveCallback {
+ void onItemMove(int fromPosition, int toPosition);
+
+ void onItemRemoved(int position);
+ }
+
+ public interface OnClickListener {
+ void onClick(Contact contact);
+ }
+
+ private OnClickListener mOnClickListener;
+
+ public void setOnClickListener(OnClickListener onClickListener) {
+ mOnClickListener = onClickListener;
+ }
+
+ @NonNull
+ @Override
+ public ContactInfoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ mContext = (FragmentActivity) parent.getContext();
+ return new ContactInfoHolder(LayoutInflater.from(mContext).inflate(R.layout.item_contact_home, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ContactInfoHolder holder, int position) {
+ Contact contact = mContacts.get(position);
+ String name = contact.getName();
+ holder.tv_name.setText(name);
+ String phone = contact.getPhoneNumber();
+ holder.tv_phone.setText(phone);
+ holder.root.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.e(TAG, "onClick: " + contact);
+ if (mOnClickListener != null) {
+ mOnClickListener.onClick(contact);
+ }
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mContacts == null ? 0 : mContacts.size();
+ }
+
+
+ // 处理拖动交换位置
+ public void onItemMove(int fromPosition, int toPosition) {
+ Log.e(TAG, "onItemMove: fromPosition = " + fromPosition);
+ Log.e(TAG, "onItemMove: toPosition = " + toPosition);
+ if (fromPosition == toPosition) {
+ return; // 直接返回,不执行后续操作
+ }
+
+ if (fromPosition < toPosition) {
+ for (int i = fromPosition; i < toPosition; i++) {
+ Collections.swap(mContacts, i, i + 1);
+ }
+ } else {
+ for (int i = fromPosition; i > toPosition; i--) {
+ Collections.swap(mContacts, i, i - 1);
+ }
+ }
+ notifyItemMoved(fromPosition, toPosition);
+ if (mItemMoveCallback != null) {
+ mItemMoveCallback.onItemMove(fromPosition, toPosition);
+ }
+ }
+
+ // 处理滑动删除
+ public void onItemDismiss(int position) {
+// mContacts.remove(position);
+ if (mItemMoveCallback != null) {
+ mItemMoveCallback.onItemRemoved(position);
+ }
+ notifyItemRemoved(position);
+ }
+
+ public class ContactInfoHolder extends RecyclerView.ViewHolder {
+ ConstraintLayout root;
+ NiceImageView nv_avatar;
+ TextView tv_name, tv_phone;
+
+ public ContactInfoHolder(@NonNull View itemView) {
+ super(itemView);
+ root = itemView.findViewById(R.id.root);
+ nv_avatar = itemView.findViewById(R.id.nv_avatar);
+ tv_name = itemView.findViewById(R.id.tv_name);
+ tv_phone = itemView.findViewById(R.id.tv_phone);
+ }
+ }
+}
diff --git a/app/src/main/java/com/ttstd/dialer/base/mvvm/fragment/BaseMvvmDialogFragment.java b/app/src/main/java/com/ttstd/dialer/base/mvvm/fragment/BaseMvvmDialogFragment.java
new file mode 100644
index 0000000..7c78404
--- /dev/null
+++ b/app/src/main/java/com/ttstd/dialer/base/mvvm/fragment/BaseMvvmDialogFragment.java
@@ -0,0 +1,276 @@
+package com.ttstd.dialer.base.mvvm.fragment;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.databinding.DataBindingUtil;
+import androidx.databinding.ViewDataBinding;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.ttstd.dialer.base.BaseDialogFragment;
+import com.ttstd.dialer.base.BaseFragment;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.ParameterizedType;
+
+/**
+ * @author: lml
+ * @date: 2021/12/15
+ */
+public abstract class BaseMvvmDialogFragment extends BaseDialogFragment {
+ protected String mTag = this.getClass().getSimpleName();
+ /**
+ * 是否顯示了
+ */
+ protected boolean mIsVisible;
+ /**
+ * 是否準備好了-Created
+ */
+ protected boolean mHasPrepare;
+
+
+ protected VM mViewModel;
+ protected VDB mViewDataBinding;
+ protected Class vmClass;
+ //
+// protected Toolbar toolbar;
+// protected View statusBarView;
+ //
+ protected Bundle bundle;//来自getArguments()
+ protected Bundle savedInstanceState;
+
+// protected Context context;
+
+ /**
+ * 上下文
+ */
+ private WeakReference ctx;
+
+ public Context getCtx() {
+ return ctx == null ? null : ctx.get();
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+// this.context = context;
+ ctx = new WeakReference<>(context);
+ }
+
+ /**
+ * onCreate、onResume里不能调用
+ *
+ * @return
+ */
+ public boolean isAttached() {
+ boolean flag = getCtx() != null && isAdded();
+ Log.e(" >> isAttached >>", "flag = " + flag);
+ return flag;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ //ViewDataBinding
+ mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
+ mViewDataBinding.setLifecycleOwner(this);
+
+ //ViewModel
+ vmClass = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ mViewModel = new ViewModelProvider(this).get(vmClass);
+ //
+ return mViewDataBinding.getRoot();
+ }
+
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+// if (initStatusBarToolBar()) {
+// toolbar = getToolbar();
+// }
+ //注册eventbus
+// if (getClass().isAnnotationPresent(BindEventBus.class))
+// EventBusManager.register(this);
+ //
+
+// fitsLayoutOverlap();
+ initDataBinding();
+ initView(bundle = getArguments());
+ //
+ initData(this.savedInstanceState = savedInstanceState);
+ //
+ if (mIsVisible) {
+ onEnter();
+ }
+ mHasPrepare = true;
+ //
+// LiveDataBus.get().with(ConstantUtils.DATA_BUS_LOADING_FRAGMENT, Boolean.class).observe(getActivity(), bool -> {
+// L.e(" >> LiveDataBus >> DATA_BUS_LOADING_FRAGMENT: %s", bool);
+// if(bool) {
+// showLoading(R.string.str_please_wait);
+// } else {
+// hideLoading();
+// }
+// });
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mHasPrepare = false;
+ mViewDataBinding = null;
+ //移除eventbus
+// if (getClass().isAnnotationPresent(BindEventBus.class))
+// EventBusManager.unregister(this);
+ //
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ if (mIsVisible == getUserVisibleHint())
+ return;
+ mIsVisible = getUserVisibleHint();
+ if (mIsVisible) {
+ if (!mHasPrepare)
+ return;
+ onEnter();
+ } else {
+ onExit();
+ }
+ }
+
+ @LayoutRes
+ protected abstract int getLayoutId();
+
+// protected abstract Toolbar getToolbar();
+
+// protected View getStatusView() {
+// return null;
+// }
+
+ protected abstract void initDataBinding();
+
+ protected abstract void initView(Bundle bundle);
+
+ protected abstract void initData(Bundle savedInstanceState);
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+// fitsLayoutOverlap();
+ }
+
+// protected boolean isImmersionBarEnabled() {
+// return true;
+// }
+
+// protected boolean initStatusBarToolBar() {
+// return true;
+// }
+
+
+// private void fitsLayoutOverlap() {
+// if (!isImmersionBarEnabled()) return;
+// if (statusBarView != null) {
+// ImmersionBar.setStatusBarView(getActivity(), statusBarView);
+// }
+// if (toolbar != null) {
+// ImmersionBar.setTitleBar(getActivity(), toolbar);
+// }
+// }
+
+ protected void hideInputMethod(Activity activity) {
+ InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ View view = activity.getCurrentFocus();
+ if (view != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+ }
+
+ protected void hideInputMethod(Activity activity, EditText editText) {
+ InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
+ View view = activity.getCurrentFocus();
+ if (view != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ protected void showInputMethod(EditText editText) {
+ InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
+ }
+
+
+// private CustomDialog mWaitDialog;
+//
+// public void showLoading(@StringRes int contentID) {
+// showLoading(contentID, R.color.white);
+// }
+//
+// public void showLoading(@StringRes int contentID, @ColorRes int color) {
+// hideLoading();
+// DialogX.init(getActivity());
+// if (color == R.color.white) {
+// mWaitDialog = DialogXUtil.getInstance().showLoading(getActivity(), getString(contentID), getResources().getColor(color));
+// } else {
+// mWaitDialog = DialogXUtil.getInstance().showLoading_black(getActivity(), getString(contentID), getResources().getColor(color));
+// }
+// }
+//
+// public void updateLoadingTip(@StringRes int messageID, int percent) {
+// try {
+// if (mWaitDialog != null && mWaitDialog.isShow()) {
+// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip);
+// if (tvTip != null)
+// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%"));
+// }
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+//
+// public boolean isShowLoading() {
+// return mWaitDialog != null && mWaitDialog.isShow();
+// }
+//
+// public void hideLoading() {
+// try {
+// boolean isShow = isShowLoading();
+// L.d(" >> hideLoading :: isShow: %s", isShow);
+// if (isShow)
+// mWaitDialog.dismiss();
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+
+ /**
+ * 進入界面
+ */
+ protected void onEnter() {
+
+ }
+
+ /**
+ * 離開界面
+ */
+ protected void onExit() {
+
+ }
+
+}
diff --git a/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactFragment.java b/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactFragment.java
index 421f194..68b65f6 100644
--- a/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactFragment.java
+++ b/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactFragment.java
@@ -1,16 +1,37 @@
package com.ttstd.dialer.fragment.contact;
+import android.Manifest;
import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
+
+import androidx.core.app.ActivityCompat;
+import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.GridLayoutManager;
import com.ttstd.dialer.R;
+import com.ttstd.dialer.adapter.HomeContactAdapter;
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
import com.ttstd.dialer.databinding.FragmentContactBinding;
+import com.ttstd.dialer.db.contact.Contact;
+import com.ttstd.dialer.fragment.dialog.call.CallFragment;
+import com.ttstd.dialer.fragment.dialog.permission.PermissionDialogFragment;
+import com.ttstd.dialer.view.EqualHeightDecoration;
+
+import java.util.List;
public class ContactFragment extends BaseMvvmFragment {
- private static final String TAG ="ContactFragment";
+ private static final String TAG = "ContactFragment";
private Activity mContext;
+ private HomeContactAdapter mHomeContactAdapter;
+ private List mContacts;
+
+ private static final int REQUEST_CODE_CALL = 7897;
+
@Override
protected int getLayoutId() {
@@ -28,20 +49,44 @@ public class ContactFragment extends BaseMvvmFragment>() {
+ @Override
+ public void onChanged(List contacts) {
+ Log.e(TAG, "mContactListData: " + contacts);
+ mContacts = contacts;
+ mHomeContactAdapter.setContacts(mContacts);
+ }
+ });
}
+
@Override
public void fetchData() {
}
- public class BtnClick{
+ @Override
+ public void onResume() {
+ super.onResume();
+ mViewModel.getAllContacts();
+ }
+
+ public class BtnClick {
}
}
diff --git a/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactViewModel.java b/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactViewModel.java
index 0ee3790..32c458c 100644
--- a/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactViewModel.java
+++ b/app/src/main/java/com/ttstd/dialer/fragment/contact/ContactViewModel.java
@@ -1,9 +1,81 @@
package com.ttstd.dialer.fragment.contact;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.lifecycle.MutableLiveData;
+
+import com.trello.rxlifecycle4.RxLifecycle;
+import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentContactBinding;
+import com.ttstd.dialer.db.contact.Contact;
+import com.ttstd.dialer.db.contact.ContactRepository;
+
+import java.util.List;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
public class ContactViewModel extends BaseViewModel {
+ private ContactRepository mRepository;
+
+ @Override
+ public void setContext(Context context) {
+ super.setContext(context);
+ mRepository = new ContactRepository(context);
+ }
+
+ public MutableLiveData> mContactListData = new MutableLiveData<>();
+
+ public void getAllContacts() {
+ Observable.create(new ObservableOnSubscribe>() {
+ @Override
+ public void subscribe(@NonNull ObservableEmitter> emitter) throws Throwable {
+ List contacts = mRepository.getAllContacts();
+ emitter.onNext(contacts);
+ emitter.onComplete();
+ }
+ }).compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Observer>() {
+ @Override
+ public void onSubscribe(@NonNull Disposable d) {
+ Log.e("getAllContacts", "onSubscribe: ");
+ }
+
+ @Override
+ public void onNext(@NonNull List contacts) {
+ Log.e("getAllContacts", "onNext: ");
+ mContactListData.setValue(contacts);
+
+// List sorted = contacts.stream().sorted(new Comparator() {
+// @Override
+// public int compare(Contact o1, Contact o2) {
+// return Integer.compare(o1.getSort(), o2.getSort());
+// }
+// }).collect(Collectors.toList());
+// mContactListData.setValue(sorted);
+ }
+
+ @Override
+ public void onError(@NonNull Throwable e) {
+ Log.e("getAllContacts", "onError: " + e.getMessage());
+ }
+
+ @Override
+ public void onComplete() {
+ Log.e("getAllContacts", "onComplete: ");
+ }
+ });
+ }
}
diff --git a/app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallFragment.java b/app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallFragment.java
new file mode 100644
index 0000000..d774780
--- /dev/null
+++ b/app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallFragment.java
@@ -0,0 +1,150 @@
+package com.ttstd.dialer.fragment.dialog.call;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.ttstd.dialer.R;
+import com.ttstd.dialer.activity.contact.list.ContactListActivity;
+import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmDialogFragment;
+import com.ttstd.dialer.databinding.FragmentCallBinding;
+import com.ttstd.dialer.db.contact.Contact;
+
+public class CallFragment extends BaseMvvmDialogFragment {
+ private static final String TAG = "CallFragment";
+
+ private static final int REQUEST_CODE_CALL = 7897;
+
+ private Activity mContext;
+ private Contact mContact;
+
+ public CallFragment(Contact contact) {
+ mContact = contact;
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.fragment_call;
+ }
+
+ @Override
+ protected void initDataBinding() {
+ mContext = getActivity();
+ mViewModel.setContext(mContext);
+ mViewModel.setVDBinding(mViewDataBinding);
+ mViewModel.setLifecycle(getLifecycleSubject());
+ mViewDataBinding.setClick(new BtnClick());
+ }
+
+ @Override
+ protected void initView(Bundle bundle) {
+
+ }
+
+ @Override
+ protected void initData(Bundle savedInstanceState) {
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getDialog() != null) {
+ Window window = getDialog().getWindow();
+ if (window == null) return;
+ WindowManager.LayoutParams params = window.getAttributes();
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.gravity = Gravity.BOTTOM;
+ window.setAttributes(params);
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ getDialog().setCancelable(true);
+ getDialog().setCanceledOnTouchOutside(true);
+ }
+ }
+
+ @Override
+ public void show(FragmentManager manager, String tag) {
+ DialogFragment fragment = (DialogFragment) manager.findFragmentByTag(tag);
+ if (fragment != null && fragment.isAdded()
+ && fragment.getDialog() != null && fragment.getDialog().isShowing()) {
+ return;
+ }
+
+ try {
+ FragmentTransaction ft = manager.beginTransaction();
+ ft.add(this, tag);
+ ft.commitAllowingStateLoss();
+ } catch (Exception e) {
+ Log.e(TAG, "show: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_CODE_CALL) {
+ call();
+ dismiss();
+ }
+ }
+
+ @Override
+ public void fetchData() {
+
+ }
+
+ public void call() {
+ try {
+ String phone = mContact.getPhoneNumber();
+ Intent dialIntent = new Intent(Intent.ACTION_CALL);
+ Uri data = Uri.parse("tel:" + phone);
+ dialIntent.setData(data);
+ dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(dialIntent);
+ } catch (Exception e) {
+ Log.e(TAG, "callNumber: " + e.getMessage());
+ }
+ }
+
+ public class BtnClick {
+
+ public void callWechatVideo(View view) {
+
+ dismiss();
+ }
+
+ public void callWechatAudio(View view) {
+
+ dismiss();
+ }
+
+ public void callPhone(View view) {
+ if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL);
+ } else {
+ call();
+ }
+ }
+
+ public void cancel(View view) {
+ dismiss();
+ }
+ }
+}
diff --git a/app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallViewModel.java b/app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallViewModel.java
new file mode 100644
index 0000000..fa7ccef
--- /dev/null
+++ b/app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallViewModel.java
@@ -0,0 +1,10 @@
+package com.ttstd.dialer.fragment.dialog.call;
+
+import com.trello.rxlifecycle4.android.FragmentEvent;
+import com.ttstd.dialer.base.mvvm.BaseViewModel;
+import com.ttstd.dialer.databinding.FragmentCallBinding;
+
+public class CallViewModel extends BaseViewModel {
+
+
+}
diff --git a/app/src/main/java/com/ttstd/dialer/fragment/dialog/permission/PermissionDialogFragment.java b/app/src/main/java/com/ttstd/dialer/fragment/dialog/permission/PermissionDialogFragment.java
new file mode 100644
index 0000000..ba201d3
--- /dev/null
+++ b/app/src/main/java/com/ttstd/dialer/fragment/dialog/permission/PermissionDialogFragment.java
@@ -0,0 +1,117 @@
+package com.ttstd.dialer.fragment.dialog.permission;
+
+import android.Manifest;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
+import androidx.databinding.DataBindingUtil;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.ttstd.dialer.R;
+import com.ttstd.dialer.base.BaseDialogFragment;
+import com.ttstd.dialer.databinding.DialogFragmentPermissionsBinding;
+
+public class PermissionDialogFragment extends BaseDialogFragment {
+ private static final String TAG = "PermissionDialog";
+ private static final int REQUEST_CODE_CALL = 7897;
+
+ private View rootView;
+ private FragmentActivity mContext;
+ private DialogFragmentPermissionsBinding mBinding;
+ private String mContent;
+
+ public PermissionDialogFragment(String content) {
+ this.mContent = content;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ Log.e(TAG, "onCreateView: ");
+ mBinding = DataBindingUtil.inflate(inflater, R.layout.dialog_fragment_permissions, container, false);
+ mBinding.setClick(new BtnClick());
+ rootView = mBinding.getRoot();
+ mContext = getActivity();
+ initView();
+ return rootView;
+ }
+
+ private void initView() {
+ mBinding.tvContent.setText(mContent);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getDialog() != null) {
+ Window window = getDialog().getWindow();
+ if (window == null) return;
+ WindowManager.LayoutParams params = window.getAttributes();
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.gravity = Gravity.CENTER;
+ window.setAttributes(params);
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ getDialog().setCancelable(true);
+ getDialog().setCanceledOnTouchOutside(true);
+ }
+ }
+
+ @Override
+ public void show(FragmentManager manager, String tag) {
+ DialogFragment fragment = (DialogFragment) manager.findFragmentByTag(tag);
+ if (fragment != null && fragment.isAdded()
+ && fragment.getDialog() != null && fragment.getDialog().isShowing()) {
+ return;
+ }
+
+ try {
+ FragmentTransaction ft = manager.beginTransaction();
+ ft.add(this, tag);
+ ft.commitAllowingStateLoss();
+ } catch (Exception e) {
+ Log.e(TAG, "show: " + e.getMessage());
+ }
+ }
+
+
+ @Override
+ public void fetchData() {
+
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_CODE_CALL) {
+
+ dismiss();
+ }
+ }
+
+ public class BtnClick {
+ public void onDenied(View view) {
+ dismiss();
+ }
+
+ public void onGranted(View view) {
+ ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL);
+ dismiss();
+ }
+ }
+}
diff --git a/app/src/main/java/com/ttstd/dialer/fragment/home/HomeFragment.java b/app/src/main/java/com/ttstd/dialer/fragment/home/HomeFragment.java
index 5265cd2..ac56cfa 100644
--- a/app/src/main/java/com/ttstd/dialer/fragment/home/HomeFragment.java
+++ b/app/src/main/java/com/ttstd/dialer/fragment/home/HomeFragment.java
@@ -208,6 +208,7 @@ public class HomeFragment extends BaseMvvmFragment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/wechat_call_audio.xml b/app/src/main/res/drawable/wechat_call_audio.xml
new file mode 100644
index 0000000..4060c05
--- /dev/null
+++ b/app/src/main/res/drawable/wechat_call_audio.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/wechat_call_cancel.xml b/app/src/main/res/drawable/wechat_call_cancel.xml
new file mode 100644
index 0000000..4f5ff2a
--- /dev/null
+++ b/app/src/main/res/drawable/wechat_call_cancel.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/wechat_call_dialer.xml b/app/src/main/res/drawable/wechat_call_dialer.xml
new file mode 100644
index 0000000..c69cf35
--- /dev/null
+++ b/app/src/main/res/drawable/wechat_call_dialer.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/wechat_call_video.xml b/app/src/main/res/drawable/wechat_call_video.xml
new file mode 100644
index 0000000..7d13fb6
--- /dev/null
+++ b/app/src/main/res/drawable/wechat_call_video.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_contact_add.xml b/app/src/main/res/layout/activity_contact_add.xml
index b80fb1a..2bee53f 100644
--- a/app/src/main/res/layout/activity_contact_add.xml
+++ b/app/src/main/res/layout/activity_contact_add.xml
@@ -98,7 +98,6 @@
android:layout_width="wrap_content"
android:onClick="@{click::save}"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
android:layout_marginBottom="32dp"
android:maxLines="1"
android:singleLine="true"
diff --git a/app/src/main/res/layout/dialog_fragment_permissions.xml b/app/src/main/res/layout/dialog_fragment_permissions.xml
new file mode 100644
index 0000000..fbc426c
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_permissions.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_call.xml b/app/src/main/res/layout/fragment_call.xml
new file mode 100644
index 0000000..5d6412f
--- /dev/null
+++ b/app/src/main/res/layout/fragment_call.xml
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_contact.xml b/app/src/main/res/layout/item_contact.xml
index 5befc3d..b46681f 100644
--- a/app/src/main/res/layout/item_contact.xml
+++ b/app/src/main/res/layout/item_contact.xml
@@ -5,6 +5,7 @@
android:layout_height="wrap_content">
diff --git a/app/src/main/res/layout/item_contact_home.xml b/app/src/main/res/layout/item_contact_home.xml
new file mode 100644
index 0000000..a325264
--- /dev/null
+++ b/app/src/main/res/layout/item_contact_home.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index aa3fee9..bf50753 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,4 +1,5 @@
20sp
+ 8dp
\ No newline at end of file