优化天气,增加悬浮窗,增加快捷拨号,增加设置默认桌面
This commit is contained in:
@@ -181,6 +181,8 @@ ext {
|
||||
|
||||
dependencies {
|
||||
// implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compileOnly files('libs/framework.jar')
|
||||
|
||||
//Android 4.4+
|
||||
implementation files('libs/QWeather_Public_Android_V5.1.2.jar')
|
||||
implementation 'net.i2p.crypto:eddsa:0.3.0'
|
||||
@@ -333,3 +335,29 @@ dependencies {
|
||||
// implementation 'me.jessyan:autosize:1.2.1'
|
||||
|
||||
}
|
||||
|
||||
|
||||
preBuild {
|
||||
doLast {
|
||||
def imlFile = file(project.name + ".iml")
|
||||
// def imlFile = file("..\\.idea\\modules\\" + project.name + "\\" + rootProject.name + "." + project.name + ".iml")
|
||||
println 'Change ' + project.name + '.iml order'
|
||||
try {
|
||||
def parsedXml = (new XmlParser()).parse(imlFile)
|
||||
def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
|
||||
parsedXml.component[1].remove(jdkNode)
|
||||
def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
|
||||
println 'what' + sdkString
|
||||
new Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
|
||||
groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
|
||||
} catch (FileNotFoundException e) {
|
||||
// nop, iml not found
|
||||
println "no iml found"
|
||||
}
|
||||
}
|
||||
//https://www.pianshen.com/article/93481144911/
|
||||
//https://blog.csdn.net/dhl_1986/article/details/102856026
|
||||
//https://blog.csdn.net/zhonghe1114/article/details/80923730
|
||||
//https://blog.csdn.net/u014175785/article/details/116235760
|
||||
//使用系统编译后的framework.jar
|
||||
}
|
||||
BIN
app/libs/framework.jar
Normal file
BIN
app/libs/framework.jar
Normal file
Binary file not shown.
@@ -6,9 +6,16 @@
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.GET_TASKS" />
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
|
||||
<uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
|
||||
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
|
||||
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
|
||||
|
||||
|
||||
<!--baidumap start-->
|
||||
<!-- 这个权限用于进行网络定位-->
|
||||
@@ -43,11 +50,14 @@
|
||||
android:name=".activity.main.MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:excludeFromRecents="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppThemeFitsSystemWindows">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
@@ -67,10 +77,28 @@
|
||||
android:name=".activity.app.AppListActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity android:name=".activity.weather.main.WeatherMainActivity"
|
||||
<activity
|
||||
android:name=".activity.weather.main.WeatherMainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/AppThemeBlack"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.settings.home.SettingsActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.settings.call.SettingsCallActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.settings.utils.SettingsUtilsActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<service
|
||||
android:name=".service.main.MainService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
|
||||
<service
|
||||
android:name=".service.DialerAccessibilityService"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ttstd.dialer.activity.app;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
@@ -84,6 +86,8 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
|
||||
|
||||
|
||||
public class BtnClick {
|
||||
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
|
||||
public void save(View view) {
|
||||
String name = mViewDataBinding.etName.getText().toString();
|
||||
String phone = mViewDataBinding.etPhone.getText().toString();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ttstd.dialer.activity.contact.edit;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.databinding.ActivityContactEditBinding;
|
||||
@@ -37,7 +39,9 @@ public class ContactEditActivity extends BaseMvvmActivity<ContactEditViewModel,
|
||||
|
||||
|
||||
public class BtnClick{
|
||||
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -136,6 +136,10 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
|
||||
public void addContact(View view) {
|
||||
startActivity(new Intent(ContactListActivity.this, ContactAddActivity.class));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.ttstd.dialer.fragment.app.AppFragment;
|
||||
import com.ttstd.dialer.fragment.contact.ContactFragment;
|
||||
import com.ttstd.dialer.fragment.home.HomeFragment;
|
||||
import com.ttstd.dialer.fragment.settings.SettingsFragment;
|
||||
import com.ttstd.dialer.service.main.MainService;
|
||||
import com.ttstd.dialer.view.ApkPagerAdapter;
|
||||
import com.ttstd.dialer.view.ScaleCircleNavigator;
|
||||
|
||||
@@ -150,6 +151,9 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
Log.e(TAG, "initView: mDefaultIndex = " + mDefaultIndex);
|
||||
Log.e(TAG, "initView: mFragmentSize = " + mFragmentSize);
|
||||
|
||||
Intent intent = new Intent(this, MainService.class);
|
||||
startService(intent);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.ttstd.dialer.activity.settings.call;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.settings.home.SettingsViewModel;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsBinding;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsCallBinding;
|
||||
|
||||
public class SettingsCallActivity extends BaseMvvmActivity<SettingsCallViewModel, ActivitySettingsCallBinding> {
|
||||
private static final String TAG = "SettingsActivity";
|
||||
|
||||
@Override
|
||||
public boolean setfitWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_settings_call;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mViewModel.setContext(this);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ttstd.dialer.activity.settings.call;
|
||||
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsCallBinding;
|
||||
|
||||
public class SettingsCallViewModel extends BaseViewModel<ActivitySettingsCallBinding, ActivityEvent> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.ttstd.dialer.activity.settings.home;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.settings.call.SettingsCallActivity;
|
||||
import com.ttstd.dialer.activity.settings.utils.SettingsUtilsActivity;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsBinding;
|
||||
|
||||
public class SettingsActivity extends BaseMvvmActivity<SettingsViewModel, ActivitySettingsBinding> {
|
||||
private static final String TAG = "SettingsActivity";
|
||||
|
||||
@Override
|
||||
public boolean setfitWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mViewModel.setContext(this);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
|
||||
public void openCallSettings(View view) {
|
||||
startActivity(new Intent(SettingsActivity.this, SettingsCallActivity.class));
|
||||
}
|
||||
|
||||
public void openUtilsSettings(View view) {
|
||||
startActivity(new Intent(SettingsActivity.this, SettingsUtilsActivity.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ttstd.dialer.activity.settings.home;
|
||||
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsBinding;
|
||||
import com.ttstd.dialer.databinding.ActivityTemplateBinding;
|
||||
|
||||
public class SettingsViewModel extends BaseViewModel<ActivitySettingsBinding, ActivityEvent> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.ttstd.dialer.activity.settings.utils;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.main.MainActivity;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsUtilsBinding;
|
||||
import com.ttstd.dialer.service.main.MainService;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
import com.ttstd.dialer.view.ToggleButton;
|
||||
|
||||
public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewModel, ActivitySettingsUtilsBinding> {
|
||||
private static final String TAG = "SettingsActivity";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public boolean setfitWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_settings_utils;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mViewModel.setContext(this);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
mViewDataBinding.siFloatWindow.setOnToggleChanged(new ToggleButton.OnToggleChanged() {
|
||||
@Override
|
||||
public void onToggle(boolean on) {
|
||||
Intent intent = new Intent(SettingsUtilsActivity.this, MainService.class);
|
||||
if (on) {
|
||||
intent.setAction(MainService.SHOW_FLOAT_WINDOW_ACTION);
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_ENABLE, 1);
|
||||
} else {
|
||||
intent.setAction(MainService.HIDE_FLOAT_WINDOW_ACTION);
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_ENABLE, 0);
|
||||
}
|
||||
startService(intent);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mViewDataBinding.siFloatWindowKill.setOnToggleChanged(new ToggleButton.OnToggleChanged() {
|
||||
@Override
|
||||
public void onToggle(boolean on) {
|
||||
if (on) {
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_KILL_APP, 1);
|
||||
} else {
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_KILL_APP, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewDataBinding.siDefaultLauncher.setOnToggleChanged(new ToggleButton.OnToggleChanged() {
|
||||
@Override
|
||||
public void onToggle(boolean on) {
|
||||
if (on) {
|
||||
// SystemUtils.setDefaultLauncher(SettingsUtilsActivity.this, MainActivity.class);
|
||||
// SystemUtils.setDefaultLauncher(SettingsUtilsActivity.this);
|
||||
SystemUtils.addRoleHolderAsUser(SettingsUtilsActivity.this, BuildConfig.APPLICATION_ID);
|
||||
} else {
|
||||
SystemUtils.setOtherDefaultLauncher(SettingsUtilsActivity.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
int enableFloatWindow = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_ENABLE, 0);
|
||||
Log.e(TAG, "initView: enableFloatWindow = " + enableFloatWindow);
|
||||
mViewDataBinding.siFloatWindow.setToggleStatu(enableFloatWindow == 1);
|
||||
|
||||
int floatWindowKillApp = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_KILL_APP, 0);
|
||||
Log.e(TAG, "initView: floatWindowKillApp = " + floatWindowKillApp);
|
||||
mViewDataBinding.siFloatWindowKill.setToggleStatu(floatWindowKillApp == 1);
|
||||
|
||||
boolean defaultLauncher = SystemUtils.isDefaultLauncher(SettingsUtilsActivity.this, MainActivity.class);
|
||||
Log.e(TAG, "initView: defaultLauncher = " + defaultLauncher);
|
||||
mViewDataBinding.siDefaultLauncher.setToggleStatu(defaultLauncher);
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
|
||||
public void screenshotSnap(View view) {
|
||||
// Bitmap bitmap = SystemUtils.takeFullScreenshot(SettingsUtilsActivity.this);
|
||||
Bitmap bitmap = SystemUtils.takeScreenshotHighVersion();
|
||||
if (bitmap != null) {
|
||||
mViewDataBinding.ivSnap.setImageBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ttstd.dialer.activity.settings.utils;
|
||||
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsCallBinding;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsUtilsBinding;
|
||||
|
||||
public class SettingsUtilsViewModel extends BaseViewModel<ActivitySettingsUtilsBinding, ActivityEvent> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.ttstd.dialer.activity.template;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.databinding.ActivityTemplateBinding;
|
||||
|
||||
public class TemplateActivity extends BaseMvvmActivity<TemplateViewModel, ActivityTemplateBinding> {
|
||||
private static final String TAG = "TemplateActivity";
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setfitWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_template;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mViewModel.setContext(this);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void exit(View view) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ttstd.dialer.activity.template;
|
||||
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityTemplateBinding;
|
||||
|
||||
public class TemplateViewModel extends BaseViewModel<ActivityTemplateBinding, ActivityEvent> {
|
||||
|
||||
}
|
||||
@@ -35,6 +35,12 @@ public class WeatherMainActivity extends BaseMvvmActivity<WeatherMainViewModel,
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_weather_main;
|
||||
|
||||
@@ -44,8 +44,8 @@ public class HourlyWeatherAdapter extends RecyclerView.Adapter<HourlyWeatherAdap
|
||||
mContext = (FragmentActivity) parent.getContext();
|
||||
requestBuilder = GlideApp.with(mContext)
|
||||
.as(PictureDrawable.class)
|
||||
.placeholder(R.drawable.image_loading)
|
||||
.error(R.drawable.not_applicable)
|
||||
// .placeholder(R.drawable.image_loading)
|
||||
.error(R.drawable.ic_not_applicable)
|
||||
.transition(withCrossFade())
|
||||
.listener(new SvgSoftwareLayerSetter());
|
||||
return new HourlyWeather(LayoutInflater.from(mContext).inflate(R.layout.item_hourly_weather, parent, false));
|
||||
@@ -53,31 +53,34 @@ public class HourlyWeatherAdapter extends RecyclerView.Adapter<HourlyWeatherAdap
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull HourlyWeather holder, int position) {
|
||||
WeatherHourly weatherHourly = mWeatherHourlyList.get(position);
|
||||
String fxTime = weatherHourly.getFxTime();
|
||||
holder.tv_time.setText(TimeUtils.formatToHourDescription(fxTime));
|
||||
String icon = weatherHourly.getIcon();
|
||||
// 获取资源ID
|
||||
String fileName = "qweather_" + icon; // 替换为你的文件名
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "raw", mContext.getPackageName());
|
||||
if (resId != 0) {
|
||||
// 使用Glide加载,假设已配置SVG支持
|
||||
Glide.with(mContext)
|
||||
.as(PictureDrawable.class)
|
||||
.load(resId) // 加载raw资源ID
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE) // raw资源通常不缓存
|
||||
.into(holder.iv_icon);
|
||||
} else {
|
||||
// 处理错误
|
||||
Log.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
}
|
||||
if (mWeatherHourlyList != null && !mWeatherHourlyList.isEmpty()) {
|
||||
WeatherHourly weatherHourly = mWeatherHourlyList.get(position);
|
||||
String fxTime = weatherHourly.getFxTime();
|
||||
holder.tv_time.setText(TimeUtils.formatToHourDescription(fxTime));
|
||||
String icon = weatherHourly.getIcon();
|
||||
// 获取资源ID
|
||||
String fileName = "qweather_" + icon; // 替换为你的文件名
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "raw", mContext.getPackageName());
|
||||
if (resId != 0) {
|
||||
// 使用Glide加载,假设已配置SVG支持
|
||||
// Glide.with(mContext)
|
||||
// .as(PictureDrawable.class)
|
||||
// .load(resId) // 加载raw资源ID
|
||||
// .diskCacheStrategy(DiskCacheStrategy.NONE) // raw资源通常不缓存
|
||||
// .into(holder.iv_icon);
|
||||
requestBuilder.load(resId).into(holder.iv_icon);
|
||||
} else {
|
||||
// 处理错误
|
||||
Log.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
}
|
||||
|
||||
holder.tv_temp.setText(weatherHourly.getTemp() + "°");
|
||||
holder.tv_temp.setText(weatherHourly.getTemp() + "°");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mWeatherHourlyList == null ? 0 : mWeatherHourlyList.size();
|
||||
return mWeatherHourlyList == null ? 27 : mWeatherHourlyList.size();
|
||||
}
|
||||
|
||||
public class HourlyWeather extends RecyclerView.ViewHolder {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -9,15 +11,20 @@ import android.widget.TextView;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.qweather.sdk.response.weather.WeatherDaily;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.glide.GlideApp;
|
||||
import com.ttstd.dialer.glide.svg.SvgSoftwareLayerSetter;
|
||||
import com.ttstd.dialer.utils.DateUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherHolder> {
|
||||
private static final String TAG = "WeatherAdapter";
|
||||
|
||||
@@ -25,6 +32,8 @@ public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherH
|
||||
|
||||
private FragmentActivity mContext;
|
||||
private List<WeatherDaily> mWeatherDailyList;
|
||||
private RequestBuilder<PictureDrawable> requestBuilder;
|
||||
|
||||
|
||||
public void setWeatherDailyList(List<WeatherDaily> weatherDailyList) {
|
||||
mWeatherDailyList = weatherDailyList;
|
||||
@@ -34,31 +43,54 @@ public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherH
|
||||
@Override
|
||||
public WeatherHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
mContext = (FragmentActivity) parent.getContext();
|
||||
requestBuilder = GlideApp.with(mContext)
|
||||
.as(PictureDrawable.class)
|
||||
// .placeholder(R.drawable.image_loading)
|
||||
.error(R.drawable.ic_not_applicable)
|
||||
.transition(withCrossFade())
|
||||
.listener(new SvgSoftwareLayerSetter());
|
||||
return new WeatherHolder(LayoutInflater.from(mContext).inflate(R.layout.item_weather, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(WeatherHolder holder, int position) {
|
||||
WeatherDaily weatherDaily = mWeatherDailyList.get(position);
|
||||
String date = weatherDaily.getFxDate();
|
||||
if (DateUtil.isTodayAll(date)) {
|
||||
holder.tv_date.setText("今天");
|
||||
} else {
|
||||
String week = DateUtil.convertToChineseWeekdayAll(date);
|
||||
holder.tv_date.setText(week);
|
||||
if (mWeatherDailyList != null && !mWeatherDailyList.isEmpty()) {
|
||||
WeatherDaily weatherDaily = mWeatherDailyList.get(position);
|
||||
String date = weatherDaily.getFxDate();
|
||||
if (DateUtil.isTodayAll(date)) {
|
||||
holder.tv_date.setText("今天");
|
||||
} else {
|
||||
String week = DateUtil.convertToChineseWeekdayAll(date);
|
||||
holder.tv_date.setText(week);
|
||||
}
|
||||
|
||||
String tempMin = weatherDaily.getTempMin();
|
||||
holder.tv_temp_min.setText(tempMin + "°");
|
||||
String tempMax = weatherDaily.getTempMax();
|
||||
holder.tv_temp_max.setText(tempMax + "°");
|
||||
|
||||
String iconDay = weatherDaily.getIconDay();
|
||||
// 获取资源ID
|
||||
String fileName = "qweather_" + iconDay; // 替换为你的文件名
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "raw", mContext.getPackageName());
|
||||
if (resId != 0) {
|
||||
// 使用Glide加载,假设已配置SVG支持
|
||||
// Glide.with(mContext)
|
||||
// .as(PictureDrawable.class)
|
||||
// .load(resId) // 加载raw资源ID
|
||||
// .diskCacheStrategy(DiskCacheStrategy.NONE) // raw资源通常不缓存
|
||||
// .into(holder.iv_icon);
|
||||
requestBuilder.load(resId).into(holder.iv_icon);
|
||||
} else {
|
||||
// 处理错误
|
||||
Log.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
String tempMin = weatherDaily.getTempMin();
|
||||
holder.tv_temp_min.setText(tempMin + "°");
|
||||
String tempMax = weatherDaily.getTempMax();
|
||||
holder.tv_temp_max.setText(tempMax + "°");
|
||||
|
||||
String iconDay = weatherDaily.getIconDay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mWeatherDailyList == null ? 0 : mWeatherDailyList.size();
|
||||
return mWeatherDailyList == null ? 10 : mWeatherDailyList.size();
|
||||
}
|
||||
|
||||
public class WeatherHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@@ -5,6 +5,14 @@ public class CommonConfig {
|
||||
|
||||
public static final String CONTACT_HOME_PAGE = "contact_home_page_key";
|
||||
|
||||
public static final String FLOAT_WINDOW_X = "float_window_x_key";
|
||||
public static final String FLOAT_WINDOW_Y = "float_window_y_key";
|
||||
public static final String FLOAT_WINDOW_ENABLE = "float_window_enable_key";
|
||||
public static final String FLOAT_WINDOW_KILL_APP = "float_window_kill_app_key";
|
||||
|
||||
public static final String WECHAT_AUTO_ACCEPT_CALL= "wechat_auto_accept_call_key";
|
||||
public static final String WECHAT_AUTO_HNADS_FREE= "wechat_auto_hands_free_key";
|
||||
|
||||
public static final String MANUALLY_SELECT_LOCATION = "manually_select_location_key";
|
||||
|
||||
/*地址改变时发送*/
|
||||
|
||||
@@ -38,6 +38,9 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
// }
|
||||
|
||||
|
||||
public AppFragment() {
|
||||
}
|
||||
|
||||
public AppFragment(List<AppInfo> appInfos) {
|
||||
mAppInfos = appInfos;
|
||||
Log.e(TAG, "AppFragment: mAppInfos = " + mAppInfos.hashCode());
|
||||
|
||||
@@ -26,6 +26,8 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
|
||||
|
||||
private static final int REQUEST_CODE_CALL = 7897;
|
||||
|
||||
public ContactFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
|
||||
@@ -38,6 +38,9 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, Fragment
|
||||
private Activity mContext;
|
||||
private ContactInfo mContactInfo;
|
||||
|
||||
public CallFragment() {
|
||||
}
|
||||
|
||||
public CallFragment(ContactInfo contactInfo) {
|
||||
mContactInfo = contactInfo;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ public class PermissionDialogFragment extends BaseDialogFragment {
|
||||
private DialogFragmentPermissionsBinding mBinding;
|
||||
private String mContent;
|
||||
|
||||
public PermissionDialogFragment() {
|
||||
}
|
||||
|
||||
public PermissionDialogFragment(String content) {
|
||||
this.mContent = content;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
|
||||
private String mNegativeText;
|
||||
private String mPositiveText;
|
||||
|
||||
public ShortcutDialogFagment() {
|
||||
}
|
||||
|
||||
public ShortcutDialogFagment(AppInfo appInfo) {
|
||||
mAppInfo = appInfo;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ package com.ttstd.dialer.fragment.settings;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.app.AppListActivity;
|
||||
import com.ttstd.dialer.activity.settings.home.SettingsActivity;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentSettingsBinding;
|
||||
|
||||
@@ -17,6 +17,9 @@ public class SettingsFragment extends BaseMvvmFragment<SettingsViewModel, Fragme
|
||||
|
||||
private Activity mContext;
|
||||
|
||||
public SettingsFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_settings;
|
||||
@@ -48,7 +51,7 @@ public class SettingsFragment extends BaseMvvmFragment<SettingsViewModel, Fragme
|
||||
|
||||
public class BtnClick {
|
||||
public void openSettings(View view) {
|
||||
Intent intent = new Intent(Settings.ACTION_SETTINGS);
|
||||
Intent intent = new Intent(mContext, SettingsActivity.class);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SystemDeviceController implements IDeviceController {
|
||||
private Context mContext;
|
||||
|
||||
public SystemDeviceController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDisallowedRunningApp(String pkg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDisallowedRunningApp(String pkg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisallowedRunningApp(String pkg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallPackage(String pkg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownDevice() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebootDevice() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultLauncher(String pkg, String className) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableBluetoothTransfer(boolean disable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInstallPackageTrustList(List<String> packages) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getInstallPackageTrustList() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,135 @@
|
||||
package com.ttstd.dialer.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.Build;
|
||||
import android.os.Bundle;
|
||||
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.view.accessibility.AccessibilityWindowInfo;
|
||||
|
||||
import com.blankj.utilcode.util.ToastUtils;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.functions.Consumer;
|
||||
|
||||
/**
|
||||
* 通过微信标签最高支持8.0.49,8.0.50 获取不到数据
|
||||
* 8.0.54 可以获取
|
||||
* 通过 {@link AccessibilityService#getWindows}和修改accessibility-service 配置能遍历屏幕元素
|
||||
*/
|
||||
public class DialerAccessibilityService extends AccessibilityService {
|
||||
private static final String TAG = "AccessibilityService";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
|
||||
public static final int ACTION_VIDEO = 1;
|
||||
public static final int ACTION_AUDIO = 2;
|
||||
|
||||
private ContactInfo mContactInfo;
|
||||
private static final int ACTION_IME_ENTER_VERSION = 30;
|
||||
private static final int ACTION_IME_ENTER_ID = 16908372;
|
||||
|
||||
private static final String DIALER_TEXT = "音视频通话";
|
||||
private static final String CONTACT_TEXT = "通讯录";
|
||||
private static final String SEARCH_TEXT = "搜索";
|
||||
private static final String TAG_TEXT = "标签";
|
||||
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 = "接听";
|
||||
private static final String HANDS_FREE_TEXT = "扬声器已关";
|
||||
private static final String DIALER_HANDS_FREE_TEXT = "免提";
|
||||
private static final String DIALER_HANDS_FREE_CLOSE_TEXT = "免提,已关闭";
|
||||
|
||||
private static final int WAIT_TIME = 1600;
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
private Step mCurrentStep = Step.WAITING;
|
||||
private String mName = "";//微信昵称
|
||||
private String mTagName = "";//微信联系人标签名
|
||||
|
||||
private boolean mAutoAccept = false;
|
||||
private boolean mAutoHandsFree = false;
|
||||
|
||||
private ContactInfo mContactInfo;
|
||||
private int mCallType = ACTION_VIDEO;
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
|
||||
public interface AccessibilityEventCallback {
|
||||
void onAccessibilityEventCallback(AccessibilityEvent accessibilityEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
|
||||
}
|
||||
private AccessibilityEventCallback mAccessibilityEventCallback;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
registerSettingReceiver();
|
||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_AUTO_ACCEPT_CALL, false);
|
||||
mAutoHandsFree = mMMKV.decodeBool(CommonConfig.WECHAT_AUTO_HNADS_FREE, false);
|
||||
analysisAccessibilityEvent();
|
||||
}
|
||||
|
||||
private void analysisAccessibilityEvent() {
|
||||
Observable.create(new ObservableOnSubscribe<AccessibilityEvent>() {
|
||||
@Override
|
||||
public void subscribe(@NonNull ObservableEmitter<AccessibilityEvent> emitter) throws Throwable {
|
||||
mAccessibilityEventCallback = emitter::onNext;
|
||||
}
|
||||
}).throttleLast(WAIT_TIME, TimeUnit.MILLISECONDS)
|
||||
.subscribe(new Consumer<AccessibilityEvent>() {
|
||||
@Override
|
||||
public void accept(AccessibilityEvent accessibilityEvent) throws Throwable {
|
||||
Log.e(TAG, "analysisAccessibilityEvent accept: ");
|
||||
_onAccessibilityEvent(accessibilityEvent);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.e(TAG, "onStartCommand: ");
|
||||
if (intent != null) {
|
||||
mContactInfo = (ContactInfo) intent.getSerializableExtra("ContactInfo");
|
||||
mCallType = intent.getIntExtra("callType", 1);
|
||||
Log.e(TAG, "onStartCommand: contactInfo = " + mContactInfo);
|
||||
mCallType = intent.getIntExtra("call_type", ACTION_VIDEO);
|
||||
mName = mContactInfo.getName();
|
||||
mCurrentStep = Step.CLICK_HOME;
|
||||
if (mContactInfo != null) {
|
||||
startWeixin();
|
||||
}
|
||||
@@ -50,6 +140,847 @@ public class DialerAccessibilityService extends AccessibilityService {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.e(TAG, "onDestroy: ");
|
||||
if (mSettingReceiver != null) {
|
||||
unregisterReceiver(mSettingReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.v(TAG, "onAccessibilityEvent: event = " + event.toString());
|
||||
checkClassName(event);
|
||||
mAccessibilityEventCallback.onAccessibilityEventCallback(event);
|
||||
}
|
||||
|
||||
private void checkClassName(AccessibilityEvent event) {
|
||||
Log.e(TAG, "checkClassName: mCurrentStep = " + mCurrentStep);
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
String currentPackageName = event.getPackageName().toString();
|
||||
String currentClassName = event.getClassName().toString();
|
||||
|
||||
switch (mCurrentStep) {
|
||||
case WAITING:
|
||||
if (!TextUtils.isEmpty(currentPackageName) && "com.android.incallui".equals(currentPackageName)) {
|
||||
Log.e(TAG, "checkClassName: to dialer hands free");
|
||||
// mCurrentStep = Step.DIALER_HANDS_FREE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!TextUtils.isEmpty(currentClassName)) {
|
||||
switch (currentClassName) {
|
||||
case "com.tencent.mm.ui.LauncherUI":
|
||||
// if (mCurrentStep != Step.FIND_CONTACT) {
|
||||
// mCurrentStep = Step.CLICK_CONTACT;
|
||||
// }
|
||||
break;
|
||||
case "com.tencent.mm.plugin.account.ui.WelcomeActivity":
|
||||
case "com.tencent.mm.plugin.account.ui.LoginPasswordUI":
|
||||
Toaster.showLong("请先登录微信");
|
||||
mCurrentStep = Step.WAITING;
|
||||
break;
|
||||
case "com.tencent.mm.plugin.label.ui.ContactLabelManagerUI":
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1.在微信页面直接找到联系人拨打电话
|
||||
* 2.在联系人页面找到并拨打
|
||||
* 3.通过进入联系人-标签找到并拨打
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
private void _onAccessibilityEvent(AccessibilityEvent event) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: " + mCurrentStep);
|
||||
switch (mCurrentStep) {
|
||||
case WAITING:
|
||||
mAutoAccept = mMMKV.decodeBool(CommonConfig.WECHAT_AUTO_ACCEPT_CALL, false);
|
||||
Log.e(TAG, "_onAccessibilityEvent: mAutoAccept = " + mAutoAccept);
|
||||
if (mAutoAccept) {
|
||||
autoAccept();
|
||||
}
|
||||
break;
|
||||
case WECHAT_HANDS_FREE:
|
||||
mAutoHandsFree = mMMKV.decodeInt(CommonConfig.WECHAT_AUTO_HNADS_FREE, 0) == 1;
|
||||
Log.e(TAG, "_onAccessibilityEvent: mAutoHandsFree = " + mAutoHandsFree);
|
||||
if (mAutoHandsFree) {
|
||||
handsFree(Property.DESCRIPTION, HANDS_FREE_TEXT);
|
||||
} else {
|
||||
Log.e(TAG, "_onAccessibilityEvent: not enable auto handsfree");
|
||||
}
|
||||
case DIALER_HANDS_FREE:
|
||||
if (findHandsFree(Property.DESCRIPTION, DIALER_HANDS_FREE_CLOSE_TEXT)) {
|
||||
dialerHandsFree(Property.TEXT, DIALER_HANDS_FREE_TEXT);
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
}
|
||||
break;
|
||||
case CLICK_HOME://主页能找到直接点击进去更多
|
||||
if (stepHome(Property.TEXT, mName)) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: not found contact in home");
|
||||
} else {
|
||||
clickViewById("com.tencent.mm:id/jha", Step.CLICK_SEARCH);
|
||||
// step(Property.DESCRIPTION, SEARCH_TEXT, Step.CLICK_SEARCH);
|
||||
}
|
||||
break;
|
||||
case CLICK_SEARCH:
|
||||
putString(mName, Step.CLICK_SEARCH_CONTACT);
|
||||
break;
|
||||
case CLICK_SEARCH_CONTACT:
|
||||
if (findSearchContact(Step.FIND_CONTACT)) {
|
||||
findSearchContact(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL);
|
||||
} else {
|
||||
Toaster.show("没有找到联系人");
|
||||
}
|
||||
break;
|
||||
case CLICK_QUICK_WECHAT_CALL://点击更多页面
|
||||
clickViewById("com.tencent.mm:id/bjz", Step.CLICK_TARGET);
|
||||
// step(Property.DESCRIPTION, MORE_NAME, Step.CLICK_TARGET);
|
||||
break;
|
||||
case CLICK_TARGET://点击视频通话
|
||||
stepCall(Property.TEXT, PARENT_VIDEO_TEXT);
|
||||
// clickVideoCall();
|
||||
break;
|
||||
case CLICK_CALL://打视频或者电话
|
||||
if (mCallType == ACTION_AUDIO) {
|
||||
step(Property.TEXT, VIDEO_TEXT, Step.WAITING);
|
||||
} else if (mCallType == ACTION_VIDEO) {
|
||||
step(Property.TEXT, CALL_TEXT, Step.WAITING);
|
||||
}
|
||||
break;
|
||||
|
||||
case CLICK_CONTACT://进入通讯录界面
|
||||
if (stepHome(Property.TEXT, CONTACT_TEXT, Step.FIND_TAG)) {
|
||||
Log.e(TAG, "_onAccessibilityEvent: enter contact");
|
||||
} else {
|
||||
touchContact();
|
||||
}
|
||||
break;
|
||||
case FIND_CONTACT://模拟滑动找到联系人
|
||||
findSearchContact(Property.TEXT, mName, Step.CLICK_QUICK_WECHAT_CALL);
|
||||
break;
|
||||
case FIND_TAG:
|
||||
step(Property.TEXT, TAG_TEXT, Step.CLICK_TAG);
|
||||
break;
|
||||
case CLICK_TAG:
|
||||
if (!step(Property.TEXT, mTagName, Step.CLICK_NAME)) {
|
||||
Toaster.show("没有找到标签");
|
||||
mCurrentStep = Step.WAITING;
|
||||
}
|
||||
break;
|
||||
case CLICK_NAME://点击item
|
||||
findContact(Property.TEXT, mName, Step.CLICK_INFO);
|
||||
break;
|
||||
case CLICK_INFO://进入个人信息页面
|
||||
stepCallDialog(Property.TEXT, DIALER_TEXT, Step.CLICK_CALL);
|
||||
break;
|
||||
|
||||
// case CLICK_VIDEO_CALL:
|
||||
// if (step(Property.TEXT, VIDEO_TEXT)) {
|
||||
// Log.d(TAG, "finish, now: " + mCurrentStep);
|
||||
// ToastUtils.show("成功发起视频聊天");
|
||||
// }
|
||||
// break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
Log.e(TAG, "onInterrupt: ");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceConnected() {
|
||||
super.onServiceConnected();
|
||||
Log.e(TAG, "onServiceConnected: ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开这个界面可以直接选择联系人拨打
|
||||
*/
|
||||
private void openVoip() {
|
||||
Intent intent = new Intent();
|
||||
ComponentName component = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.contact.VoipAddressUI");
|
||||
intent.setComponent(component);
|
||||
intent.putExtra("voip_video", false);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void autoAccept() {
|
||||
if (stepAnswer(Property.DESCRIPTION, RECEIVE_DESCRIPTION)) {
|
||||
mCurrentStep = Step.WECHAT_HANDS_FREE;
|
||||
ToastUtils.showShort("已自动接听视频/语音");
|
||||
} else if (clickNode("com.tencent.mm:id/kfp", false)) {
|
||||
mCurrentStep = Step.WECHAT_HANDS_FREE;
|
||||
ToastUtils.showShort("已自动接听视频/语音");
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
// clickAnswer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param text 对应文本
|
||||
* @param simulate 是否通过坐标模拟点击
|
||||
* @return
|
||||
*/
|
||||
private boolean clickNode(String text, boolean simulate) {
|
||||
findFloatWindowNode(text);
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId(text);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
if (optional.isPresent()) {
|
||||
AccessibilityNodeInfo node = optional.get();
|
||||
if (node.isClickable()) {
|
||||
boolean performAction = node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
|
||||
Log.e(TAG, "clickNode: performAction = " + performAction);
|
||||
node.recycle();
|
||||
return performAction;
|
||||
} else {
|
||||
if (simulate) {
|
||||
Point point = getPointtByNode(node);
|
||||
Log.e(TAG, "clickNode: " + point);
|
||||
clickByPoint(point.x, point.y);
|
||||
Log.e(TAG, "clickNode: mCurrentStep " + mCurrentStep + " done");
|
||||
} else {
|
||||
clickNode(findClickableNode(node));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "clickNode: not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo findClickableNode(AccessibilityNodeInfo node) {
|
||||
if (node == null) {
|
||||
Log.e(TAG, "findClickableNode: node is null");
|
||||
return null;
|
||||
}
|
||||
if (node.isClickable()) {
|
||||
return node;
|
||||
} else {
|
||||
return findClickableNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
public void findFloatWindowNode(String id) {
|
||||
List<AccessibilityWindowInfo> windows = getWindows();
|
||||
for (AccessibilityWindowInfo window : windows) {
|
||||
// 筛选悬浮窗窗口
|
||||
if (isFloatingWindow(window)) {
|
||||
AccessibilityNodeInfo rootNode = window.getRoot();
|
||||
// 处理悬浮窗节点
|
||||
traverseNode(rootNode);
|
||||
rootNode.recycle(); // 释放资源
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isFloatingWindow(AccessibilityWindowInfo window) {
|
||||
Log.e(TAG, "isFloatingWindow: " + window.getType());
|
||||
// 根据窗口类型或标题筛选
|
||||
return window.getType() == AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
|
||||
}
|
||||
|
||||
private void traverseNode(AccessibilityNodeInfo node) {
|
||||
if (node == null) return;
|
||||
// 提取节点信息(如文本、ID等)
|
||||
String text = node.getText() != null ? node.getText().toString() : "";
|
||||
String id = node.getViewIdResourceName();
|
||||
// 递归遍历子节点
|
||||
for (int i = 0; i < node.getChildCount(); i++) {
|
||||
traverseNode(node.getChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void clickAnswer() {
|
||||
String className = SystemUtils.getForegroundActivityClassName(DialerAccessibilityService.this);
|
||||
Log.e(TAG, "clickAnswer: " + className);
|
||||
if (!TextUtils.isEmpty(className)) {
|
||||
if ("com.tencent.mm.plugin.voip.ui.VideoActivity".contentEquals(className)) {
|
||||
boolean successful = clickByPoint(595, 1376);
|
||||
Log.e(TAG, "clickAnswer: " + successful);
|
||||
if (successful) {
|
||||
ToastUtils.showShort("已自动接听视频/语音");
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "clickAnswer: Not in the answering interface");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean step(Property type, String text, Step nextStep) {
|
||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||
if (node != null) {
|
||||
Rect rect = new Rect();
|
||||
node.getBoundsInScreen(rect);
|
||||
Log.e(TAG, "step: rect = " + rect);
|
||||
if (rect.left < 0 || rect.top < 0 || rect.right < 0 || rect.bottom < 0) {
|
||||
return false;
|
||||
}
|
||||
clickNode(node);
|
||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e(TAG, "step: next: " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 2025/2/8 先把通讯录点击的换成node
|
||||
private boolean stepHome(Property type, String text, Step nextStep) {
|
||||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||||
if (node != null) {
|
||||
Rect rect = new Rect();
|
||||
node.getBoundsInScreen(rect);
|
||||
Log.e(TAG, "step: rect = " + rect);
|
||||
if (rect.left < 0 || rect.top < 0 || rect.right < 0 || rect.bottom < 0) {
|
||||
return false;
|
||||
}
|
||||
clickNode(node);
|
||||
Log.e(TAG, "step: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e(TAG, "step: next: " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void touchContact() {
|
||||
boolean successful = clickByPoint(268, 1440);
|
||||
if (successful) {
|
||||
mCurrentStep = Step.FIND_TAG;
|
||||
} else {
|
||||
mCurrentStep = Step.WAITING;
|
||||
Toaster.show("点击失败,请重试");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean findSearchContact(Step nextStep) {
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/gzf");
|
||||
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
return optional.isPresent();
|
||||
}
|
||||
|
||||
private void findSearchContact(Property type, String text, Step nextStep) {
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/odf");
|
||||
Log.e(TAG, "findSearchContact: " + nodeInfos);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
if (optional.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||
clickNode(nodeInfo);
|
||||
mCurrentStep = nextStep;
|
||||
} else {
|
||||
Toaster.show("没有找到联系人");
|
||||
mCurrentStep = Step.WAITING;
|
||||
}
|
||||
}
|
||||
|
||||
private void putString(String text, Step nextStep) {
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/d98");
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
if (optional.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||
Bundle args = new Bundle();
|
||||
args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
|
||||
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
|
||||
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS); // 确保焦点在输入框
|
||||
|
||||
if (Build.VERSION.SDK_INT >= ACTION_IME_ENTER_VERSION) {
|
||||
//see https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER
|
||||
nodeInfo.performAction(ACTION_IME_ENTER_ID);
|
||||
}
|
||||
mCurrentStep = nextStep;
|
||||
} else {
|
||||
Toaster.show("没有找到搜索框");
|
||||
mCurrentStep = Step.WAITING;
|
||||
}
|
||||
}
|
||||
|
||||
private void clickViewById(String id, Step nextStep) {
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId(id);
|
||||
Optional<AccessibilityNodeInfo> optional = nodeInfos.stream().findAny();
|
||||
if (optional.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = optional.get();
|
||||
clickNode(nodeInfo);
|
||||
mCurrentStep = nextStep;
|
||||
} else {
|
||||
// Toaster.show("没有找到搜索按钮");
|
||||
step(Property.DESCRIPTION, SEARCH_TEXT, Step.CLICK_SEARCH);
|
||||
}
|
||||
}
|
||||
|
||||
private List<AccessibilityNodeInfo> findNodesByViewId(String id) {
|
||||
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
|
||||
if (nodeInfo != null) {
|
||||
List<AccessibilityNodeInfo> accessibilityNodeInfos = nodeInfo.findAccessibilityNodeInfosByViewId(id);
|
||||
return accessibilityNodeInfos;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo findNodeByText(AccessibilityNodeInfo root, String text) {
|
||||
if (root == null) return null;
|
||||
Log.e(TAG, "findNodeByText: getText = " + root.getText());
|
||||
Log.e(TAG, "findNodeByText: getContentDescription = " + root.getContentDescription());
|
||||
boolean found = root.getText() != null && text.contentEquals(root.getText());
|
||||
if (found) {
|
||||
return root;
|
||||
} else {
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo result = findNodeByText(root.getChild(i), text);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
root.recycle();
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean stepCallDialog(Property type, String text, Step nextStep) {
|
||||
AccessibilityNodeInfo node = findNode(getRootInActiveWindow(), type, text);
|
||||
if (node != null) {
|
||||
Log.e(TAG, "stepCallDialog: isVisibleToUser: " + node.isVisibleToUser());
|
||||
if (node.isVisibleToUser()) {
|
||||
clickNode(node);
|
||||
Log.e(TAG, "stepCallDialog: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = nextStep;
|
||||
Log.e(TAG, "stepCallDialog: next: " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
scrollDown();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (mFindCount == mMaxCount) {
|
||||
Log.e("stepCallDialog", "mCurrentStep: max");
|
||||
ToastUtils.showShort("没有找到联系人");
|
||||
mCurrentStep = Step.WAITING;
|
||||
mFindCount = 0;
|
||||
return false;
|
||||
} else {
|
||||
Log.e("stepCallDialog", "mCurrentStep: not found");
|
||||
mFindCount++;
|
||||
Log.e("stepCallDialog", "mCurrentStep: mFindCount = " + mFindCount);
|
||||
scrollDown();
|
||||
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 = Step.CLICK_QUICK_WECHAT_CALL;
|
||||
Log.e(TAG, "stepHome: next: " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
mCurrentStep = Step.CLICK_SEARCH;
|
||||
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");
|
||||
ToastUtils.showShort("没有找到联系人");
|
||||
mCurrentStep = Step.WAITING;
|
||||
mFindCount = 0;
|
||||
return false;
|
||||
} else {
|
||||
Log.e("findContact", "mCurrentStep: not found");
|
||||
mFindCount++;
|
||||
Log.e("findContact", "mCurrentStep: mFindCount = " + mFindCount);
|
||||
scrollDown();
|
||||
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 AccessibilityNodeInfo findNode(List<AccessibilityWindowInfo> windows, Property type, String text) {
|
||||
for (AccessibilityWindowInfo accessibilityWindowInfo : windows) {
|
||||
AccessibilityNodeInfo nodeInfo = findNode(accessibilityWindowInfo.getRoot(), type, text);
|
||||
if (nodeInfo != null) {
|
||||
return nodeInfo;
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "findNode windows: not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void clickNode(AccessibilityNodeInfo node) {
|
||||
if (node == null) {
|
||||
Log.e(TAG, "clickNode: node is null");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Log.e(TAG, "clickNode: getText = " + node.getText());
|
||||
Log.e(TAG, "clickNode: isClickable = " + node.isClickable());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "clickNode: e = " + e.getMessage());
|
||||
}
|
||||
if (node.isClickable()) {
|
||||
//防检测机制:
|
||||
//添加随机延迟(避免高频操作)
|
||||
// handler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
//
|
||||
// }
|
||||
// }, 1000 + new Random().nextInt(100));
|
||||
boolean performAction = node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
|
||||
Log.e(TAG, "clickNode: performAction = " + performAction);
|
||||
if (!performAction) {
|
||||
Rect rect = new Rect();
|
||||
node.getBoundsInScreen(rect);
|
||||
Log.e(TAG, "clickNode: rect = " + rect);
|
||||
// 点击节点的中心位置
|
||||
int centerX = (rect.left + rect.right) / 2;
|
||||
int centerY = (rect.top + rect.bottom) / 2;
|
||||
Log.e(TAG, "clickNode: clickByNode = " + clickByPoint(centerX, centerY));
|
||||
}
|
||||
node.recycle();
|
||||
} else {
|
||||
Rect rect = new Rect();
|
||||
node.getBoundsInScreen(rect);
|
||||
Log.e(TAG, "clickNode: rect = " + rect);
|
||||
// 点击节点的中心位置
|
||||
int centerX = (rect.left + rect.right) / 2;
|
||||
int centerY = (rect.top + rect.bottom) / 2;
|
||||
Log.e(TAG, "clickNode: clickByNode = " + clickByPoint(centerX, centerY));
|
||||
}
|
||||
// else {
|
||||
// AccessibilityNodeInfo parent = node.getParent();
|
||||
// node.recycle();
|
||||
// clickNode(parent);
|
||||
// }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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);
|
||||
clickByPoint(point.x, point.y);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
private void clickVideoCall() {
|
||||
List<AccessibilityNodeInfo> nodeInfos = findNodesByViewId("com.tencent.mm:id/a12");
|
||||
Optional<AccessibilityNodeInfo> accessibilityNodeInfo = nodeInfos.stream().findAny();
|
||||
if (accessibilityNodeInfo.isPresent()) {
|
||||
AccessibilityNodeInfo nodeInfo = accessibilityNodeInfo.get();
|
||||
clickNode(nodeInfo);
|
||||
mCurrentStep = Step.CLICK_CALL;
|
||||
} else {
|
||||
Toaster.show("没有找到通话按钮");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean stepAnswer(Property type, String text) {
|
||||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||||
if (node != null) {
|
||||
Point point = getPointtByNode(node);
|
||||
Log.e(TAG, "stepAnswer: " + point);
|
||||
clickByPoint(point.x, point.y - 50);
|
||||
clickByPoint(point.x, point.y);
|
||||
// clickNode(node);
|
||||
Log.e(TAG, "stepAnswer: mCurrentStep " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.WAITING;
|
||||
Log.e(TAG, "stepAnswer: next " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "stepAnswer: not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean dialerHandsFree(Property type, String text) {
|
||||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||||
if (node != null) {
|
||||
Rect rect = new Rect();
|
||||
node.getBoundsInScreen(rect);
|
||||
Log.e(TAG, "dialerHandsFree: rect = " + rect);
|
||||
clickNode(node);
|
||||
Log.e(TAG, "dialerHandsFree: mCurrentStep: " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.WAITING;
|
||||
Log.e(TAG, "dialerHandsFree: next: " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean findHandsFree(Property type, String text) {
|
||||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||||
if (node != null) {
|
||||
Log.e(TAG, "findHandsFree: true");
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "findHandsFree: false");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handsFree(Property type, String text) {
|
||||
AccessibilityNodeInfo node = findNode(getWindows(), type, text);
|
||||
if (node != null) {
|
||||
Point point = getPointtByNode(node);
|
||||
Log.e(TAG, "handsFree: " + point);
|
||||
clickByPoint(point.x, point.y - 50);
|
||||
clickByPoint(point.x, point.y);
|
||||
// clickNode(node);
|
||||
Log.e(TAG, "handsFree: mCurrentStep " + mCurrentStep + " done");
|
||||
mCurrentStep = Step.WAITING;
|
||||
Log.e(TAG, "handsFree: next " + mCurrentStep);
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "handsFree: not found");
|
||||
mCurrentStep = Step.WAITING;
|
||||
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 clickByPoint(int x, int y) {
|
||||
Log.e(TAG, "clickByNode: x = " + x);
|
||||
Log.e(TAG, "clickByNode: y = " + 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 + new Random().nextInt(100)));
|
||||
GestureDescription gesture = builder.build();
|
||||
boolean dispatched = 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 dispatched;
|
||||
}
|
||||
|
||||
private boolean scrollScreen(double startY, double endY) {
|
||||
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, "scrollScreen: screenWidth = " + width);
|
||||
Log.e(TAG, "scrollScreen: screenHeight = " + height);
|
||||
int center_X = width / 2;
|
||||
int center_Y = height / 2;
|
||||
Log.e("scrollScreen", "center position:" + "(" + center_X + "," + center_Y + ")");
|
||||
Path path = new Path();
|
||||
path.moveTo(center_X, (int) (center_Y * startY)); //起点坐标。
|
||||
path.lineTo(center_X, (int) (center_Y * endY)); //终点坐标。
|
||||
GestureDescription.Builder builder = new GestureDescription.Builder();
|
||||
GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200)).build();
|
||||
boolean dispatched = dispatchGesture(gestureDescription, new GestureResultCallback() {
|
||||
@Override
|
||||
public void onCompleted(GestureDescription gestureDescription) {
|
||||
super.onCompleted(gestureDescription);
|
||||
Log.d("scrollScreen", "dispatchGesture ScrollUp onCompleted.");
|
||||
path.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(GestureDescription gestureDescription) {
|
||||
super.onCancelled(gestureDescription);
|
||||
Log.d("scrollScreen", "dispatchGesture ScrollUp cancel.");
|
||||
}
|
||||
}, null);
|
||||
return dispatched;
|
||||
}
|
||||
|
||||
private boolean scrollDown() {
|
||||
return scrollScreen(1.5, 0.5);
|
||||
}
|
||||
|
||||
private boolean scrollUp() {
|
||||
return scrollScreen(0.5, 1.5);
|
||||
}
|
||||
|
||||
private enum Step {
|
||||
WAITING,
|
||||
//微信免提
|
||||
WECHAT_HANDS_FREE,
|
||||
//电话免提
|
||||
DIALER_HANDS_FREE,
|
||||
|
||||
//1-1微信主页找用户名
|
||||
CLICK_HOME,
|
||||
//2-2进入搜索界面
|
||||
CLICK_SEARCH,
|
||||
//2-3是否弹出了联系人列表
|
||||
CLICK_SEARCH_CONTACT,
|
||||
//1-2 2-4聊天界面+号
|
||||
CLICK_QUICK_WECHAT_CALL,
|
||||
//1-3 2-5更多里面视频通话
|
||||
CLICK_TARGET,
|
||||
/*1-4 语音通话*/
|
||||
CLICK_CALL,
|
||||
|
||||
|
||||
//主页点击导航栏通讯录
|
||||
CLICK_CONTACT,
|
||||
FIND_CONTACT,
|
||||
//通讯录页面点击标签
|
||||
FIND_TAG,
|
||||
//点击对应的标签名
|
||||
CLICK_TAG,
|
||||
CLICK_NAME,
|
||||
CLICK_INFO,
|
||||
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 SettingReceiver mSettingReceiver;
|
||||
|
||||
private void registerSettingReceiver() {
|
||||
if (mSettingReceiver == null) {
|
||||
mSettingReceiver = new SettingReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(SETTING_CALL_TYPE_ACTION);
|
||||
filter.addAction(SETTING_AUTOMATIC_ANSWER_ACTION);
|
||||
registerReceiver(mSettingReceiver, filter);
|
||||
}
|
||||
|
||||
private class SettingReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
Log.e("SettingReceiver", "onReceive: " + action);
|
||||
if (TextUtils.isEmpty(action)) return;
|
||||
switch (action) {
|
||||
case SETTING_CALL_TYPE_ACTION:
|
||||
int callType = intent.getIntExtra("call_type", ACTION_VIDEO);
|
||||
mCallType = callType;
|
||||
Log.e("SettingReceiver", "onReceive: callType = " + callType);
|
||||
break;
|
||||
case SETTING_AUTOMATIC_ANSWER_ACTION:
|
||||
boolean autoAnswer = intent.getBooleanExtra("auto_answer", false);
|
||||
mAutoAccept = autoAnswer;
|
||||
Log.e("SettingReceiver", "onReceive: autoAnswer = " + autoAnswer);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startWeixin() {
|
||||
@@ -64,7 +995,7 @@ public class DialerAccessibilityService extends AccessibilityService {
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "launchWeChat: " + e.getMessage());
|
||||
Log.e(TAG, "startWeixin: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
258
app/src/main/java/com/ttstd/dialer/service/main/MainService.java
Normal file
258
app/src/main/java/com/ttstd/dialer/service/main/MainService.java
Normal file
@@ -0,0 +1,258 @@
|
||||
package com.ttstd.dialer.service.main;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.shehuan.niv.NiceImageView;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.main.MainActivity;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class MainService extends Service {
|
||||
private static final String TAG = "MainService";
|
||||
|
||||
public static final String SHOW_FLOAT_WINDOW_ACTION = "show_float_window";
|
||||
public static final String HIDE_FLOAT_WINDOW_ACTION = "hide_float_window";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
private View floatingView;
|
||||
private WindowManager.LayoutParams mLayoutParams;
|
||||
private boolean mFloatWindowShowing = false;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
|
||||
boolean enableFloatWindow = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_ENABLE, 0) == 1;
|
||||
Log.e(TAG, "onCreate: enableFloatWindow = " + enableFloatWindow);
|
||||
if (enableFloatWindow) {
|
||||
showFloatWindow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null) {
|
||||
String action = intent.getAction();
|
||||
Log.e(TAG, "onStartCommand: action = " + action);
|
||||
if (!TextUtils.isEmpty(action)) {
|
||||
switch (action) {
|
||||
case SHOW_FLOAT_WINDOW_ACTION:
|
||||
showFloatWindow();
|
||||
break;
|
||||
case HIDE_FLOAT_WINDOW_ACTION:
|
||||
hideFloatWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void showFloatWindow() {
|
||||
Log.e(TAG, "showFloatWindow: ");
|
||||
initFloatingView();
|
||||
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
// 4. 将 View 添加到 WindowManager
|
||||
try {
|
||||
mWindowManager.addView(floatingView, mLayoutParams);
|
||||
mFloatWindowShowing = true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: addView = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initFloatingView() {
|
||||
Log.e(TAG, "initFloatingView: ");
|
||||
floatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_window, null);
|
||||
|
||||
int layoutType;
|
||||
// Android 8.0 (API 26) 及以上需要使用 TYPE_APPLICATION_OVERLAY
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
layoutType = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
} else {
|
||||
layoutType = WindowManager.LayoutParams.TYPE_PHONE;
|
||||
}
|
||||
|
||||
mLayoutParams = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
layoutType,
|
||||
// FLAG_NOT_FOCUSABLE 确保悬浮窗不会拦截原本属于背后应用/桌面的按键事件
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT
|
||||
);
|
||||
|
||||
// 初始化位置
|
||||
mLayoutParams.gravity = Gravity.TOP | Gravity.START;
|
||||
mLayoutParams.x = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_X, 0);
|
||||
mLayoutParams.y = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_Y, 0);
|
||||
|
||||
NiceImageView nvBack = floatingView.findViewById(R.id.nv_back);
|
||||
nvBack.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.e(TAG, "onClick: ");
|
||||
// stopSelf(); // 停止服务,触发 onDestroy 移除悬浮窗
|
||||
// mWindowManager.removeView(floatingView);
|
||||
// floatingView = null;
|
||||
// mFloatWindowShowing = false;
|
||||
String foreground = SystemUtils.getForegroundActivityPackageName(MainService.this);
|
||||
if (BuildConfig.APPLICATION_ID.equals(foreground)) {
|
||||
int floatWindowKillApp = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_KILL_APP, 0);
|
||||
if (floatWindowKillApp == 1) {
|
||||
killApp();
|
||||
} else {
|
||||
|
||||
}
|
||||
} else {
|
||||
Intent intent = new Intent(MainService.this, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
nvBack.setOnTouchListener(new View.OnTouchListener() {
|
||||
private int initialX;
|
||||
private int initialY;
|
||||
private float initialTouchX;
|
||||
private float initialTouchY;
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
Log.e(TAG, "onTouch: ");
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 记录初始位置
|
||||
initialX = mLayoutParams.x;
|
||||
initialY = mLayoutParams.y;
|
||||
initialTouchX = event.getRawX();
|
||||
initialTouchY = event.getRawY();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 计算偏移量并更新位置
|
||||
mLayoutParams.x = initialX + (int) (event.getRawX() - initialTouchX);
|
||||
mLayoutParams.y = initialY + (int) (event.getRawY() - initialTouchY);
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_X, mLayoutParams.x);
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_Y, mLayoutParams.y);
|
||||
mWindowManager.updateViewLayout(floatingView, mLayoutParams);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void hideFloatWindow() {
|
||||
Log.e(TAG, "hideFloatWindow: ");
|
||||
if (mFloatWindowShowing) {
|
||||
if (floatingView != null) {
|
||||
mWindowManager.removeView(floatingView);
|
||||
floatingView = null;
|
||||
}
|
||||
mFloatWindowShowing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Set<String> mWhiteRunningAppSets = new HashSet<String>() {{
|
||||
this.add(BuildConfig.APPLICATION_ID);
|
||||
this.add("com.tencent.mm");
|
||||
this.add("com.ss.android.ugc.aweme");
|
||||
|
||||
this.add("com.tencent.wetype");
|
||||
this.add("com.tencent.qqpinyin");
|
||||
this.add("com.google.android.inputmethod.pinyin");
|
||||
this.add("com.sohu.inputmethod.sogou");
|
||||
this.add("com.iflytek.inputmethod");
|
||||
this.add("com.baidu.input");
|
||||
}};
|
||||
|
||||
private void killApp() {
|
||||
Log.e(TAG, "killApp: ");
|
||||
List<String> runningPackages = SystemUtils.getRunningTaskPackages(MainService.this);
|
||||
int i = 0;
|
||||
for (String pkg : runningPackages) {
|
||||
if (mWhiteRunningAppSets.contains(pkg)) {
|
||||
continue;
|
||||
}
|
||||
if (ApkUtils.isSystemApp(MainService.this, pkg)) {
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
SystemUtils.killBackgroundProcesses(MainService.this, pkg);
|
||||
}
|
||||
Toaster.show(String.format(getString(R.string.kill_app_number), i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 务必在服务销毁时移除 View,防止内存泄漏或系统崩溃
|
||||
if (floatingView != null) {
|
||||
mWindowManager.removeView(floatingView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
return super.onUnbind(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRebind(Intent intent) {
|
||||
super.onRebind(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
public class BitmapUtils {
|
||||
}
|
||||
@@ -2,14 +2,47 @@ package com.ttstd.dialer.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.role.RoleManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.os.Process;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
|
||||
|
||||
public class SystemUtils {
|
||||
private static final String TAG = "SystemUtils";
|
||||
|
||||
/**
|
||||
* 获取设备序列号
|
||||
@@ -72,4 +105,364 @@ public class SystemUtils {
|
||||
}
|
||||
|
||||
|
||||
public static String getForegroundActivityPackageName(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningTaskInfo> runningTaskInfos = activityManager.getRunningTasks(1);
|
||||
if (runningTaskInfos != null && !runningTaskInfos.isEmpty()) {
|
||||
ComponentName componentName = runningTaskInfos.get(0).topActivity;
|
||||
if (componentName != null) {
|
||||
String currentPackageName = componentName.getPackageName();
|
||||
return currentPackageName;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取栈顶的应用包名
|
||||
*/
|
||||
public static String getForegroundActivityClassName(Context context) {
|
||||
ActivityManager manager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
String currentClassName = manager.getRunningTasks(1).get(0).topActivity.getClassName();
|
||||
return currentClassName;
|
||||
}
|
||||
|
||||
public static List<String> getRunningTaskPackages(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningTaskInfo> runningTaskInfos = activityManager.getRunningTasks(Integer.MAX_VALUE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return runningTaskInfos.stream().map(new Function<ActivityManager.RunningTaskInfo, String>() {
|
||||
@Override
|
||||
public String apply(ActivityManager.RunningTaskInfo runningTaskInfo) {
|
||||
return runningTaskInfo.topActivity.getPackageName();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
} else {
|
||||
List<String> packageNames = new ArrayList<>();
|
||||
for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos) {
|
||||
packageNames.add(runningTaskInfo.topActivity.getPackageName());
|
||||
}
|
||||
return packageNames;
|
||||
}
|
||||
}
|
||||
|
||||
public static void killBackgroundProcesses(Context context, String processName) {
|
||||
Log.e(TAG, "killBackgroundProcesses: " + processName);
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
String packageName;
|
||||
try {
|
||||
if (!processName.contains(":")) {
|
||||
packageName = processName;
|
||||
} else {
|
||||
packageName = processName.split(":")[0];
|
||||
}
|
||||
activityManager.killBackgroundProcesses(packageName);
|
||||
activityManager.forceStopPackage(packageName);
|
||||
// removeTask(context, processName);
|
||||
|
||||
// Method forceStopPackage = activityManager.getClass()
|
||||
// .getDeclaredMethod("forceStopPackage", String.class);
|
||||
// forceStopPackage.setAccessible(true);
|
||||
// forceStopPackage.invoke(activityManager, packageName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeTask(Context context, String packageName) {
|
||||
Log.e(TAG, "removeTask: " + packageName);
|
||||
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
List<ActivityManager.RecentTaskInfo> list = getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), getCurrentUserId());
|
||||
HashMap<String, Integer> taskMap = new HashMap<>();
|
||||
for (ActivityManager.RecentTaskInfo info : list) {
|
||||
taskMap.put(info.realActivity.getPackageName(), info.id);
|
||||
}
|
||||
try {
|
||||
ActivityManagerNative.getDefault().removeTask(taskMap.get(packageName));
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "removeTask: " + e.getMessage());
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "removeTask: " + e.getMessage());
|
||||
}
|
||||
// } else {
|
||||
// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of the recents tasks.
|
||||
* 获取近期任务列表
|
||||
*/
|
||||
public static List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
|
||||
try {
|
||||
return ActivityTaskManager.getService().getRecentTasks(numTasks,
|
||||
RECENT_IGNORE_UNAVAILABLE, userId).getList();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to get recent tasks " + e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current user's id.
|
||||
* 获取userId
|
||||
*/
|
||||
public static int getCurrentUserId() {
|
||||
UserInfo ui;
|
||||
try {
|
||||
ui = ActivityManager.getService().getCurrentUser();
|
||||
return ui != null ? ui.id : 0;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDefaultLauncher(Context context, Class<?> launcherActivityClass) {
|
||||
ComponentName componentName = new ComponentName(context, launcherActivityClass);
|
||||
Log.e(TAG, "isDefaultLauncher: componentName = " + componentName);
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_HOME);
|
||||
ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
|
||||
if (resolveInfo != null && resolveInfo.activityInfo != null) {
|
||||
ComponentName defaultComponentName = resolveInfo.getComponentInfo().getComponentName();
|
||||
Log.e(TAG, "isDefaultLauncher: defaultComponentName = " + defaultComponentName);
|
||||
return componentName.equals(defaultComponentName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的 Activity 设置为系统默认桌面(需要系统签名)
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param launcherActivityClass 你的桌面 Activity 类,例如 MyLauncherActivity.class
|
||||
*/
|
||||
public static void setDefaultLauncher(Context context, Class<?> launcherActivityClass) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
// 1. 创建桌面过滤的 IntentFilter
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
|
||||
filter.addCategory(Intent.CATEGORY_HOME);
|
||||
filter.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
|
||||
// 2. 查询当前系统中所有的桌面应用(用来构建 ComponentName 数组)
|
||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
|
||||
homeIntent.addCategory(Intent.CATEGORY_HOME);
|
||||
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
|
||||
int bestMatch = 0;
|
||||
ComponentName[] set = new ComponentName[resolveInfos.size()];
|
||||
for (int i = 0; i < resolveInfos.size(); i++) {
|
||||
ResolveInfo info = resolveInfos.get(i);
|
||||
set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
|
||||
if (info.match > bestMatch) {
|
||||
bestMatch = info.match; // 获取最匹配的值
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 构建你自己的桌面 ComponentName
|
||||
ComponentName myLauncher = new ComponentName(context, launcherActivityClass);
|
||||
|
||||
// 4. 替换首选 Activity(核心步骤)
|
||||
try {
|
||||
// 注意:API 29 (Android 10) 中此方法对第三方应用废弃,但对系统签名应用依然有效
|
||||
pm.replacePreferredActivity(filter, bestMatch, set, myLauncher);
|
||||
Log.i("LauncherHelper", "成功设置为默认桌面");
|
||||
|
||||
// 可选:发送一个回到桌面的 Intent 来验证
|
||||
// Intent startHome = new Intent(Intent.ACTION_MAIN);
|
||||
// startHome.addCategory(Intent.CATEGORY_HOME);
|
||||
// startHome.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// context.startActivity(startHome);
|
||||
|
||||
} catch (SecurityException e) {
|
||||
Log.e("LauncherHelper", "缺少权限或未生效,请检查系统签名是否正确", e);
|
||||
} catch (Exception e) {
|
||||
Log.e("LauncherHelper", "设置默认桌面失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDefaultLauncher(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
|
||||
|
||||
// 检查当前应用是否已经是桌面角色持有者
|
||||
if (roleManager != null && !roleManager.isRoleHeld(RoleManager.ROLE_HOME)) {
|
||||
|
||||
// 检查该角色是否可用
|
||||
if (roleManager.isRoleAvailable(RoleManager.ROLE_HOME)) {
|
||||
// 创建请求意图
|
||||
Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME);
|
||||
// 注意:在普通应用中,这会弹出系统选择框
|
||||
// 在系统签名应用中,这通常能直接提升优先级或简化流程
|
||||
context.startActivity(roleRequestIntent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Android 10 以下的传统做法:清除当前默认并弹出选择
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_HOME);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定包名设置为默认桌面 (需要系统签名)
|
||||
*/
|
||||
public static void addRoleHolderAsUser(Context context, String packageName) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
Log.e(TAG, "RoleManager requires Android 10 or higher.");
|
||||
return;
|
||||
}
|
||||
|
||||
RoleManager roleManager = context.getSystemService(RoleManager.class);
|
||||
if (roleManager == null) return;
|
||||
|
||||
String roleName = RoleManager.ROLE_HOME;
|
||||
|
||||
// 1. 检查是否已经是当前角色持有者,避免重复调用
|
||||
if (roleManager.isRoleHeld(roleName)) {
|
||||
// 注意:这里最好再判断一下持有的包名是否为目标包名
|
||||
Log.i(TAG, "Role " + roleName + " is already held by some package.");
|
||||
// 如果已经是自己,直接返回
|
||||
}
|
||||
|
||||
try {
|
||||
UserHandle user = Process.myUserHandle();
|
||||
Executor executor = context.getMainExecutor();
|
||||
|
||||
// 定义回调
|
||||
Consumer<Boolean> callback = successful -> {
|
||||
if (successful) {
|
||||
Log.i(TAG, "Successfully set " + packageName + " as " + roleName);
|
||||
} else {
|
||||
Log.e(TAG, "Failed to set " + packageName + " as " + roleName + ". Check system logs.");
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 使用反射调用隐藏方法 addRoleHolderAsUser
|
||||
// 方法签名: addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer<Boolean>)
|
||||
Method method = RoleManager.class.getMethod("addRoleHolderAsUser",
|
||||
String.class, String.class, int.class, UserHandle.class, Executor.class, Consumer.class);
|
||||
|
||||
Log.d(TAG, "Invoking addRoleHolderAsUser for package: " + packageName);
|
||||
method.invoke(roleManager, roleName, packageName, 0, user, executor, callback);
|
||||
|
||||
// roleManager.addRoleHolderAsUser(roleName, packageName, 0, user, executor, callback);
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(TAG, "Method not found. Is this a non-standard ROM?", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error invoking addRoleHolderAsUser", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setOtherDefaultLauncher(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
|
||||
filter.addCategory(Intent.CATEGORY_HOME);
|
||||
filter.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
|
||||
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
|
||||
homeIntent.addCategory(Intent.CATEGORY_HOME);
|
||||
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
Optional<ResolveInfo> systemLauncher = resolveInfos.stream().filter(new Predicate<ResolveInfo>() {
|
||||
@Override
|
||||
public boolean test(ResolveInfo resolveInfo) {
|
||||
return !BuildConfig.APPLICATION_ID.equals(resolveInfo.activityInfo.packageName);
|
||||
}
|
||||
}).filter(new Predicate<ResolveInfo>() {
|
||||
@Override
|
||||
public boolean test(ResolveInfo resolveInfo) {
|
||||
return ApkUtils.isSystemApp(context, resolveInfo.activityInfo.packageName);
|
||||
}
|
||||
}).findAny();
|
||||
systemLauncher.ifPresent(resolveInfo -> addRoleHolderAsUser(context, resolveInfo.activityInfo.packageName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统签名应用专用:静默获取全屏截图Bitmap
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 全屏截图Bitmap,失败返回null
|
||||
*/
|
||||
public static Bitmap takeFullScreenshot(Context context) {
|
||||
// 获取屏幕真实宽高(包含状态栏、导航栏)
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
wm.getDefaultDisplay().getRealMetrics(metrics);
|
||||
int screenWidth = metrics.widthPixels;
|
||||
int screenHeight = metrics.heightPixels;
|
||||
|
||||
Log.e(TAG, "takeFullScreenshot: screenWidth " + screenWidth);
|
||||
Log.e(TAG, "takeFullScreenshot: screenHeight " + screenHeight);
|
||||
|
||||
try {
|
||||
// 注意:不同 Android 版本的 SurfaceControl.screenshot 方法签名可能不同
|
||||
// 以下代码适用于 Android 9.0 及以上版本常见的隐藏 API 调用方式
|
||||
|
||||
// 1. 获取屏幕显示的 IBinder (通常为 DisplayControl 或 SurfaceControl 的内部方法)
|
||||
// 在较高版本中,可能需要通过 SurfaceControl.getInternalDisplayToken() 获取
|
||||
|
||||
// 2. 反射调用 screenshot 方法
|
||||
Class<?> surfaceControlClass = Class.forName("android.view.SurfaceControl");
|
||||
|
||||
// Android 10+ 建议使用新的反射路径,这里以通用逻辑为例:
|
||||
// 对于 Android 11+,Google 引入了 SurfaceControl.LayerCaptureArgs 等内部类
|
||||
|
||||
// 这是一个针对 Android 9/10 的简化逻辑参考:
|
||||
Bitmap bitmap = (Bitmap) surfaceControlClass.getDeclaredMethod("screenshot",
|
||||
Rect.class, Integer.TYPE, Integer.TYPE, Integer.TYPE)
|
||||
.invoke(null, new Rect(), screenWidth, screenHeight, 0);
|
||||
|
||||
return bitmap;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static Bitmap takeScreenshotHighVersion() {
|
||||
try {
|
||||
// 1. 获取主屏幕的 Token (Internal Display)
|
||||
// 反射调用 SurfaceControl.getInternalDisplayToken()
|
||||
Method getInternalDisplayTokenMethod = SurfaceControl.class.getDeclaredMethod("getInternalDisplayToken");
|
||||
getInternalDisplayTokenMethod.setAccessible(true);
|
||||
IBinder displayToken = (IBinder) getInternalDisplayTokenMethod.invoke(null);
|
||||
|
||||
if (displayToken == null) return null;
|
||||
|
||||
// 2. 构造 DisplayCaptureArgs.Builder (Android 11+ 的新包装类)
|
||||
Class<?> builderClass = Class.forName("android.view.SurfaceControl$DisplayCaptureArgs$Builder");
|
||||
Constructor<?> builderConstructor = builderClass.getConstructor(IBinder.class);
|
||||
Object builder = builderConstructor.newInstance(displayToken);
|
||||
|
||||
// 可以通过 Builder 设置缩放、格式等,这里直接 build()
|
||||
Method buildMethod = builderClass.getDeclaredMethod("build");
|
||||
Object captureArgs = buildMethod.invoke(builder);
|
||||
|
||||
// 3. 调用 SurfaceControl.screenshot(DisplayCaptureArgs)
|
||||
// 返回值是一个 ScreenshotHardwareBuffer 对象
|
||||
Class<?> captureArgsClass = Class.forName("android.view.SurfaceControl$DisplayCaptureArgs");
|
||||
Method screenshotMethod = SurfaceControl.class.getDeclaredMethod("screenshot", captureArgsClass);
|
||||
screenshotMethod.setAccessible(true);
|
||||
|
||||
Object screenshotBuffer = screenshotMethod.invoke(null, captureArgs);
|
||||
|
||||
if (screenshotBuffer == null) return null;
|
||||
|
||||
// 4. 从 ScreenshotHardwareBuffer 中提取 Bitmap
|
||||
Method asBitmapMethod = screenshotBuffer.getClass().getDeclaredMethod("asBitmap");
|
||||
asBitmapMethod.setAccessible(true);
|
||||
return (Bitmap) asBitmapMethod.invoke(screenshotBuffer);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
230
app/src/main/java/com/ttstd/dialer/view/SettingItem.java
Normal file
230
app/src/main/java/com/ttstd/dialer/view/SettingItem.java
Normal file
@@ -0,0 +1,230 @@
|
||||
package com.ttstd.dialer.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SettingItem extends ConstraintLayout {
|
||||
|
||||
private OnClickListener mRootOnClickListener;
|
||||
private ToggleButton.OnToggleChanged mOnToggleChanged;
|
||||
|
||||
private static final String DefaultOptionsText = "设置选项";
|
||||
private static final String DefaultEnableText = "开启描述";
|
||||
private static final String DefaultDisableText = "关闭描述";
|
||||
|
||||
private String mOptionsText = "";
|
||||
private String mEnableText = "";
|
||||
private String mDisableText = "";
|
||||
|
||||
private int mOptionsTextColor = 0xFF000000;
|
||||
private int mHintTextColor = 0xFF9D9D9D;
|
||||
// private boolean mRootClick = false;
|
||||
private boolean mShowHintText = true;
|
||||
private boolean mShowToggle = true;
|
||||
private boolean mShowMore = false;
|
||||
private boolean mLinkage = true;
|
||||
private boolean mShowDivider = true;
|
||||
|
||||
private ConstraintLayout cl_root;
|
||||
private TextView tv_options, tv_hint;
|
||||
private ToggleButton tb;
|
||||
private ImageView iv_more;
|
||||
private View dividerLine;
|
||||
|
||||
public void setRootOnClickListener(OnClickListener rootOnClickListener) {
|
||||
mRootOnClickListener = rootOnClickListener;
|
||||
}
|
||||
|
||||
public void setOnToggleChanged(ToggleButton.OnToggleChanged onToggleChanged) {
|
||||
mOnToggleChanged = onToggleChanged;
|
||||
tb.setOnToggleChanged(mOnToggleChanged);
|
||||
}
|
||||
|
||||
public void setToggleStatu(boolean on) {
|
||||
tb.setToggleStatu(on);
|
||||
if (on) {
|
||||
if (!TextUtils.isEmpty(mEnableText)) {
|
||||
tv_hint.setText(mEnableText);
|
||||
} else {
|
||||
tv_hint.setText(DefaultEnableText);
|
||||
}
|
||||
} else {
|
||||
if (!TextUtils.isEmpty(mDisableText)) {
|
||||
tv_hint.setText(mDisableText);
|
||||
} else {
|
||||
tv_hint.setText(DefaultDisableText);
|
||||
}
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public SettingItem(@NonNull @NotNull Context context) {
|
||||
super(context);
|
||||
init(context, null);
|
||||
}
|
||||
|
||||
public SettingItem(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public SettingItem(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public SettingItem(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
inflater.inflate(R.layout.layout_setting_content_item, this, true);
|
||||
|
||||
cl_root = findViewById(R.id.cl_root);
|
||||
tv_options = findViewById(R.id.tv_options);
|
||||
tv_hint = findViewById(R.id.tv_hint);
|
||||
tb = findViewById(R.id.tb);
|
||||
iv_more = findViewById(R.id.iv_more);
|
||||
dividerLine = findViewById(R.id.divider);
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SettingItem);
|
||||
try {
|
||||
mOptionsText = typedArray.getString(R.styleable.SettingItem_optionsText);
|
||||
if (!TextUtils.isEmpty(mOptionsText)) {
|
||||
tv_options.setText(mOptionsText);
|
||||
} else {
|
||||
tv_options.setText(DefaultOptionsText);
|
||||
}
|
||||
int optionsTextColor = typedArray.getColor(R.styleable.SettingItem_optionsTextColor, mOptionsTextColor);
|
||||
tv_options.setTextColor(optionsTextColor);
|
||||
|
||||
|
||||
mEnableText = typedArray.getString(R.styleable.SettingItem_enableText);
|
||||
mDisableText = typedArray.getString(R.styleable.SettingItem_disableText);
|
||||
|
||||
int enableTextColor = typedArray.getColor(R.styleable.SettingItem_enableTextColor, mHintTextColor);
|
||||
tv_hint.setTextColor(enableTextColor);
|
||||
|
||||
// boolean rootClick = typedArray.getBoolean(R.styleable.SettingItem_rootClick, mRootClick);
|
||||
// if (rootClick) {
|
||||
// if (mRootOnClickListener != null) {
|
||||
// cl_root.setOnClickListener(mRootOnClickListener);
|
||||
// }
|
||||
// }
|
||||
mShowHintText = typedArray.getBoolean(R.styleable.SettingItem_showHintText, mShowHintText);
|
||||
mShowToggle = typedArray.getBoolean(R.styleable.SettingItem_showToggle, mShowToggle);
|
||||
mShowMore = typedArray.getBoolean(R.styleable.SettingItem_showMore, mShowMore);
|
||||
mLinkage = typedArray.getBoolean(R.styleable.SettingItem_linkage, mLinkage);
|
||||
mShowDivider = typedArray.getBoolean(R.styleable.SettingItem_dividerLine, mShowDivider);
|
||||
} finally {
|
||||
typedArray.recycle();
|
||||
}
|
||||
} else {
|
||||
tv_options.setText(mOptionsText);
|
||||
tv_hint.setText(mEnableText);
|
||||
tv_options.setTextColor(mOptionsTextColor);
|
||||
tv_hint.setTextColor(mHintTextColor);
|
||||
}
|
||||
if (mShowHintText) {
|
||||
tv_hint.setVisibility(VISIBLE);
|
||||
} else {
|
||||
tv_hint.setVisibility(GONE);
|
||||
}
|
||||
if (mShowToggle) {
|
||||
tb.setVisibility(VISIBLE);
|
||||
} else {
|
||||
tb.setVisibility(GONE);
|
||||
}
|
||||
if (mShowMore) {
|
||||
iv_more.setVisibility(VISIBLE);
|
||||
} else {
|
||||
iv_more.setVisibility(GONE);
|
||||
}
|
||||
if (mShowDivider) {
|
||||
dividerLine.setVisibility(VISIBLE);
|
||||
} else {
|
||||
dividerLine.setVisibility(GONE);
|
||||
}
|
||||
|
||||
if (tb.isToggleOn()) {
|
||||
if (!TextUtils.isEmpty(mEnableText)) {
|
||||
tv_hint.setText(mEnableText);
|
||||
} else {
|
||||
tv_hint.setText(DefaultEnableText);
|
||||
}
|
||||
} else {
|
||||
if (!TextUtils.isEmpty(mDisableText)) {
|
||||
tv_hint.setText(mDisableText);
|
||||
} else {
|
||||
tv_hint.setText(DefaultDisableText);
|
||||
}
|
||||
}
|
||||
tb.setOnToggleInsideChanged(new ToggleButton.OnToggleInsideChanged() {
|
||||
@Override
|
||||
public void onToggle(boolean on) {
|
||||
if (on) {
|
||||
if (!TextUtils.isEmpty(mEnableText)) {
|
||||
tv_hint.setText(mEnableText);
|
||||
} else {
|
||||
tv_hint.setText(DefaultEnableText);
|
||||
}
|
||||
} else {
|
||||
if (!TextUtils.isEmpty(mDisableText)) {
|
||||
tv_hint.setText(mDisableText);
|
||||
} else {
|
||||
tv_hint.setText(DefaultDisableText);
|
||||
}
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
if (mRootOnClickListener != null) {
|
||||
cl_root.setOnClickListener(mRootOnClickListener);
|
||||
}
|
||||
if (mLinkage) {
|
||||
cl_root.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (tb.isToggleOn()) {
|
||||
tb.toggleOff();
|
||||
} else {
|
||||
tb.toggleOn();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
389
app/src/main/java/com/ttstd/dialer/view/ToggleButton.java
Normal file
389
app/src/main/java/com/ttstd/dialer/view/ToggleButton.java
Normal file
@@ -0,0 +1,389 @@
|
||||
package com.ttstd.dialer.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.rebound.SimpleSpringListener;
|
||||
import com.facebook.rebound.Spring;
|
||||
import com.facebook.rebound.SpringConfig;
|
||||
import com.facebook.rebound.SpringSystem;
|
||||
import com.facebook.rebound.SpringUtil;
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
public class ToggleButton extends View {
|
||||
private static final String TAG = "ToggleButton";
|
||||
private SpringSystem springSystem;
|
||||
private Spring spring;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private float radius;
|
||||
/**
|
||||
* 开启颜色
|
||||
*/
|
||||
private int onColor = Color.parseColor("#246FFE");
|
||||
/**
|
||||
* 关闭颜色
|
||||
*/
|
||||
private int offBorderColor = Color.parseColor("#c7c7c7");
|
||||
/**
|
||||
* 灰色带颜色
|
||||
*/
|
||||
private int offColor = Color.parseColor("#ffffff");
|
||||
/**
|
||||
* 手柄颜色
|
||||
*/
|
||||
private int spotColor = Color.parseColor("#ffffff");
|
||||
/**
|
||||
* 边框颜色
|
||||
*/
|
||||
private int borderColor = offBorderColor;
|
||||
/**
|
||||
* 画笔
|
||||
*/
|
||||
private Paint paint;
|
||||
/**
|
||||
* 开关状态
|
||||
*/
|
||||
private boolean toggleOn = false;
|
||||
/**
|
||||
* 边框大小
|
||||
*/
|
||||
private int borderWidth = 2;
|
||||
/**
|
||||
* 垂直中心
|
||||
*/
|
||||
private float centerY;
|
||||
/**
|
||||
* 按钮的开始和结束位置
|
||||
*/
|
||||
private float startX, endX;
|
||||
/**
|
||||
* 手柄X位置的最小和最大值
|
||||
*/
|
||||
private float spotMinX, spotMaxX;
|
||||
/**
|
||||
* 手柄大小
|
||||
*/
|
||||
private int spotSize;
|
||||
/**
|
||||
* 手柄X位置
|
||||
*/
|
||||
private float spotX;
|
||||
/**
|
||||
* 关闭时内部灰色带高度
|
||||
*/
|
||||
private float offLineWidth;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private RectF rect = new RectF();
|
||||
/**
|
||||
* 默认使用动画
|
||||
*/
|
||||
private boolean defaultAnimate = true;
|
||||
/**
|
||||
* 是否默认处于打开状态
|
||||
*/
|
||||
private boolean isDefaultOn = false;
|
||||
/**
|
||||
* 禁止点击
|
||||
*/
|
||||
private boolean disable = false;
|
||||
|
||||
private OnToggleChanged listener;
|
||||
|
||||
public void setDisable(boolean dis) {
|
||||
this.disable = dis;
|
||||
}
|
||||
|
||||
private ToggleButton(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setup(attrs);
|
||||
}
|
||||
|
||||
public ToggleButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setup(attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
spring.removeListener(springListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
spring.addListener(springListener);
|
||||
}
|
||||
|
||||
public void setup(AttributeSet attrs) {
|
||||
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
springSystem = SpringSystem.create();
|
||||
spring = springSystem.createSpring();
|
||||
//张力(tension),摩擦力(friction)
|
||||
//增大张力会使弹簧更快地向目标值运动,减小摩擦力会减少弹簧运动过程中的阻力,从而使回弹更加迅速和有力。
|
||||
spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(80, 10));
|
||||
this.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
if (disable) {
|
||||
|
||||
} else {
|
||||
toggle(defaultAnimate);
|
||||
}
|
||||
}
|
||||
});
|
||||
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ToggleButton);
|
||||
offBorderColor = typedArray.getColor(R.styleable.ToggleButton_tbOffBorderColor, offBorderColor);
|
||||
onColor = typedArray.getColor(R.styleable.ToggleButton_tbOnColor, onColor);
|
||||
spotColor = typedArray.getColor(R.styleable.ToggleButton_tbSpotColor, spotColor);
|
||||
offColor = typedArray.getColor(R.styleable.ToggleButton_tbOffColor, offColor);
|
||||
borderWidth = typedArray.getDimensionPixelSize(R.styleable.ToggleButton_tbBorderWidth, borderWidth);
|
||||
defaultAnimate = typedArray.getBoolean(R.styleable.ToggleButton_tbAnimate, defaultAnimate);
|
||||
isDefaultOn = typedArray.getBoolean(R.styleable.ToggleButton_tbAsDefaultOn, isDefaultOn);
|
||||
typedArray.recycle();
|
||||
borderColor = offBorderColor;
|
||||
if (isDefaultOn) {
|
||||
toggleOn();
|
||||
}
|
||||
}
|
||||
|
||||
public void toggle() {
|
||||
toggle(true);
|
||||
}
|
||||
|
||||
public void toggle(boolean animate) {
|
||||
toggleOn = !toggleOn;
|
||||
Log.e(TAG, "toggle: toggleOn = " + toggleOn);
|
||||
takeEffect(animate);
|
||||
if (listener != null) {
|
||||
listener.onToggle(toggleOn);
|
||||
}
|
||||
if (mOnToggleInsideChanged != null) {
|
||||
mOnToggleInsideChanged.onToggle(toggleOn);
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleOn() {
|
||||
setToggleOn();
|
||||
if (listener != null) {
|
||||
listener.onToggle(toggleOn);
|
||||
}
|
||||
if (mOnToggleInsideChanged != null) {
|
||||
mOnToggleInsideChanged.onToggle(toggleOn);
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleOff() {
|
||||
setToggleOff();
|
||||
if (listener != null) {
|
||||
listener.onToggle(toggleOn);
|
||||
}
|
||||
if (mOnToggleInsideChanged != null) {
|
||||
mOnToggleInsideChanged.onToggle(toggleOn);
|
||||
}
|
||||
}
|
||||
|
||||
public void setToggleStatu(boolean on) {
|
||||
if (on) {
|
||||
setToggleOn();
|
||||
} else {
|
||||
setToggleOff();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置显示成打开样式,不会触发toggle事件
|
||||
*/
|
||||
public void setToggleOn() {
|
||||
setToggleOn(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param animate asd
|
||||
*/
|
||||
public void setToggleOn(boolean animate) {
|
||||
toggleOn = true;
|
||||
takeEffect(animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置显示成关闭样式,不会触发toggle事件
|
||||
*/
|
||||
public void setToggleOff() {
|
||||
setToggleOff(true);
|
||||
}
|
||||
|
||||
public void setToggleOff(boolean animate) {
|
||||
toggleOn = false;
|
||||
takeEffect(animate);
|
||||
}
|
||||
|
||||
|
||||
public int getToggleOnStatu() {
|
||||
Log.e(TAG, "getToggleOnStatu: " + toggleOn);
|
||||
return toggleOn ? 1 : 0;
|
||||
}
|
||||
|
||||
public boolean isToggleOn() {
|
||||
Log.e(TAG, "isToggleOn: " + toggleOn);
|
||||
return toggleOn;
|
||||
}
|
||||
|
||||
private void takeEffect(boolean animate) {
|
||||
if (animate) {
|
||||
spring.setEndValue(toggleOn ? 1 : 0);
|
||||
} else {
|
||||
//这里没有调用spring,所以spring里的当前值没有变更,这里要设置一下,同步两边的当前值
|
||||
spring.setCurrentValue(toggleOn ? 1 : 0);
|
||||
calculateEffect(toggleOn ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
Resources r = Resources.getSystem();
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST) {
|
||||
widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, r.getDisplayMetrics());
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
|
||||
}
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED || heightSize == MeasureSpec.AT_MOST) {
|
||||
heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right,
|
||||
int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
radius = Math.min(width, height) * 0.5f;
|
||||
centerY = radius;
|
||||
startX = radius;
|
||||
endX = width - radius;
|
||||
spotMinX = startX + borderWidth;
|
||||
spotMaxX = endX - borderWidth;
|
||||
spotSize = height - 4 * borderWidth;
|
||||
spotX = toggleOn ? spotMaxX : spotMinX;
|
||||
offLineWidth = 0;
|
||||
}
|
||||
|
||||
SimpleSpringListener springListener = new SimpleSpringListener() {
|
||||
@Override
|
||||
public void onSpringUpdate(Spring spring) {
|
||||
final double value = spring.getCurrentValue();
|
||||
calculateEffect(value);
|
||||
}
|
||||
};
|
||||
|
||||
private int clamp(int value, int low, int high) {
|
||||
return Math.min(Math.max(value, low), high);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
//
|
||||
super.draw(canvas);
|
||||
rect.set(0, 0, getWidth(), getHeight());
|
||||
paint.setColor(borderColor);
|
||||
canvas.drawRoundRect(rect, radius, radius, paint);
|
||||
if (offLineWidth > 0) {
|
||||
final float cy = offLineWidth * 0.5f;
|
||||
rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy);
|
||||
// paint.setColor(offColor);
|
||||
canvas.drawRoundRect(rect, cy, cy, paint);
|
||||
}
|
||||
rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius);
|
||||
paint.setColor(borderColor);
|
||||
canvas.drawRoundRect(rect, radius, radius, paint);
|
||||
final float spotR = spotSize * 0.5f;
|
||||
rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR);
|
||||
paint.setColor(spotColor);
|
||||
canvas.drawRoundRect(rect, spotR, spotR, paint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value
|
||||
*/
|
||||
private void calculateEffect(final double value) {
|
||||
final float mapToggleX = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX);
|
||||
spotX = mapToggleX;
|
||||
float mapOffLineWidth = (float) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize);
|
||||
offLineWidth = mapOffLineWidth;
|
||||
final int fb = Color.blue(onColor);
|
||||
final int fr = Color.red(onColor);
|
||||
final int fg = Color.green(onColor);
|
||||
final int tb = Color.blue(offBorderColor);
|
||||
final int tr = Color.red(offBorderColor);
|
||||
final int tg = Color.green(offBorderColor);
|
||||
int sb = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fb, tb);
|
||||
int sr = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fr, tr);
|
||||
int sg = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fg, tg);
|
||||
sb = clamp(sb, 0, 255);
|
||||
sr = clamp(sr, 0, 255);
|
||||
sg = clamp(sg, 0, 255);
|
||||
borderColor = Color.rgb(sr, sg, sb);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author ThinkPad
|
||||
*/
|
||||
public interface OnToggleChanged {
|
||||
/**
|
||||
* @param on = =
|
||||
*/
|
||||
public void onToggle(boolean on);
|
||||
}
|
||||
|
||||
public interface OnToggleInsideChanged {
|
||||
/**
|
||||
* @param on = =
|
||||
*/
|
||||
void onToggle(boolean on);
|
||||
}
|
||||
|
||||
private OnToggleInsideChanged mOnToggleInsideChanged;
|
||||
|
||||
public void setOnToggleInsideChanged(OnToggleInsideChanged onToggleChanged) {
|
||||
mOnToggleInsideChanged = onToggleChanged;
|
||||
}
|
||||
|
||||
public void setOnToggleChanged(OnToggleChanged onToggleChanged) {
|
||||
listener = onToggleChanged;
|
||||
}
|
||||
|
||||
public boolean isAnimate() {
|
||||
return defaultAnimate;
|
||||
}
|
||||
|
||||
public void setAnimate(boolean animate) {
|
||||
this.defaultAnimate = animate;
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/res/drawable-hdpi/icon_more.png
Normal file
BIN
app/src/main/res/drawable-hdpi/icon_more.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 574 B |
Binary file not shown.
|
Before Width: | Height: | Size: 7.1 KiB |
6
app/src/main/res/drawable/call_phone_selector.xml
Normal file
6
app/src/main/res/drawable/call_phone_selector.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/wechat_call_video" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/wechat_call_video" android:state_focused="true" />
|
||||
<item android:drawable="@drawable/wechat_call_normal" />
|
||||
</selector>
|
||||
18
app/src/main/res/drawable/contact_edit_text_focused.xml
Normal file
18
app/src/main/res/drawable/contact_edit_text_focused.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 内部颜色 -->
|
||||
<solid android:color="@color/white" />
|
||||
|
||||
<stroke
|
||||
android:width="@dimen/dp_1"
|
||||
android:color="@color/colorAccent" />
|
||||
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="@dimen/dp_12" />
|
||||
|
||||
<padding
|
||||
android:bottom="@dimen/dp_8"
|
||||
android:left="@dimen/dp_8"
|
||||
android:right="@dimen/dp_8"
|
||||
android:top="@dimen/dp_8" />
|
||||
</shape>
|
||||
14
app/src/main/res/drawable/contact_edit_text_normal.xml
Normal file
14
app/src/main/res/drawable/contact_edit_text_normal.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 内部颜色 -->
|
||||
<solid android:color="@color/white" />
|
||||
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="@dimen/dp_12" />
|
||||
|
||||
<padding
|
||||
android:bottom="@dimen/dp_8"
|
||||
android:left="@dimen/dp_8"
|
||||
android:right="@dimen/dp_8"
|
||||
android:top="@dimen/dp_8" />
|
||||
</shape>
|
||||
18
app/src/main/res/drawable/contact_edit_text_pressed.xml
Normal file
18
app/src/main/res/drawable/contact_edit_text_pressed.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 内部颜色 -->
|
||||
<solid android:color="@color/white" />
|
||||
|
||||
<stroke
|
||||
android:width="@dimen/dp_1"
|
||||
android:color="@color/colorPrimary" />
|
||||
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="@dimen/dp_12" />
|
||||
|
||||
<padding
|
||||
android:bottom="@dimen/dp_8"
|
||||
android:left="@dimen/dp_8"
|
||||
android:right="@dimen/dp_8"
|
||||
android:top="@dimen/dp_8" />
|
||||
</shape>
|
||||
6
app/src/main/res/drawable/contact_edit_text_selector.xml
Normal file
6
app/src/main/res/drawable/contact_edit_text_selector.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/contact_edit_text_pressed" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/contact_edit_text_focused" android:state_focused="true" />
|
||||
<item android:drawable="@drawable/contact_edit_text_normal" />
|
||||
</selector>
|
||||
@@ -3,11 +3,11 @@
|
||||
<!-- 内部颜色 -->
|
||||
<solid android:color="@color/lightGray" />
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="32dp" />
|
||||
<corners android:radius="@dimen/dp_32" />
|
||||
|
||||
<padding
|
||||
android:bottom="4dp"
|
||||
android:left="20dp"
|
||||
android:right="20dp"
|
||||
android:top="4dp" />
|
||||
android:bottom="@dimen/dp_4"
|
||||
android:left="@dimen/dp_20"
|
||||
android:right="@dimen/dp_20"
|
||||
android:top="@dimen/dp_4" />
|
||||
</shape>
|
||||
@@ -3,11 +3,11 @@
|
||||
<!-- 内部颜色 -->
|
||||
<solid android:color="#5591F3" />
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="32dp" />
|
||||
<corners android:radius="@dimen/dp_32" />
|
||||
|
||||
<padding
|
||||
android:bottom="4dp"
|
||||
android:left="20dp"
|
||||
android:right="20dp"
|
||||
android:top="4dp" />
|
||||
android:bottom="@dimen/dp_4"
|
||||
android:left="@dimen/dp_20"
|
||||
android:right="@dimen/dp_20"
|
||||
android:top="@dimen/dp_4" />
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/ic_cancel.xml
Normal file
9
app/src/main/res/drawable/ic_cancel.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M801.63,222.37a30.72,30.72 0,0 1,0 43.44L555.44,512l246.19,246.21a30.72,30.72 0,1 1,-43.44 43.44L512,555.44 265.83,801.63a30.72,30.72 0,1 1,-43.46 -43.44L468.54,512l-246.17,-246.17a30.72,30.72 0,1 1,43.44 -43.46L512,468.54l246.21,-246.17a30.72,30.72 0,0 1,43.44 0z"
|
||||
android:fillColor="#131415"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_confirm.xml
Normal file
9
app/src/main/res/drawable/ic_confirm.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M149.95,471.78a30.72,30.72 0,0 1,41.27 -2.01l2.19,2.01 224.46,224.46 412.71,-412.73a30.72,30.72 0,0 1,41.27 -1.99l2.19,1.99a30.72,30.72 0,0 1,1.99 41.25l-1.99,2.19 -434.46,434.46a30.72,30.72 0,0 1,-41.25 1.99l-2.19,-1.99 -246.17,-246.19a30.72,30.72 0,0 1,0 -43.44z"
|
||||
android:fillColor="#131415"/>
|
||||
</vector>
|
||||
@@ -4,9 +4,6 @@
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M0,0m91.02,0l841.96,0q91.02,0 91.02,91.02l0,841.96q0,91.02 -91.02,91.02l-841.96,0q-91.02,0 -91.02,-91.02l0,-841.96q0,-91.02 91.02,-91.02Z"
|
||||
android:fillColor="#C5C5C5"/>
|
||||
<path
|
||||
android:pathData="M794.83,824.43c-60.46,-27.12 -37.16,-6.35 -111.37,-34.16 -74.18,-27.67 -91.75,-36.75 -91.75,-36.75l4.3,-48.51s68.12,-21.17 80.9,-104.25c25.62,6.28 34.29,-25.28 35.66,-45.51 1.58,-19.54 15.18,-80.33 -16.19,-74.93 6.43,-40.58 11.49,-77.28 9.16,-96.75 -7.79,-68.19 -63.67,-139.38 -204.62,-144.63 -31.79,1.17 -63.15,7.76 -92.71,19.47 -10.39,4.1 -77.89,-4.3 -86.97,14.14 -4.93,10.05 10.24,40.52 2.05,47.08 -33,26.51 -24.12,31.22 -27.88,64.01 -2.25,19.47 2.32,56.09 8.74,96.81 -31.29,-5.46 -17.75,55.33 -16.38,74.87 1.42,20.23 9.9,51.93 35.59,45.65C336.13,684.04 419.41,705.42 419.41,705.42l4.31,48.72s-17.57,9.69 -91.76,37.43c-74.18,27.74 -50.89,5.73 -111.35,32.79C125.16,867.41 125.16,985.33 125.16,985.33c0,17.91 16.05,32.46 35.86,32.46H854.47c19.82,0 35.87,-14.56 35.87,-32.39v-0.07s0,-117.92 -95.52,-160.82v-0.07z"
|
||||
android:fillColor="#E5E5E5"/>
|
||||
android:pathData="M512,64C264.8,64 64,264.8 64,512s200.8,448 448,448 448,-200.8 448,-448S759.2,64 512,64zM384.8,376c4,-64 56,-115.2 120,-119.2 74.4,-4 135.2,55.2 135.2,128 0,70.4 -57.6,128 -128,128 -73.6,0 -132,-62.4 -127.2,-136.8zM768,746.4c0,12 -9.6,21.6 -21.6,21.6H278.4c-12,0 -21.6,-9.6 -21.6,-21.6v-64c0,-84.8 170.4,-128 255.2,-128 84.8,0 255.2,42.4 255.2,128l0.8,64z"
|
||||
android:fillColor="#cdcdcd"/>
|
||||
</vector>
|
||||
|
||||
12
app/src/main/res/drawable/ic_dialer.xml
Normal file
12
app/src/main/res/drawable/ic_dialer.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M877.7,0H146.3C65.47,0 -0.01,65.54 -0.01,146.3v731.46c0,80.76 65.48,146.24 146.3,146.24H877.7c80.82,0 146.3,-65.48 146.3,-146.24V146.3C1024,65.54 958.52,0 877.7,0z"
|
||||
android:fillColor="#C3B898"/>
|
||||
<path
|
||||
android:pathData="M831.51,774.63s-54.05,106.01 -103.32,108.61c-228.33,-16.95 -564.75,-441.07 -533.7,-644.66 8.41,-49.28 87.99,-103.75 138.26,-97.28 46.4,25.58 138.45,164.55 138.45,232.1l-44.99,39.51c8.79,86.38 105.69,199.15 186.6,231.11 0,0 45.08,-34.93 63.3,-37.53 38.33,3.49 155.39,128.06 155.39,168.14z"
|
||||
android:fillColor="#e6e6e6"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_home_float_window.xml
Normal file
9
app/src/main/res/drawable/ic_home_float_window.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M570.03,237.23c-32.43,-32.43 -83.63,-32.43 -116.05,0L213.33,477.87c-11.95,11.95 -15.36,29.01 -8.53,44.37 6.83,15.36 20.48,25.6 37.55,25.6l8.53,0 0,179.2c0,44.37 37.55,81.92 81.92,81.92l356.69,0c44.37,0 81.92,-37.55 81.92,-81.92L771.41,547.84l8.53,0c17.07,0 30.72,-10.24 37.55,-25.6 6.83,-15.36 3.41,-32.43 -8.53,-44.37L570.03,237.23zM774.83,505.17c0,1.71 -1.71,1.71 -3.41,1.71 -22.19,0 -39.25,18.77 -39.25,40.96l0,0 0,179.2c0,22.19 -18.77,40.96 -40.96,40.96L334.51,768c-22.19,0 -40.96,-18.77 -40.96,-40.96L293.55,547.84l0,0c0,-22.19 -17.07,-40.96 -39.25,-40.96 -1.71,0 -3.41,-1.71 -3.41,-1.71 0,-1.71 0,-3.41 1.71,-5.12l232.11,-232.11c15.36,-15.36 42.67,-15.36 58.03,0l232.11,232.11C774.83,501.76 774.83,503.47 774.83,505.17zM512,0C228.69,0 0,228.69 0,512c0,283.31 228.69,512 512,512s512,-228.69 512,-512C1024,228.69 795.31,0 512,0zM512,983.04C252.59,983.04 40.96,771.41 40.96,512 40.96,252.59 252.59,40.96 512,40.96c259.41,0 471.04,209.92 471.04,471.04C983.04,771.41 771.41,983.04 512,983.04z"
|
||||
android:fillColor="#272636"/>
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/ic_not_applicable.xml
Normal file
15
app/src/main/res/drawable/ic_not_applicable.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.1,5.1h1.5l2.1,3.8 0.7,1.5h0c0,-0.7 -0.2,-1.7 -0.2,-2.5v-2.8h1.3v7.1h-1.4l-2.1,-3.8 -0.7,-1.5h0c0,0.8 0.2,1.7 0.2,2.5v2.9H0.1v-7.1Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M8.7,5.1h0.8l-2.1,7.1h-0.8l2.1,-7.1Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14,10.4h-2.3l-0.5,1.8h-1.4l2.3,-7.1h1.7l2.3,7.1h-1.5l-0.5,-1.8ZM13.7,9.3l-0.2,-0.8c-0.2,-0.7 -0.4,-1.6 -0.6,-2.3h0c-0.2,0.8 -0.4,1.6 -0.6,2.3l-0.2,0.8h1.7Z"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_return.xml
Normal file
12
app/src/main/res/drawable/ic_return.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M231.8,478.6c-16,16 -16,41.9 0,57.9l388.6,388.6c16,16 41.9,16 57.9,0s16,-41.9 0,-57.9L289.7,478.6c-16,-16 -41.9,-16 -57.9,0z"
|
||||
android:fillColor="#242424"/>
|
||||
<path
|
||||
android:pathData="M231.8,537.9c16,16 41.9,16 57.9,0l388.6,-388.6c16,-16 16,-41.9 0,-57.9s-41.9,-16 -57.9,0L231.8,480c-16,16 -16,41.9 0,57.9z"
|
||||
android:fillColor="#242424"/>
|
||||
</vector>
|
||||
25
app/src/main/res/drawable/settings_card_bg.xml
Normal file
25
app/src/main/res/drawable/settings_card_bg.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<!-- 填充的颜色:这里设置背景透明 -->
|
||||
<solid android:color="#FFFFFF" />
|
||||
<!-- 边框的颜色 :不能和窗口背景色一样 -->
|
||||
|
||||
<!-- 设置按钮的四个角为弧形 -->
|
||||
<!-- android:radius 弧形的半径 -->
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/dp_10"
|
||||
android:bottomRightRadius="@dimen/dp_10"
|
||||
android:topLeftRadius="@dimen/dp_10"
|
||||
android:topRightRadius="@dimen/dp_10" />
|
||||
|
||||
|
||||
<!-- padding:Button里面的文字与Button边界的间隔 -->
|
||||
<!-- <padding-->
|
||||
<!-- android:bottom="@dimen/dp_10"-->
|
||||
<!-- android:left="@dimen/dp_10"-->
|
||||
<!-- android:right="@dimen/dp_10"-->
|
||||
<!-- android:top="@dimen/dp_10" />-->
|
||||
|
||||
</shape>
|
||||
13
app/src/main/res/drawable/weather_card_background.xml
Normal file
13
app/src/main/res/drawable/weather_card_background.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 内部颜色 -->
|
||||
<solid android:color="#1A000000" />
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="@dimen/dp_16" />
|
||||
|
||||
<padding
|
||||
android:bottom="@dimen/dp_4"
|
||||
android:left="@dimen/dp_4"
|
||||
android:right="@dimen/dp_4"
|
||||
android:top="@dimen/dp_4" />
|
||||
</shape>
|
||||
14
app/src/main/res/drawable/wechat_call_normal.xml
Normal file
14
app/src/main/res/drawable/wechat_call_normal.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="@dimen/default_radius" />
|
||||
|
||||
<solid android:color="#BABABA" />
|
||||
|
||||
<!-- <gradient-->
|
||||
<!-- android:angle="270"-->
|
||||
<!-- android:endColor="#E6418B24"-->
|
||||
<!-- android:startColor="#E612CA56"-->
|
||||
<!-- android:type="linear" />-->
|
||||
</shape>
|
||||
@@ -4,7 +4,7 @@
|
||||
<!-- 圆角的幅度 -->
|
||||
<corners android:radius="@dimen/default_radius" />
|
||||
|
||||
<solid android:color="#17D26B" />
|
||||
<solid android:color="#3ABF11" />
|
||||
|
||||
<!-- <gradient-->
|
||||
<!-- android:angle="270"-->
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
tools:context=".activity.app.AppListActivity">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.dialer.activity.app.AppListActivity.BtnClick" />
|
||||
@@ -14,10 +15,46 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_return"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="应用列表"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -15,27 +15,77 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="新建联系人"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:src="@drawable/ic_confirm"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="@dimen/dp_8"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/tv_save"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar">
|
||||
|
||||
<com.shehuan.niv.NiceImageView
|
||||
android:id="@+id/nv_avatar"
|
||||
android:layout_width="@dimen/dp_100"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_32"
|
||||
android:src="@drawable/ic_default_avatar"
|
||||
app:is_circle="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48">
|
||||
android:layout_height="@dimen/dp_64"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<TextView
|
||||
<com.shehuan.niv.NiceImageView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="姓名"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_18"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@@ -43,12 +93,13 @@
|
||||
<EditText
|
||||
android:id="@+id/et_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/contact_edit_text_selector"
|
||||
android:hint="姓名"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/tv_name"
|
||||
@@ -58,18 +109,13 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48">
|
||||
android:layout_height="@dimen/dp_64"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<TextView
|
||||
<ImageView
|
||||
android:id="@+id/tv_phone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="电话"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_18"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@@ -77,13 +123,14 @@
|
||||
<EditText
|
||||
android:id="@+id/et_phone"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/contact_edit_text_selector"
|
||||
android:hint="电话"
|
||||
android:inputType="phone"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/tv_phone"
|
||||
@@ -91,23 +138,52 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_64"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/tv_remark"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_remark"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/contact_edit_text_selector"
|
||||
android:hint="备注"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/tv_remark"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_save"
|
||||
android:layout_width="wrap_content"
|
||||
android:onClick="@{click::save}"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/dp_32"
|
||||
android:maxLines="1"
|
||||
android:onClick="@{click::save}"
|
||||
android:singleLine="true"
|
||||
android:text="保存"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_18"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
/>
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".activity.contact.edit.ContactEditActivity">
|
||||
|
||||
<data>
|
||||
@@ -13,5 +14,47 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="编辑联系人"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:src="@drawable/ic_confirm"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -11,27 +11,69 @@
|
||||
type="com.ttstd.dialer.activity.contact.list.ContactListActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:layout_width="@dimen/dp_64"
|
||||
android:layout_height="@dimen/dp_64"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="@dimen/dp_32"
|
||||
android:layout_marginBottom="@dimen/dp_32"
|
||||
android:onClick="@{click::addContact}"
|
||||
android:src="@drawable/ic_add"
|
||||
app:fabCustomSize="@dimen/dp_64"
|
||||
app:layout_behavior="com.ttstd.dialer.view.ScrollAwareFABBehavior" />
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_return"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="联系人列表"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:layout_width="@dimen/dp_64"
|
||||
android:layout_height="@dimen/dp_64"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="@dimen/dp_32"
|
||||
android:layout_marginBottom="@dimen/dp_32"
|
||||
android:onClick="@{click::addContact}"
|
||||
android:src="@drawable/ic_add"
|
||||
app:fabCustomSize="@dimen/dp_64"
|
||||
app:layout_behavior="com.ttstd.dialer.view.ScrollAwareFABBehavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
172
app/src/main/res/layout/activity_settings.xml
Normal file
172
app/src/main/res/layout/activity_settings.xml
Normal file
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".activity.settings.home.SettingsActivity">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.dialer.activity.settings.home.SettingsActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="桌面设置"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:src="@drawable/ic_confirm"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/settings_card_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="@dimen/dp_100"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:onClick="@{click::openCallSettings}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="拨号设置"
|
||||
android:textColor="@color/black"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="@dimen/dp_100"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:onClick="@{click::openUtilsSettings}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="功能设置"
|
||||
android:textColor="@color/black"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="@dimen/dp_100"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
131
app/src/main/res/layout/activity_settings_call.xml
Normal file
131
app/src/main/res/layout/activity_settings_call.xml
Normal file
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".activity.settings.call.SettingsCallActivity">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.dialer.activity.settings.call.SettingsCallActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="拨号设置"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:src="@drawable/ic_confirm"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/settings_card_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_fast_call_phone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_fast_call_phone"
|
||||
app:enableText="@string/enable_text_fast_call_phone"
|
||||
app:optionsText="@string/options_text_fast_call_phone" />
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_voice_broadcast"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_voice_broadcast"
|
||||
app:enableText="@string/enable_text_voice_broadcast"
|
||||
app:optionsText="@string/options_text_voice_broadcast" />
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_auto_accept"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_auto_accept"
|
||||
app:enableText="@string/enable_text_auto_accept"
|
||||
app:optionsText="@string/options_text_auto_accept" />
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_hands_free"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_hands_free"
|
||||
app:enableText="@string/enable_text_hands_free"
|
||||
app:optionsText="@string/options_text_hands_free" />
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_auto_call"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_auto_call"
|
||||
app:enableText="@string/enable_text_auto_call"
|
||||
app:optionsText="@string/options_text_auto_call" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
139
app/src/main/res/layout/activity_settings_utils.xml
Normal file
139
app/src/main/res/layout/activity_settings_utils.xml
Normal file
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".activity.settings.utils.SettingsUtilsActivity">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.dialer.activity.settings.utils.SettingsUtilsActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:onClick="@{click::exit}"
|
||||
android:src="@drawable/ic_cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="拨号设置"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:src="@drawable/ic_confirm"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/settings_card_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_float_window"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_float"
|
||||
app:enableText="@string/enable_text_float"
|
||||
app:optionsText="@string/options_text_float" />
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_float_window_kill"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_float_kill"
|
||||
app:enableText="@string/enable_text_float_kill"
|
||||
app:optionsText="@string/options_text_float_kill" />
|
||||
|
||||
<com.ttstd.dialer.view.SettingItem
|
||||
android:id="@+id/si_default_launcher"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:disableText="@string/disable_text_default_launcher"
|
||||
app:enableText="@string/enable_text_default_launcher"
|
||||
app:optionsText="@string/options_text_default_launcher" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_100">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="截图"
|
||||
android:onClick="@{click::screenshotSnap}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_snap"
|
||||
android:layout_width="@dimen/dp_100"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
71
app/src/main/res/layout/activity_template.xml
Normal file
71
app/src/main/res/layout/activity_template.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".activity.template.TemplateActivity">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.dialer.activity.template.TemplateActivity.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:src="@drawable/ic_cancel"
|
||||
android:onClick="@{click::exit}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="页面标题"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/ic_confirm"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bar">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -17,104 +17,108 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black">
|
||||
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_180"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_location"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_30"
|
||||
tools:text="北京" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_temp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_4"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="@{weatherNow.temp+`°`}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_40"
|
||||
tools:text="N/A°" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_weather_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="@{weatherNow.text}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_20"
|
||||
tools:text="晴" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_temp_range"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_20"
|
||||
tools:text="最高10° 最低0°" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/coordinatorLayout">
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_180"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_location"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_30"
|
||||
tools:text="北京" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_temp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_4"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="@{weatherNow==null?`N/A°`: weatherNow.temp+`°`}"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_40"
|
||||
tools:text="N/A°" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_weather_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_4"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="@{weatherNow.text}"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_24"
|
||||
tools:text="晴" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_temp_range"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_4"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_20"
|
||||
tools:text="最高10° 最低0°" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_hourly"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/dp_8"
|
||||
android:background="@drawable/weather_card_background" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/dp_8"
|
||||
android:background="@drawable/weather_card_background" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_220"
|
||||
android:layout_marginBottom="@dimen/dp_16"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_32"
|
||||
android:layout_marginBottom="@dimen/dp_16"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
@@ -39,14 +39,13 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_video"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_height="@dimen/dp_56">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/wechat_call_video"
|
||||
android:background="@drawable/call_phone_selector"
|
||||
android:onClick="@{click::callWechatVideo}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -64,7 +63,7 @@
|
||||
android:singleLine="true"
|
||||
android:text="微信视频"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_16"
|
||||
android:textSize="@dimen/sp_22"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -78,15 +77,14 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_audio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_height="@dimen/dp_56"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:background="@drawable/wechat_call_audio"
|
||||
android:background="@drawable/call_phone_selector"
|
||||
android:onClick="@{click::callWechatAudio}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -104,7 +102,7 @@
|
||||
android:singleLine="true"
|
||||
android:text="微信语音"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_16"
|
||||
android:textSize="@dimen/sp_22"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -118,15 +116,14 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_dial"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_height="@dimen/dp_56"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:background="@drawable/wechat_call_dialer"
|
||||
android:background="@drawable/call_phone_selector"
|
||||
android:onClick="@{click::callPhone}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -144,7 +141,7 @@
|
||||
android:singleLine="true"
|
||||
android:text="电话"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_16"
|
||||
android:textSize="@dimen/sp_22"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -156,17 +153,16 @@
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_sms"
|
||||
android:id="@+id/cl_cancel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_height="@dimen/dp_56"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:background="@drawable/wechat_call_cancel"
|
||||
android:background="@drawable/call_phone_selector"
|
||||
android:onClick="@{click::cancel}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -184,7 +180,7 @@
|
||||
android:singleLine="true"
|
||||
android:text="取消"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_16"
|
||||
android:textSize="@dimen/sp_22"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="@dimen/dp_48"
|
||||
android:layout_width="@dimen/dp_60"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:layout_height="@dimen/dp_140"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -25,8 +25,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:minEms="4"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -42,14 +42,16 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_not_applicable"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@@ -62,8 +64,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
android:layout_height="@dimen/dp_52"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/view">
|
||||
@@ -29,9 +29,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:maxLines="1"
|
||||
android:minEms="4"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:minEms="3"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_22"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -39,11 +39,12 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:layout_marginStart="@dimen/dp_24"
|
||||
android:layout_width="@dimen/dp_32"
|
||||
android:layout_height="@dimen/dp_32"
|
||||
android:layout_marginStart="@dimen/dp_32"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_not_applicable"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/tv_date"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@@ -51,8 +52,8 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/dp_24"
|
||||
android:layout_marginEnd="@dimen/dp_48"
|
||||
android:layout_marginStart="@dimen/dp_48"
|
||||
android:layout_marginEnd="@dimen/dp_16"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/iv_icon">
|
||||
|
||||
@@ -60,10 +61,9 @@
|
||||
android:id="@+id/tv_temp_min"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -74,8 +74,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_18"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
||||
21
app/src/main/res/layout/layout_floating_window.xml
Normal file
21
app/src/main/res/layout/layout_floating_window.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.shehuan.niv.NiceImageView
|
||||
android:id="@+id/nv_back"
|
||||
android:layout_width="@dimen/dp_80"
|
||||
android:layout_height="@dimen/dp_80"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_home_float_window"
|
||||
app:is_circle="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
86
app/src/main/res/layout/layout_setting_content_item.xml
Normal file
86
app/src/main/res/layout/layout_setting_content_item.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/settings_item_height"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/settings_card_bg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tb"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_options"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/settings_text_color_item"
|
||||
android:textSize="@dimen/settings_text_size"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_hint"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/top_height"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/setting_disable_color"
|
||||
android:textSize="@dimen/settings_text_explanation_size"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_options" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.ttstd.dialer.view.ToggleButton
|
||||
android:id="@+id/tb"
|
||||
android:layout_width="@dimen/dp_48"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/iv_more"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_more"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/icon_more"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@color/lightGray"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -2,6 +2,7 @@
|
||||
<resources>
|
||||
<dimen name="home_item_text_size">71.1111sp</dimen>
|
||||
<dimen name="default_radius">28.4444dp</dimen>
|
||||
|
||||
<dimen name="dp_m_60">-213.3333dp</dimen>
|
||||
<dimen name="dp_m_30">-106.6667dp</dimen>
|
||||
<dimen name="dp_m_20">-71.1111dp</dimen>
|
||||
|
||||
26
app/src/main/res/values/attrs.xml
Normal file
26
app/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="ToggleButton">
|
||||
<attr name="tbBorderWidth" format="dimension" />
|
||||
<attr name="tbOffBorderColor" format="reference|color" />
|
||||
<attr name="tbOffColor" format="reference|color" />
|
||||
<attr name="tbOnColor" format="reference|color" />
|
||||
<attr name="tbSpotColor" format="reference|color" />
|
||||
<attr name="tbAnimate" format="reference|boolean" />
|
||||
<attr name="tbAsDefaultOn" format="reference|boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SettingItem">
|
||||
<attr name="optionsText" format="reference|string" />
|
||||
<attr name="optionsTextColor" format="reference|color" />
|
||||
<attr name="enableText" format="reference|string" />
|
||||
<attr name="disableText" format="reference|string" />
|
||||
<attr name="enableTextColor" format="reference|color" />
|
||||
<attr name="showHintText" format="reference|boolean" />
|
||||
<attr name="showToggle" format="reference|boolean" />
|
||||
<attr name="showMore" format="reference|boolean" />
|
||||
<attr name="linkage" format="reference|boolean" />
|
||||
<attr name="dividerLine" format="reference|boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
@@ -1,14 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#6200EE</color>
|
||||
<color name="colorPrimaryDark">#3700B3</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
<color name="colorPrimary">#2196F3</color>
|
||||
<color name="colorPrimaryDark">#3F51B5</color>
|
||||
<color name="colorAccent">#03A9F4</color>
|
||||
|
||||
<color name="indicator_color_normal">#99bd2f25</color>
|
||||
<color name="indicator_color_selected">#BD2F25</color>
|
||||
|
||||
<color name="default_background_color">#F8CBBD</color>
|
||||
<color name="default_background_color">#ECECEC</color>
|
||||
|
||||
<color name="settings_text_color_item">#000000</color>
|
||||
<color name="setting_enable_color">#000000</color>
|
||||
<color name="setting_disable_color">#9D9D9D</color>
|
||||
|
||||
|
||||
<!--https://www.jianshu.com/p/8dc258dfd189-->
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
<dimen name="home_item_text_size">20sp</dimen>
|
||||
<dimen name="default_radius">8dp</dimen>
|
||||
|
||||
<dimen name="settings_item_height">80dp</dimen>
|
||||
<dimen name="settings_text_size">18sp</dimen>
|
||||
<dimen name="settings_text_explanation_size">15sp</dimen>
|
||||
<dimen name="top_height">2dp</dimen>
|
||||
|
||||
|
||||
<dimen name="dp_m_60">-60dp</dimen>
|
||||
<dimen name="dp_m_30">-30dp</dimen>
|
||||
<dimen name="dp_m_20">-20dp</dimen>
|
||||
|
||||
@@ -5,6 +5,45 @@
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
<string name="floating_action_button_behavior">com.ttstd.dialer.view.ScrollAwareFABBehavior</string>
|
||||
|
||||
<string name="kill_app_number">共清理了%d个应用</string>
|
||||
|
||||
<string name="accessibility_service_label">❤拨号助手无障碍服务👈🏻</string>
|
||||
<string name="accessibility_service_description">使用拨号助手一键拨号</string>
|
||||
|
||||
<!--call start-->
|
||||
<string name="options_text_fast_call_phone">开启快捷通话</string>
|
||||
<string name="enable_text_fast_call_phone">已开启,快捷联系人通话类型</string>
|
||||
<string name="disable_text_fast_call_phone">未开启,快捷联系人通话类型</string>
|
||||
|
||||
<string name="options_text_voice_broadcast">来电语音播报</string>
|
||||
<string name="enable_text_voice_broadcast">已开启,电话呼入时语音播报联系人</string>
|
||||
<string name="disable_text_voice_broadcast">未开启,电话呼入时语音播报联系人</string>
|
||||
|
||||
<string name="options_text_auto_accept">微信自动接听</string>
|
||||
<string name="enable_text_auto_accept">已开启,自动接听视频和语音</string>
|
||||
<string name="disable_text_auto_accept">未开启,自动接听视频和语音</string>
|
||||
|
||||
<string name="options_text_hands_free">自动开启免提</string>
|
||||
<string name="enable_text_hands_free">已开启,自动开启免提</string>
|
||||
<string name="disable_text_hands_free">未开启,自动开启免提</string>
|
||||
|
||||
<string name="options_text_auto_call">微信自动拨打视频</string>
|
||||
<string name="enable_text_auto_call">已开启,无障碍模式实现微信一键通话</string>
|
||||
<string name="disable_text_auto_call">未开启,无障碍模式实现微信一键通话</string>
|
||||
<!--call end-->
|
||||
|
||||
<!--utils start-->
|
||||
<string name="options_text_float">开启悬浮按钮</string>
|
||||
<string name="enable_text_float">已开启,点小圆点可以直接返回桌面</string>
|
||||
<string name="disable_text_float">未开启,点小圆点可以直接返回桌面</string>
|
||||
|
||||
<string name="options_text_float_kill">悬浮按钮清理内存</string>
|
||||
<string name="enable_text_float_kill">已开启,在桌面时点击悬浮窗清理内存</string>
|
||||
<string name="disable_text_float_kill">未开启,在桌面时点击悬浮窗清理内存</string>
|
||||
|
||||
<string name="options_text_default_launcher">设置为默认桌面</string>
|
||||
<string name="enable_text_default_launcher">已设置为默认桌面</string>
|
||||
<string name="disable_text_default_launcher">未设置为默认桌面</string>
|
||||
<!--utils end-->
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="backgroundColor">@color/default_background_color</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="AppThemeFitsSystemWindows" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
@@ -28,6 +30,7 @@
|
||||
<item name="colorPrimary">@color/black</item>
|
||||
<item name="colorPrimaryDark">@color/black</item>
|
||||
<item name="colorAccent">@color/black</item>
|
||||
<item name="backgroundColor">@color/black</item>
|
||||
<item name="android:fitsSystemWindows">true</item>
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user