diff --git a/app/build.gradle b/app/build.gradle index 6f5b3ad..fc3fc8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "com.xxpatx.os" minSdkVersion 24 targetSdkVersion 29 - versionCode 1066 - versionName "1.6.6" + versionCode 1068 + versionName "1.6.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -229,7 +229,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' /*xCrash */ implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0' //阿里云推送 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8b3c3a..cfd0676 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -287,6 +287,11 @@ android:name=".activity.service.ServiceActivity" android:launchMode="singleTask" android:theme="@style/activity_styles" /> + { + private static final String TAG = "PrivacyActivity"; + + + @Override + public boolean setNightMode() { + return true; + } + + @Override + public boolean setfitWindow() { + return true; + } + + @Override + protected int getLayoutId() { + return R.layout.activity_privacy; + } + + @Override + protected void initDataBinding() { + mViewModel.setCtx(this); + mViewModel.setVDBinding(mViewDataBinding); + mViewModel.setLifecycle(getLifecycleSubject()); + mViewDataBinding.setClick(new BtnClick()); + } + + @Override + protected void initView() { + WebSettings settings = mViewDataBinding.webView.getSettings(); +// settings.setUseWideViewPort(true); + settings.setJavaScriptEnabled(true); + settings.setAllowFileAccess(true); + settings.setAllowContentAccess(true); + settings.setAllowFileAccessFromFileURLs(true); + settings.setAllowUniversalAccessFromFileURLs(true); + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + + mViewDataBinding.webView.setWebViewClient(new WebViewClient()); + mViewDataBinding.webView.setWebChromeClient(new WebChromeClient() { + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + if (newProgress == 100) { + mViewDataBinding.progressBar.setVisibility(View.GONE); + } else { + mViewDataBinding.progressBar.setVisibility(View.VISIBLE); + mViewDataBinding.progressBar.setMax(100); + mViewDataBinding.progressBar.setProgress(newProgress); + } + } + }); + Intent intent = getIntent(); + int contentType = intent.getIntExtra("ContentType", 1); + switch (contentType) { + default: + case 1: + mViewDataBinding.tvTitle.setText("用户协议"); + mViewDataBinding.webView.loadUrl("https://www.uiuios.com/agreement.html?section=1-1&status=1&projectId=10"); + break; + case 2: + mViewDataBinding.tvTitle.setText("隐私政策"); + mViewDataBinding.webView.loadUrl("https://www.uiuios.com/agreement.html?section=1-2&status=1&projectId=10"); + break; + case 3: + mViewDataBinding.tvTitle.setText("SDK共享清单"); + mViewDataBinding.webView.loadUrl("https://www.uiuios.com/agreement.html?section=1-3&status=1&projectId=10"); + break; + case 4: + mViewDataBinding.tvTitle.setText("微信一键视频、语音通话功能用户须知"); + mViewDataBinding.webView.loadUrl("https://www.uiuios.com/agreement.html?section=3-6&status=1&projectId=10"); + break; + } + } + + @Override + protected void initData() { + + } + + public class BtnClick { + public void exit(View view) { + finish(); + } + } +} diff --git a/app/src/main/java/com/xxpatx/os/activity/privacy/PrivacyViewModel.java b/app/src/main/java/com/xxpatx/os/activity/privacy/PrivacyViewModel.java new file mode 100644 index 0000000..c29fa07 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/activity/privacy/PrivacyViewModel.java @@ -0,0 +1,19 @@ +package com.xxpatx.os.activity.privacy; + +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.xxpatx.os.base.mvvm.BaseViewModel; +import com.xxpatx.os.databinding.ActivityPrivacyBinding; + +public class PrivacyViewModel extends BaseViewModel { + + @Override + public ActivityPrivacyBinding getVDBinding() { + return binding; + } + + @Override + public void onDestroy() { + + } + +} diff --git a/app/src/main/java/com/xxpatx/os/activity/setting/SettingActivity.java b/app/src/main/java/com/xxpatx/os/activity/setting/SettingActivity.java index a6adc16..a808d38 100644 --- a/app/src/main/java/com/xxpatx/os/activity/setting/SettingActivity.java +++ b/app/src/main/java/com/xxpatx/os/activity/setting/SettingActivity.java @@ -32,6 +32,7 @@ import com.xxpatx.os.bean.AppInfo; import com.xxpatx.os.bean.SystemSettings; import com.xxpatx.os.config.CommonConfig; import com.xxpatx.os.databinding.ActivitySettingBinding; +import com.xxpatx.os.fragment.dialog.PrivacyPolicyFragment; import com.xxpatx.os.service.main.MainService; import com.xxpatx.os.utils.AccessibilityUtils; import com.xxpatx.os.utils.ApkUtils; @@ -277,21 +278,86 @@ public class SettingActivity extends BaseMvvmActivity factoryClass = Class.forName("android.webkit.WebViewFactory"); + Field field = factoryClass.getDeclaredField("sProviderInstance"); + field.setAccessible(true); + Object sProviderInstance = field.get(null); + if (sProviderInstance != null) { + Log.d(TAG, "sProviderInstance isn't null"); + return; + } + Method getProviderClassMethod; + if (sdkInt > 22) { // above 22 + getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass"); + } else if (sdkInt == 22) { // method name is a little different + getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass"); + } else { // no security check below 22 + Log.i(TAG, "Don't need to Hook WebView"); + return; + } + getProviderClassMethod.setAccessible(true); + Class providerClass = (Class) getProviderClassMethod.invoke(factoryClass); + Class delegateClass = Class.forName("android.webkit.WebViewDelegate"); + Constructor declaredConstructor = delegateClass.getDeclaredConstructor(); + declaredConstructor.setAccessible(true); + sProviderInstance = providerClass + .getDeclaredMethod("create", delegateClass) + .invoke(providerClass, declaredConstructor.newInstance()); + Log.d("sProviderInstance", sProviderInstance.toString()); + field.set("sProviderInstance", sProviderInstance); + Log.d(TAG, "Hook done!"); + } catch (Throwable e) { + Log.e(TAG, "hook WebView Failed", e); + } + } } diff --git a/app/src/main/java/com/xxpatx/os/config/CommonConfig.java b/app/src/main/java/com/xxpatx/os/config/CommonConfig.java index a5654e7..b31b888 100644 --- a/app/src/main/java/com/xxpatx/os/config/CommonConfig.java +++ b/app/src/main/java/com/xxpatx/os/config/CommonConfig.java @@ -108,6 +108,11 @@ public class CommonConfig { public static final String VOICE_BROADCAST = "voice_broadcast_key"; /*微信语音自动接听*/ public static final String WECHAT_CALL_AUTO_ACCEPT = "wechat_call_auto_accept"; + /*微信语音自动免提*/ + public static final String WECHAT_AUTO_HNADS_FREE = "wechat_auto_hands_free"; + /*是否为微信自动拨打电话*/ + public static final String WECHAT_AUTO_CALL_KEY = "wechat_auto_call_video"; + /*app启动播报*/ public static final String VOICE_SPEAKER_KEY = "voice_speaker"; /*电话白名单*/ diff --git a/app/src/main/java/com/xxpatx/os/fragment/dialog/PrivacyPolicyFragment.java b/app/src/main/java/com/xxpatx/os/fragment/dialog/PrivacyPolicyFragment.java new file mode 100644 index 0000000..e6b1c31 --- /dev/null +++ b/app/src/main/java/com/xxpatx/os/fragment/dialog/PrivacyPolicyFragment.java @@ -0,0 +1,182 @@ +package com.xxpatx.os.fragment.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.ForegroundColorSpan; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.CompoundButton; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +import com.xxpatx.os.R; +import com.xxpatx.os.activity.privacy.PrivacyActivity; +import com.xxpatx.os.databinding.FragmentPrivacyPolicyBinding; + +public class PrivacyPolicyFragment extends DialogFragment { + private static final String TAG = "PrivacyPolicyFragment"; + + private FragmentPrivacyPolicyBinding mBinding; + private View rootView; + private Context mContext; + + public interface DialogFragmentCallback { + void onPositive(); + + void onNegative(); + + void onDismiss(); + } + + private DialogFragmentCallback mDismissCallback; + + public PrivacyPolicyFragment(DialogFragmentCallback dialogFragmentCallback) { + this.mDismissCallback = dialogFragmentCallback; + // Required empty public constructor + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @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.fragment_privacy_policy, container, false); + mBinding.setClick(new BtnClick()); + rootView = mBinding.getRoot(); + mContext = rootView.getContext(); + initView(); + return rootView; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + return super.onCreateDialog(savedInstanceState); + } + + private void initView() { + mBinding.tvConfirm.setEnabled(false); + mBinding.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + mBinding.tvConfirm.setEnabled(true); + } else { + mBinding.tvConfirm.setEnabled(false); + } + } + }); + + SpannableString spannableString = new SpannableString(mContext.getResources().getString(R.string.user_notice)); + spannableString.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { +// String url = "https://www.uiuios.com/agreement.html?section=1-1&status=1"; +// CustomTabsIntent intent = new CustomTabsIntent.Builder().build(); +// try { +// intent.launchUrl(mContext, Uri.parse(url)); +// } catch (Exception e) { +// Log.e(TAG, "onClick: " + e.getMessage()); +// } + + Intent intent = new Intent(mContext, PrivacyActivity.class); + intent.putExtra("ContentType", 4); + mContext.startActivity(intent); + } + }, 0, spannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + spannableString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + mBinding.tvUserNotice.setText(spannableString); + mBinding.tvUserNotice.setMovementMethod(LinkMovementMethod.getInstance()); + } + + @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.WRAP_CONTENT; + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + 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 onDestroy() { + super.onDestroy(); + } + + @Override + public void onDismiss(@NonNull DialogInterface dialog) { + super.onDismiss(dialog); + if (mDismissCallback != null) { + mDismissCallback.onDismiss(); + } + } + + public class BtnClick { + + public void onPositiveClick(View view) { + if (mBinding.checkBox.isChecked()) { + if (mDismissCallback != null) { + mDismissCallback.onPositive(); + } + dismiss(); + } else { + if (mDismissCallback != null) { + mDismissCallback.onNegative(); + } + } + } + + public void onRead(View view) { + mBinding.checkBox.setChecked(!mBinding.checkBox.isChecked()); + } + + } +} diff --git a/app/src/main/java/com/xxpatx/os/service/main/MainSPresenter.java b/app/src/main/java/com/xxpatx/os/service/main/MainSPresenter.java index 7a6dfca..4fe2835 100644 --- a/app/src/main/java/com/xxpatx/os/service/main/MainSPresenter.java +++ b/app/src/main/java/com/xxpatx/os/service/main/MainSPresenter.java @@ -313,12 +313,16 @@ public class MainSPresenter implements MainSContact.Presenter { if (baseResponse.code == 200) { ClockSetting clockSetting = baseResponse.data; String time = clockSetting.getClock(); - List integers = new ArrayList<>(); - List timeLsit = new ArrayList<>(Arrays.asList(time.split(","))); - for (String s : timeLsit) { - integers.add(Integer.parseInt(s)); + if (!TextUtils.isEmpty(time)) { + List integers = new ArrayList<>(); + List timeLsit = new ArrayList<>(Arrays.asList(time.split(","))); + for (String s : timeLsit) { + integers.add(Integer.parseInt(s)); + } + mMMKV.encode(CommonConfig.TIME_HOUR_LIST_KEY, GsonUtils.toJSONString(integers)); + } else { + mMMKV.encode(CommonConfig.TIME_HOUR_LIST_KEY, ""); } - mMMKV.encode(CommonConfig.TIME_HOUR_LIST_KEY, GsonUtils.toJSONString(integers)); } } diff --git a/app/src/main/res/drawable-hdpi/icon_back_black.png b/app/src/main/res/drawable-hdpi/icon_back_black.png new file mode 100644 index 0000000..5bd377b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_back_black.png differ diff --git a/app/src/main/res/drawable/ic_negative.xml b/app/src/main/res/drawable/ic_negative.xml new file mode 100644 index 0000000..2a3f38c --- /dev/null +++ b/app/src/main/res/drawable/ic_negative.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_positive.xml b/app/src/main/res/drawable/ic_positive.xml new file mode 100644 index 0000000..557dcdf --- /dev/null +++ b/app/src/main/res/drawable/ic_positive.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/policy_checkbox_bg.xml b/app/src/main/res/drawable/policy_checkbox_bg.xml new file mode 100644 index 0000000..ae1863c --- /dev/null +++ b/app/src/main/res/drawable/policy_checkbox_bg.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/privacy_policy_card_bg.xml b/app/src/main/res/drawable/privacy_policy_card_bg.xml new file mode 100644 index 0000000..fa6a0ce --- /dev/null +++ b/app/src/main/res/drawable/privacy_policy_card_bg.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/privacy_policy_negative_bg.xml b/app/src/main/res/drawable/privacy_policy_negative_bg.xml new file mode 100644 index 0000000..a6b2310 --- /dev/null +++ b/app/src/main/res/drawable/privacy_policy_negative_bg.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/privacy_policy_positive_bg.xml b/app/src/main/res/drawable/privacy_policy_positive_bg.xml new file mode 100644 index 0000000..da28637 --- /dev/null +++ b/app/src/main/res/drawable/privacy_policy_positive_bg.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/privacy_policy_positive_selector.xml b/app/src/main/res/drawable/privacy_policy_positive_selector.xml new file mode 100644 index 0000000..8604090 --- /dev/null +++ b/app/src/main/res/drawable/privacy_policy_positive_selector.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progress_background.xml b/app/src/main/res/drawable/progress_background.xml new file mode 100644 index 0000000..4f3104c --- /dev/null +++ b/app/src/main/res/drawable/progress_background.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_privacy.xml b/app/src/main/res/layout/activity_privacy.xml new file mode 100644 index 0000000..8be538e --- /dev/null +++ b/app/src/main/res/layout/activity_privacy.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml index 2f80520..1867dba 100644 --- a/app/src/main/res/layout/activity_setting.xml +++ b/app/src/main/res/layout/activity_setting.xml @@ -22,6 +22,10 @@ name="auto_accept" type="Boolean" /> + + @@ -102,7 +106,7 @@ android:onClick="@{click::openFloat}"> + + + + + + + + + + + - + android:onClick="@{click::callWechatVoice}"> + android:onClick="@{click::callWechatVideo}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 eaf4825..75e9000 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,9 @@ Settings 已清理%d个应用 + 您当前要开启微信自动拨打视频、语音、自动接听等功能,此功能需要开启系统的无障碍服务才能实现。用户在开启前需要明确知晓其风险,请详细查看协议内容,用户同意后方可使用此功能。 + 《微信一键视频、语音通话功能用户须知》 + Messages Sync diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4398925..e041b28 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -26,6 +26,15 @@ @color/gray + +