From a9a89a357431760e042c9d60584df582c04d247b Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Sun, 26 Oct 2025 20:52:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BB=E9=A1=B5=E6=98=BE=E7=A4=BA=E8=81=94?= =?UTF-8?q?=E7=B3=BB=E4=BA=BA=EF=BC=8C=E5=A2=9E=E5=8A=A0=E8=81=94=E7=B3=BB?= =?UTF-8?q?=E4=BA=BA=E6=8B=A8=E5=8F=B7=E9=80=89=E9=A1=B9=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=9D=83=E9=99=90=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 8 +- app/src/main/AndroidManifest.xml | 2 + .../contact/list/ContactListActivity.java | 37 +++ .../dialer/adapter/ContactInfoAdapter.java | 27 ++ .../dialer/adapter/HomeContactAdapter.java | 131 +++++++++ .../mvvm/fragment/BaseMvvmDialogFragment.java | 276 ++++++++++++++++++ .../fragment/contact/ContactFragment.java | 53 +++- .../fragment/contact/ContactViewModel.java | 72 +++++ .../fragment/dialog/call/CallFragment.java | 150 ++++++++++ .../fragment/dialog/call/CallViewModel.java | 10 + .../permission/PermissionDialogFragment.java | 117 ++++++++ .../dialer/fragment/home/HomeFragment.java | 1 + .../dialer/view/EqualHeightDecoration.java | 28 ++ app/src/main/res/drawable/bg_permissions.xml | 21 ++ .../main/res/drawable/wechat_call_audio.xml | 14 + .../main/res/drawable/wechat_call_cancel.xml | 14 + .../main/res/drawable/wechat_call_dialer.xml | 14 + .../main/res/drawable/wechat_call_video.xml | 14 + .../main/res/layout/activity_contact_add.xml | 1 - .../layout/dialog_fragment_permissions.xml | 104 +++++++ app/src/main/res/layout/fragment_call.xml | 203 +++++++++++++ app/src/main/res/layout/item_contact.xml | 2 + app/src/main/res/layout/item_contact_home.xml | 69 +++++ app/src/main/res/values/dimens.xml | 1 + 24 files changed, 1361 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/ttstd/dialer/adapter/HomeContactAdapter.java create mode 100644 app/src/main/java/com/ttstd/dialer/base/mvvm/fragment/BaseMvvmDialogFragment.java create mode 100644 app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallFragment.java create mode 100644 app/src/main/java/com/ttstd/dialer/fragment/dialog/call/CallViewModel.java create mode 100644 app/src/main/java/com/ttstd/dialer/fragment/dialog/permission/PermissionDialogFragment.java create mode 100644 app/src/main/java/com/ttstd/dialer/view/EqualHeightDecoration.java create mode 100644 app/src/main/res/drawable/bg_permissions.xml create mode 100644 app/src/main/res/drawable/wechat_call_audio.xml create mode 100644 app/src/main/res/drawable/wechat_call_cancel.xml create mode 100644 app/src/main/res/drawable/wechat_call_dialer.xml create mode 100644 app/src/main/res/drawable/wechat_call_video.xml create mode 100644 app/src/main/res/layout/dialog_fragment_permissions.xml create mode 100644 app/src/main/res/layout/fragment_call.xml create mode 100644 app/src/main/res/layout/item_contact_home.xml 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