feat: 联系人增加头像选择
This commit is contained in:
@@ -22,7 +22,7 @@ android {
|
||||
applicationId "com.ttstd.dialer"
|
||||
|
||||
//There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
|
||||
// minSdkVersion 23
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 37
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
@@ -98,20 +98,19 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "version"
|
||||
productFlavors {
|
||||
// 用于正常开发和发布的版本
|
||||
normal {
|
||||
dimension "version"
|
||||
minSdkVersion 23 // 你的原始最低版本
|
||||
}
|
||||
// 专门用于调试高版本特性的版本
|
||||
debugApi26 {
|
||||
dimension "version"
|
||||
minSdkVersion 31 // 为了使用 Database Inspector
|
||||
}
|
||||
}
|
||||
|
||||
// flavorDimensions "version"
|
||||
// productFlavors {
|
||||
// // 用于正常开发和发布的版本
|
||||
// normal {
|
||||
// dimension "version"
|
||||
// minSdkVersion 23 // 你的原始最低版本
|
||||
// }
|
||||
// // 专门用于调试高版本特性的版本
|
||||
// debugApi26 {
|
||||
// dimension "version"
|
||||
// minSdkVersion 31 // 为了使用 Database Inspector
|
||||
// }
|
||||
// }
|
||||
|
||||
signingConfigs {
|
||||
keypub {
|
||||
@@ -232,6 +231,10 @@ dependencies {
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0"
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation "androidx.work:work-runtime:2.9.0"
|
||||
implementation 'androidx.browser:browser:1.8.0'
|
||||
implementation "androidx.webkit:webkit:1.14.0"
|
||||
|
||||
// Room依赖
|
||||
implementation "androidx.room:room-runtime:2.8.4"
|
||||
implementation "androidx.room:room-rxjava3:2.8.4"
|
||||
@@ -358,8 +361,6 @@ dependencies {
|
||||
kapt 'com.arialyy.aria:compiler:3.8.15'
|
||||
//状态栏透明
|
||||
implementation 'com.gitee.zackratos:UltimateBarX:0.8.0'
|
||||
//指示器
|
||||
implementation 'com.github.hackware1993:MagicIndicator:1.7.0'
|
||||
implementation 'com.opencsv:opencsv:5.12.0'
|
||||
|
||||
// 吐司框架:https://github.com/getActivity/Toaster
|
||||
@@ -368,6 +369,24 @@ dependencies {
|
||||
implementation 'com.github.getActivity:XXPermissions:20.0'
|
||||
implementation 'com.github.zcweng:switch-button:0.0.3@aar'
|
||||
implementation "com.github.kongzue.DialogX:DialogX:0.0.50"
|
||||
implementation 'cn.6tail:lunar:1.7.7'
|
||||
implementation "io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.4.0"
|
||||
implementation 'io.github.youth5201314:banner:2.2.3'
|
||||
|
||||
// TinyPinyin核心包,约80KB
|
||||
implementation 'com.github.promeg:tinypinyin:2.0.3'
|
||||
// 可选,适用于Android的中国地区词典
|
||||
implementation 'com.github.promeg:tinypinyin-lexicons-android-cncity:2.0.3'
|
||||
|
||||
// PictureSelector 基础 (必须)
|
||||
implementation 'io.github.lucksiege:pictureselector:v3.11.2'
|
||||
// 图片压缩 (按需引入)
|
||||
implementation 'io.github.lucksiege:compress:v3.11.2'
|
||||
// 图片裁剪 (按需引入)
|
||||
implementation 'io.github.lucksiege:ucrop:v3.11.2'
|
||||
// 自定义相机 (按需引入)
|
||||
implementation 'io.github.lucksiege:camerax:v3.11.2'
|
||||
|
||||
}
|
||||
|
||||
// 在 dependencies 之后添加
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
coreApp="true"
|
||||
package="com.ttstd.dialer"
|
||||
android:sharedUserId="android.uid.system">
|
||||
|
||||
@@ -23,6 +24,13 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
|
||||
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.SET_ALARM" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
|
||||
<!--baidumap start-->
|
||||
<!-- 这个权限用于进行网络定位-->
|
||||
@@ -104,13 +112,18 @@
|
||||
android:name=".activity.settings.utils.SettingsUtilsActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.alarm.AlarmAlertActivity"
|
||||
android:showOnLockScreen="true"
|
||||
android:turnScreenOn="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".service.main.MainService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
|
||||
<service
|
||||
android:name=".service.DialerAccessibilityService"
|
||||
android:exported="true"
|
||||
@@ -125,18 +138,29 @@
|
||||
android:resource="@xml/accessibility_service_config" />
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.AppChangedReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<!-- <receiver-->
|
||||
<!-- android:name=".receiver.AppChangedReceiver"-->
|
||||
<!-- android:enabled="true"-->
|
||||
<!-- android:exported="true">-->
|
||||
<!-- <intent-filter android:priority="1000">-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_INSTALL" />-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_ADDED" />-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_REPLACED" />-->
|
||||
<!-- <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_REMOVED" />-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_CHANGED" />-->
|
||||
<!-- <action android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />-->
|
||||
<!-- <action android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />-->
|
||||
<!-- <action android:name="android.intent.action.ROLLBACK_COMMITTED" />-->
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<!-- <data android:scheme="package" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </receiver>-->
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.AlarmReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.ttstd.dialer.activity.alarm;
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
public class AlarmAlertActivity extends AppCompatActivity {
|
||||
|
||||
private Ringtone ringtone;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// 允许在锁屏上显示并点亮屏幕
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(true);
|
||||
setTurnScreenOn(true);
|
||||
} else {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_alarm_alert);
|
||||
|
||||
Button btnStop = findViewById(R.id.btn_stop_alarm);
|
||||
btnStop.setOnClickListener(v -> {
|
||||
if (ringtone != null && ringtone.isPlaying()) {
|
||||
ringtone.stop();
|
||||
}
|
||||
finish();
|
||||
});
|
||||
|
||||
playAlarmRingtone();
|
||||
}
|
||||
|
||||
private void playAlarmRingtone() {
|
||||
Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
|
||||
if (alarmUri == null) {
|
||||
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
|
||||
}
|
||||
|
||||
ringtone = RingtoneManager.getRingtone(this, alarmUri);
|
||||
if (ringtone != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
AudioAttributes aa = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_ALARM)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.build();
|
||||
ringtone.setAudioAttributes(aa);
|
||||
}
|
||||
ringtone.play();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (ringtone != null && ringtone.isPlaying()) {
|
||||
ringtone.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,10 @@ import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.adapter.MoreAppAdapter;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.config.LiveDataAction;
|
||||
import com.ttstd.dialer.databinding.ActivityAppListBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.livedata.LiveDataBus;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -64,6 +66,11 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
|
||||
.observe(this, event -> {
|
||||
mViewModel.getDbAppList();
|
||||
});
|
||||
|
||||
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
|
||||
@Override
|
||||
public void onChanged(List<AppInfo> appInfos) {
|
||||
|
||||
@@ -1,21 +1,55 @@
|
||||
package com.ttstd.dialer.activity.contact.add;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.text.InputFilter;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.kongzue.dialogx.dialogs.PopTip;
|
||||
import com.kongzue.dialogx.dialogs.WaitDialog;
|
||||
import com.luck.picture.lib.basic.PictureSelector;
|
||||
import com.luck.picture.lib.config.SelectMimeType;
|
||||
import com.luck.picture.lib.entity.LocalMedia;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.databinding.ActivityContactAddBinding;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.filter.NoSpaceInputFilter;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.utils.GlideUtils;
|
||||
import com.ttstd.dialer.utils.PhoneUtils;
|
||||
import com.ttstd.dialer.view.GlideEngine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, ActivityContactAddBinding> {
|
||||
|
||||
private static final String TAG = "ContactAddActivity";
|
||||
|
||||
private ActivityResultLauncher<Intent> launcherResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
|
||||
new ActivityResultCallback<ActivityResult>() {
|
||||
@Override
|
||||
public void onActivityResult(ActivityResult result) {
|
||||
int resultCode = result.getResultCode();
|
||||
if (resultCode == RESULT_OK) {
|
||||
ArrayList<LocalMedia> selectList = PictureSelector.obtainSelectorList(result.getData());
|
||||
// analyticalSelectResults(selectList);
|
||||
Log.i(TAG, "onActivityResult PictureSelector size = " + selectList.size());
|
||||
GlideUtils.loadBitmapSafe(ContactAddActivity.this, selectList.get(0).getRealPath(), mViewDataBinding.nvAvatar);
|
||||
} else if (resultCode == RESULT_CANCELED) {
|
||||
Log.i(TAG, "onActivityResult PictureSelector Cancel");
|
||||
}
|
||||
}
|
||||
});
|
||||
;
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
@@ -44,6 +78,8 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
mViewDataBinding.etName.setFilters(new InputFilter[]{new NoSpaceInputFilter()});
|
||||
mViewDataBinding.etPhone.setFilters(new InputFilter[]{new NoSpaceInputFilter()});
|
||||
mViewDataBinding.etRemark.setFilters(new InputFilter[]{new NoSpaceInputFilter()});
|
||||
|
||||
mViewDataBinding.sbPinned.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,7 +88,7 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
@Override
|
||||
public void onChanged(Long aLong) {
|
||||
if (aLong > 0) {
|
||||
Toaster.show("添加成功");
|
||||
PopTip.show("添加成功").iconSuccess();
|
||||
finish();
|
||||
} else {
|
||||
|
||||
@@ -62,6 +98,7 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
mViewModel.mDbIdData.observe(this, new Observer<Long>() {
|
||||
@Override
|
||||
public void onChanged(Long aLong) {
|
||||
PopTip.show("添加成功").iconSuccess();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
@@ -76,20 +113,26 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
|
||||
public void save(View view) {
|
||||
if (mViewDataBinding.etName.getText() == null) {
|
||||
Toaster.show("请输入姓名");
|
||||
PopTip.show("请输入姓名").iconWarning();
|
||||
return;
|
||||
}
|
||||
String name = mViewDataBinding.etName.getText().toString();
|
||||
|
||||
if (mViewDataBinding.etPhone.getText() == null) {
|
||||
Toaster.show("请输入手机号码");
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
PopTip.show("请输入姓名").iconWarning();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mViewDataBinding.etPhone.getText() == null) {
|
||||
PopTip.show("请输入手机号码").iconWarning();
|
||||
return;
|
||||
}
|
||||
String phone = mViewDataBinding.etPhone.getText().toString();
|
||||
|
||||
if (TextUtils.isEmpty(phone)) {
|
||||
PopTip.show("请输入手机号码").iconWarning();
|
||||
return;
|
||||
}
|
||||
if (!PhoneUtils.isValidPhone(phone)) {
|
||||
Toaster.show("手机号码格式错误");
|
||||
PopTip.show("手机号码格式错误").iconWarning();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,7 +148,7 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
contactInfo.setCreateTime(System.currentTimeMillis());
|
||||
contactInfo.setUpdateTime(System.currentTimeMillis());
|
||||
mViewModel.addContact(contactInfo);
|
||||
|
||||
WaitDialog.show("正在上传联系人");
|
||||
}
|
||||
|
||||
public void saveLocalDb(View view) {
|
||||
@@ -121,7 +164,17 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
|
||||
contactInfo.setBindSn(DeviceManagerService.getInstance().getSerial());
|
||||
contactInfo.setCreateTime(System.currentTimeMillis());
|
||||
contactInfo.setUpdateTime(System.currentTimeMillis());
|
||||
mViewModel.saveContact(contactInfo);
|
||||
mViewModel.saveContactDb(contactInfo);
|
||||
}
|
||||
|
||||
public void openSelector(View view) {
|
||||
PictureSelector.create(ContactAddActivity.this)
|
||||
.openGallery(SelectMimeType.ofImage())
|
||||
.setImageEngine(GlideEngine.createGlideEngine())
|
||||
// .setVideoPlayerEngine(videoPlayerEngine)
|
||||
.forResult(launcherResult);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
|
||||
return Observable.just(id);
|
||||
} else {
|
||||
Logger.w(TAG, "业务失败,降级到本地保存: " + baseResponse.getMsg());
|
||||
return saveContactObservable(contactInfo);
|
||||
return saveContactDbObservable(contactInfo);
|
||||
}
|
||||
})
|
||||
.onErrorResumeNext(throwable -> {
|
||||
Logger.e(TAG, "网络异常,降级到本地保存: " + throwable.getMessage());
|
||||
return saveContactObservable(contactInfo);
|
||||
return saveContactDbObservable(contactInfo);
|
||||
})
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<Long>() {
|
||||
@@ -80,7 +80,7 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
|
||||
});
|
||||
}
|
||||
|
||||
private Observable<Long> saveContactObservable(ContactInfo contactInfo) {
|
||||
private Observable<Long> saveContactDbObservable(ContactInfo contactInfo) {
|
||||
return Observable.create((ObservableOnSubscribe<Long>) emitter -> {
|
||||
Logger.d(TAG, "执行本地保存: " + contactInfo);
|
||||
contactInfo.setPosition(mRepository.getTotalCount() + 1);
|
||||
@@ -92,8 +92,8 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public void saveContact(ContactInfo contactInfo) {
|
||||
saveContactObservable(contactInfo)
|
||||
public void saveContactDb(ContactInfo contactInfo) {
|
||||
saveContactDbObservable(contactInfo)
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<Long>() {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package com.ttstd.dialer.activity.main;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Window;
|
||||
@@ -20,12 +17,14 @@ import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.adapter.AppGridAdapter;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.config.LiveDataAction;
|
||||
import com.ttstd.dialer.databinding.ActivityMainBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
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.livedata.LiveDataBus;
|
||||
import com.ttstd.dialer.manager.AppManager;
|
||||
import com.ttstd.dialer.manager.WeatherUpdateManager;
|
||||
import com.ttstd.dialer.service.main.MainService;
|
||||
@@ -157,7 +156,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
mViewDataBinding.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
Logger.e(TAG, "onPageScrolled: position = " + position);
|
||||
Logger.v(TAG, "onPageScrolled: position = " + position);
|
||||
if (mCurrentIndex == -1 && position == 0) {
|
||||
mViewDataBinding.viewPager.setCurrentItem(mDefaultIndex, true);
|
||||
mCurrentIndex = mDefaultIndex;
|
||||
@@ -168,12 +167,12 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
Logger.e(TAG, "onPageSelected: position = " + position);
|
||||
Logger.v(TAG, "onPageSelected: position = " + position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
Logger.e(TAG, "onPageScrollStateChanged: state = " + state);
|
||||
Logger.v(TAG, "onPageScrollStateChanged: state = " + state);
|
||||
}
|
||||
});
|
||||
mViewDataBinding.magicIndicator.setNavigator(mScaleCircleNavigator);
|
||||
@@ -213,6 +212,12 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
}
|
||||
});
|
||||
|
||||
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
|
||||
.observe(this, event -> {
|
||||
mViewModel.getOutsideApp();
|
||||
mViewModel.getHotseatApp();
|
||||
});
|
||||
|
||||
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
|
||||
@Override
|
||||
public void onChanged(List<AppInfo> appInfos) {
|
||||
@@ -319,35 +324,11 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
}
|
||||
|
||||
private void registerReceivers() {
|
||||
registerAppChangedReceive();
|
||||
|
||||
}
|
||||
|
||||
private void unregisterReceivers() {
|
||||
if (mPackageChangedReceiver != null) {
|
||||
unregisterReceiver(mPackageChangedReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
private PackageChangedReceiver mPackageChangedReceiver;
|
||||
|
||||
private void registerAppChangedReceive() {
|
||||
if (null == mPackageChangedReceiver) {
|
||||
mPackageChangedReceiver = new PackageChangedReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
registerReceiver(mPackageChangedReceiver, filter);
|
||||
}
|
||||
|
||||
private class PackageChangedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Logger.e(TAG, "onReceive: " + intent.getAction());
|
||||
}
|
||||
}
|
||||
|
||||
private void setAppList() {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.ttstd.dialer.activity.settings.home;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.settings.call.SettingsCallActivity;
|
||||
import com.ttstd.dialer.activity.settings.utils.SettingsUtilsActivity;
|
||||
@@ -58,5 +62,14 @@ public class SettingsActivity extends BaseMvvmActivity<SettingsViewModel, Activi
|
||||
public void openUtilsSettings(View view) {
|
||||
startActivity(new Intent(SettingsActivity.this, SettingsUtilsActivity.class));
|
||||
}
|
||||
|
||||
public void aboutUs(View view) {
|
||||
String url = "https://www.ttstd.com";
|
||||
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
// (可选)自定义配置
|
||||
builder.setToolbarColor(ContextCompat.getColor(SettingsActivity.this, R.color.colorPrimary));
|
||||
CustomTabsIntent customTabsIntent = builder.build();
|
||||
customTabsIntent.launchUrl(SettingsActivity.this, Uri.parse(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,15 +59,22 @@ public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewMod
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
if (mViewDataBinding.siFloatWindow.isChecked()) {
|
||||
mViewDataBinding.siFloatWindowKill.setEnabled(true);
|
||||
} else {
|
||||
mViewDataBinding.siFloatWindowKill.setEnabled(false);
|
||||
}
|
||||
|
||||
mViewDataBinding.siFloatWindow.setOnToggleChanged(new ToggleButton.OnToggleChanged() {
|
||||
@Override
|
||||
public void onToggle(boolean on) {
|
||||
Intent intent = new Intent(SettingsUtilsActivity.this, MainService.class);
|
||||
if (on) {
|
||||
mViewDataBinding.siFloatWindowKill.setEnabled(true);
|
||||
intent.setAction(MainService.SHOW_FLOAT_WINDOW_ACTION);
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_ENABLE, 1);
|
||||
} else {
|
||||
mViewDataBinding.siFloatWindowKill.setEnabled(false);
|
||||
intent.setAction(MainService.HIDE_FLOAT_WINDOW_ACTION);
|
||||
mMMKV.encode(CommonConfig.FLOAT_WINDOW_ENABLE, 0);
|
||||
}
|
||||
@@ -99,6 +106,7 @@ public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewMod
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,7 +79,7 @@ public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBindi
|
||||
|
||||
public void getWeather10D(String adCode) {
|
||||
Logger.e(TAG, "getWeather10D: " + adCode);
|
||||
WeatherManager.getInstance().getWeather10Day(adCode, new Callback<WeatherDailyResponse>() {
|
||||
WeatherManager.getInstance().getWeather10DayAdCode(adCode, new Callback<WeatherDailyResponse>() {
|
||||
@Override
|
||||
public void onSuccess(WeatherDailyResponse weatherDailyResponse) {
|
||||
Logger.e("getWeather10D", "onSuccess: ");
|
||||
|
||||
@@ -16,12 +16,13 @@ import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.kongzue.dialogx.dialogs.PopTip;
|
||||
import com.shehuan.niv.NiceImageView;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.fragment.dialog.shortcut.ShortcutDialogFagment;
|
||||
import com.ttstd.dialer.fragment.dialog.shortcut.MoveAppDialogFagment;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.iconloader.IconCacheManager;
|
||||
@@ -82,29 +83,49 @@ public class AppAdapter extends RecyclerView.Adapter<AppAdapter.AppHolder> imple
|
||||
holder.root.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ApkUtils.openApp(mContext, appInfo.getComponentName());
|
||||
if (!ApkUtils.openApp(mContext, appInfo.getComponentName())) {
|
||||
PopTip.show("打开失败");
|
||||
}
|
||||
}
|
||||
});
|
||||
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
|
||||
shortcutDialogFagment.setTitil("温馨提示");
|
||||
shortcutDialogFagment.setTips("是否将应用放入更多应用");
|
||||
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
|
||||
// ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
|
||||
// shortcutDialogFagment.setTitile("温馨提示");
|
||||
// shortcutDialogFagment.setTips("是否把这个应用移到“更多应用”里?");
|
||||
// shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
|
||||
// @Override
|
||||
// public void onPositiveClick() {
|
||||
// if (mShortcutCallback != null)
|
||||
// mShortcutCallback.setAppInside(appInfo);
|
||||
// shortcutDialogFagment.dismiss();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNegativeClick() {
|
||||
// shortcutDialogFagment.dismiss();
|
||||
// }
|
||||
// });
|
||||
// shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
|
||||
|
||||
MoveAppDialogFagment moveAppDialogFagment = new MoveAppDialogFagment(appInfo);
|
||||
moveAppDialogFagment.setTitile("温馨提示");
|
||||
moveAppDialogFagment.setTips("是否把这个应用移到“更多应用”里?");
|
||||
moveAppDialogFagment.setOnClickListener(new MoveAppDialogFagment.OnClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
if (mShortcutCallback != null)
|
||||
mShortcutCallback.setAppInside(appInfo);
|
||||
shortcutDialogFagment.dismiss();
|
||||
moveAppDialogFagment.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
shortcutDialogFagment.dismiss();
|
||||
moveAppDialogFagment.dismiss();
|
||||
}
|
||||
});
|
||||
shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
|
||||
moveAppDialogFagment.show(mContext.getSupportFragmentManager(), "MoveAppDialogFagment");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -111,8 +110,8 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
|
||||
shortcutDialogFagment.setTitil("温馨提示");
|
||||
shortcutDialogFagment.setTips("是否将应用放入更多应用");
|
||||
shortcutDialogFagment.setTitile("温馨提示");
|
||||
shortcutDialogFagment.setTips("是否把这个应用移到“更多应用”里?");
|
||||
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -16,13 +18,12 @@ import com.qweather.sdk.response.weather.WeatherHourly;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.glide.GlideApp;
|
||||
import com.ttstd.dialer.glide.svg.SvgSoftwareLayerSetter;
|
||||
import com.ttstd.dialer.utils.GlideUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.TimeUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
public class HourlyWeatherAdapter extends RecyclerView.Adapter<HourlyWeatherAdapter.HourlyWeather> {
|
||||
private static final String TAG = "HourlyWeatherAdapter";
|
||||
|
||||
@@ -57,7 +58,7 @@ public class HourlyWeatherAdapter extends RecyclerView.Adapter<HourlyWeatherAdap
|
||||
String icon = weatherHourly.getIcon();
|
||||
// 获取资源ID
|
||||
String fileName = "qweather_" + icon; // 替换为你的文件名
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "raw", mContext.getPackageName());
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "drawable", mContext.getPackageName());
|
||||
if (resId != 0) {
|
||||
// 使用Glide加载,假设已配置SVG支持
|
||||
// Glide.with(mContext)
|
||||
@@ -65,7 +66,8 @@ public class HourlyWeatherAdapter extends RecyclerView.Adapter<HourlyWeatherAdap
|
||||
// .load(resId) // 加载raw资源ID
|
||||
// .diskCacheStrategy(DiskCacheStrategy.NONE) // raw资源通常不缓存
|
||||
// .into(holder.iv_icon);
|
||||
requestBuilder.load(resId).into(holder.iv_icon);
|
||||
// requestBuilder.load(resId).into(holder.iv_icon);
|
||||
GlideUtils.loadImageSafe(mContext, resId, holder.iv_icon, R.drawable.ic_not_applicable);
|
||||
} else {
|
||||
// 处理错误
|
||||
Logger.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
|
||||
@@ -93,7 +93,7 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
|
||||
shortcutDialogFagment.setTitil("温馨提示");
|
||||
shortcutDialogFagment.setTitile("温馨提示");
|
||||
shortcutDialogFagment.setTips("是否将应用放在桌面显示");
|
||||
|
||||
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -18,12 +20,11 @@ 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 com.ttstd.dialer.utils.GlideUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherHolder> {
|
||||
private static final String TAG = "WeatherAdapter";
|
||||
|
||||
@@ -71,7 +72,7 @@ public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherH
|
||||
String iconDay = weatherDaily.getIconDay();
|
||||
// 获取资源ID
|
||||
String fileName = "qweather_" + iconDay; // 替换为你的文件名
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "raw", mContext.getPackageName());
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "drawable", mContext.getPackageName());
|
||||
if (resId != 0) {
|
||||
// 使用Glide加载,假设已配置SVG支持
|
||||
// Glide.with(mContext)
|
||||
@@ -79,7 +80,7 @@ public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherH
|
||||
// .load(resId) // 加载raw资源ID
|
||||
// .diskCacheStrategy(DiskCacheStrategy.NONE) // raw资源通常不缓存
|
||||
// .into(holder.iv_icon);
|
||||
requestBuilder.load(resId).into(holder.iv_icon);
|
||||
GlideUtils.loadImageSafe(mContext, resId, holder.iv_icon, R.drawable.ic_not_applicable);
|
||||
} else {
|
||||
// 处理错误
|
||||
Logger.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.ttstd.dialer.alarmclock;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import cn.jpush.android.service.AlarmReceiver;
|
||||
|
||||
public class AlarmManagerHelper {
|
||||
|
||||
public static final int ALARM_REQUEST_CODE = 1001;
|
||||
|
||||
/**
|
||||
* 设置一个精准闹钟(兼容 Android 5.0 到 Android 16)
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param triggerAtMillis 触发的时间戳(毫秒)
|
||||
*/
|
||||
public static void setExactAlarm(Context context, long triggerAtMillis) {
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarmManager == null) return;
|
||||
|
||||
// 检查 Android 12+ 的精准闹钟权限
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (!alarmManager.canScheduleExactAlarms()) {
|
||||
// 如果没有权限,跳转到系统设置页让用户授权
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
// 使用 FLAG_IMMUTABLE 或 FLAG_MUTABLE 增强 Android 12+ 安全性
|
||||
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE :
|
||||
PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, ALARM_REQUEST_CODE, intent, flags);
|
||||
|
||||
// 核心兼容性定时逻辑
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Android 6.0+ 支持低功耗休眠模式(Doze Mode)下的精准唤醒
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// Android 4.4 - 5.1 精准唤醒
|
||||
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
} else {
|
||||
// Android 4.4 以下
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消闹钟
|
||||
*/
|
||||
public static void cancelAlarm(Context context) {
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarmManager != null) {
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE :
|
||||
PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, ALARM_REQUEST_CODE, intent, flags);
|
||||
alarmManager.cancel(pendingIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ttstd.dialer.alarmclock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class AlarmRepeatConfig {
|
||||
public static final int REPEAT_ONCE = 0; // 只响一次
|
||||
public static final int REPEAT_EVERYDAY = 1; // 每天
|
||||
public static final int REPEAT_WEEKDAYS = 2; // 周一至周五
|
||||
public static final int REPEAT_CUSTOM = 3; // 自定义星期几(比如只选周六日)
|
||||
|
||||
private int repeatType;
|
||||
private ArrayList<Integer> customDays; // 存储 Calendar.MONDAY (2) 到 Calendar.SATURDAY (7)
|
||||
|
||||
public AlarmRepeatConfig(int repeatType) {
|
||||
this.repeatType = repeatType;
|
||||
this.customDays = new ArrayList<>();
|
||||
if (repeatType == REPEAT_WEEKDAYS) {
|
||||
customDays.add(Calendar.MONDAY);
|
||||
customDays.add(Calendar.TUESDAY);
|
||||
customDays.add(Calendar.WEDNESDAY);
|
||||
customDays.add(Calendar.THURSDAY);
|
||||
customDays.add(Calendar.FRIDAY);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRepeatType() {
|
||||
return repeatType;
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getCustomDays() {
|
||||
return customDays;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.ttstd.dialer.alarmclock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class AlarmTimeCalculator {
|
||||
|
||||
/**
|
||||
* 根据重复配置,计算下一次闹钟应该响铃的绝对时间(毫秒)
|
||||
*
|
||||
* @param hour 闹钟设定的小时
|
||||
* @param minute 闹钟设定的分钟
|
||||
* @param config 重复配置
|
||||
* @return 下一次响铃的时间戳
|
||||
*/
|
||||
public static long calculateNextAlarmTime(int hour, int minute, AlarmRepeatConfig config) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
||||
Calendar target = Calendar.getInstance();
|
||||
target.set(Calendar.HOUR_OF_DAY, hour);
|
||||
target.set(Calendar.MINUTE, minute);
|
||||
target.set(Calendar.SECOND, 0);
|
||||
target.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
// 情况 1:只响一次
|
||||
if (config.getRepeatType() == AlarmRepeatConfig.REPEAT_ONCE) {
|
||||
// 如果设定的时间在今天已经过去了,就定在明天
|
||||
if (target.getTimeInMillis() <= now.getTimeInMillis()) {
|
||||
target.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
return target.getTimeInMillis();
|
||||
}
|
||||
|
||||
// 情况 2:每天
|
||||
if (config.getRepeatType() == AlarmRepeatConfig.REPEAT_EVERYDAY) {
|
||||
if (target.getTimeInMillis() <= now.getTimeInMillis()) {
|
||||
target.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
return target.getTimeInMillis();
|
||||
}
|
||||
|
||||
// 情况 3 & 4:周一至周五 或 自定义星期
|
||||
if (config.getRepeatType() == AlarmRepeatConfig.REPEAT_WEEKDAYS ||
|
||||
config.getRepeatType() == AlarmRepeatConfig.REPEAT_CUSTOM) {
|
||||
|
||||
ArrayList<Integer> targetDays = config.getCustomDays();
|
||||
|
||||
// 从今天开始,往后最多循环7天,找到最近的符合星期要求的日子
|
||||
for (int i = 0; i < 7; i++) {
|
||||
int currentDayOfWeek = target.get(Calendar.DAY_OF_WEEK);
|
||||
|
||||
// 如果 target 的日子在今天或今天之后,且其星期在用户选中的列表中
|
||||
if (targetDays.contains(currentDayOfWeek)) {
|
||||
// 如果刚好是今天,但时间已经过去了,则不能算今天,继续往后找
|
||||
if (i == 0 && target.getTimeInMillis() <= now.getTimeInMillis()) {
|
||||
target.add(Calendar.DAY_OF_MONTH, 1);
|
||||
continue;
|
||||
}
|
||||
// 找到了最近的匹配日期
|
||||
return target.getTimeInMillis();
|
||||
}
|
||||
// 否则,日期加一天,继续匹配
|
||||
target.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return target.getTimeInMillis();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.ttstd.dialer.base;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -12,16 +14,19 @@ import androidx.multidex.MultiDex;
|
||||
import com.alibaba.android.arouter.launcher.ARouter;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.kongzue.dialogx.DialogX;
|
||||
import com.tencent.bugly.crashreport.CrashReport;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.config.SystemIntentAction;
|
||||
import com.ttstd.dialer.manager.AppManager;
|
||||
import com.ttstd.dialer.manager.MapManager;
|
||||
import com.ttstd.dialer.manager.WeatherManager;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import com.ttstd.dialer.push.PushExecutor;
|
||||
import com.ttstd.dialer.receiver.AppChangedReceiver;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.NativeUtils;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
@@ -69,6 +74,22 @@ public class BaseApplication extends Application {
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
unregisterReceivers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
Logger.e(TAG, "init: ");
|
||||
Logger.e(TAG, "init: getNonce = " + NativeUtils.getNonce());
|
||||
@@ -93,6 +114,7 @@ public class BaseApplication extends Application {
|
||||
|
||||
// 初始化 Toast 框架
|
||||
Toaster.init(this);
|
||||
DialogX.init(this);
|
||||
|
||||
Logger.e(TAG, "slowInit: ");
|
||||
Aria.init(this);
|
||||
@@ -107,6 +129,7 @@ public class BaseApplication extends Application {
|
||||
|
||||
WeatherManager.init(this);
|
||||
IconCacheManager.init(this);
|
||||
registerReceivers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,5 +183,38 @@ public class BaseApplication extends Application {
|
||||
});
|
||||
}
|
||||
|
||||
private void registerReceivers() {
|
||||
registerAppChangedReceive();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void unregisterReceivers() {
|
||||
if (mAppChangedReceiver != null) {
|
||||
unregisterReceiver(mAppChangedReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
private AppChangedReceiver mAppChangedReceiver;
|
||||
|
||||
|
||||
private void registerAppChangedReceive() {
|
||||
if (null == mAppChangedReceiver) {
|
||||
mAppChangedReceiver = new AppChangedReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_INSTALL);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addAction(Intent.ACTION_MY_PACKAGE_REPLACED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
filter.addAction(SystemIntentAction.ACTION_PACKAGE_ENABLE_ROLLBACK);
|
||||
filter.addAction(SystemIntentAction.ACTION_CANCEL_ENABLE_ROLLBACK);
|
||||
filter.addAction(SystemIntentAction.ACTION_ROLLBACK_COMMITTED);
|
||||
filter.addDataScheme("package");
|
||||
registerReceiver(mAppChangedReceiver, filter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.ttstd.dialer.config;
|
||||
|
||||
public class LiveDataAction {
|
||||
public static final String ACTION_UPDATE_APPS = "update_apps";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ttstd.dialer.config;
|
||||
|
||||
import android.annotation.SystemApi;
|
||||
|
||||
public class SystemIntentAction {
|
||||
public static final String ACTION_PACKAGE_ENABLE_ROLLBACK = "android.intent.action.PACKAGE_ENABLE_ROLLBACK";
|
||||
public static final String ACTION_CANCEL_ENABLE_ROLLBACK = "android.intent.action.CANCEL_ENABLE_ROLLBACK";
|
||||
@SystemApi
|
||||
public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
|
||||
|
||||
}
|
||||
@@ -12,21 +12,21 @@ import java.util.List;
|
||||
public interface AppDao {
|
||||
// 基本查询:获取总行数
|
||||
@Query("SELECT COUNT(*) FROM app_list")
|
||||
Integer getTotalCount();
|
||||
int getTotalCount();
|
||||
|
||||
// 查询:根据特定列的非空值计数
|
||||
@Query("SELECT COUNT(id) FROM app_list")
|
||||
Integer getCountById();
|
||||
int getCountById();
|
||||
|
||||
@Query("SELECT * FROM app_list WHERE package_name = :packageName AND class_name = :className")
|
||||
AppInfo getAppInfo(String packageName, String className);
|
||||
|
||||
@Query("SELECT id FROM app_list WHERE package_name = :packageName AND class_name = :className")
|
||||
Integer getIdByPackageAndClass(String packageName, String className);
|
||||
int getIdByPackageAndClass(String packageName, String className);
|
||||
|
||||
// 检查数据是否存在(返回布尔值)
|
||||
@Query("SELECT COUNT(*) FROM app_list WHERE package_name = :pkgName AND class_name = :clsName")
|
||||
Integer checkAppInfoExists(String pkgName, String clsName);
|
||||
int checkAppInfoExists(String pkgName, String clsName);
|
||||
|
||||
@Insert
|
||||
long insert(AppInfo appInfo);
|
||||
@@ -35,16 +35,16 @@ public interface AppDao {
|
||||
long[] insert(List<AppInfo> appInfos);
|
||||
|
||||
@Update
|
||||
Integer update(AppInfo appInfo);
|
||||
int update(AppInfo appInfo);
|
||||
|
||||
@Delete
|
||||
Integer delete(AppInfo appInfo);
|
||||
int delete(AppInfo appInfo);
|
||||
|
||||
@Query("DELETE FROM app_list WHERE id = :id")
|
||||
Integer deleteById(Integer id);
|
||||
int deleteById(int id);
|
||||
|
||||
@Query("DELETE FROM app_list")
|
||||
Integer deleteAll();
|
||||
int deleteAll();
|
||||
|
||||
@Query("SELECT * FROM app_list ORDER BY position ASC")
|
||||
List<AppInfo> getAllApp();
|
||||
@@ -59,7 +59,7 @@ public interface AppDao {
|
||||
List<AppInfo> getInsideApp();
|
||||
|
||||
@Query("SELECT * FROM app_list WHERE id = :id")
|
||||
AppInfo getAppById(Integer id);
|
||||
AppInfo getAppById(int id);
|
||||
|
||||
@Query("SELECT * FROM app_list WHERE label LIKE :searchQuery OR package_name LIKE :searchQuery")
|
||||
List<AppInfo> searchApp(String searchQuery);
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.ttstd.dialer.fragment.dialog.shortcut;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmDialogFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentDialogMoveAppBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class MoveAppDialogFagment extends BaseMvvmDialogFragment<ShortcutViewModel, FragmentDialogMoveAppBinding> {
|
||||
private static final String TAG = "ShortcutDialogFagment";
|
||||
|
||||
private Activity mContext;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private AppInfo mAppInfo;
|
||||
private String mTitile;
|
||||
private String mTips;
|
||||
private String mNegativeText;
|
||||
private String mPositiveText;
|
||||
|
||||
public MoveAppDialogFagment() {
|
||||
}
|
||||
|
||||
public MoveAppDialogFagment(AppInfo appInfo) {
|
||||
mAppInfo = appInfo;
|
||||
}
|
||||
|
||||
public void setTitile(String titile) {
|
||||
mTitile = titile;
|
||||
}
|
||||
|
||||
public void setTips(String tips) {
|
||||
mTips = tips;
|
||||
}
|
||||
|
||||
public void setNegativeText(String negativeText) {
|
||||
mNegativeText = negativeText;
|
||||
}
|
||||
|
||||
public void setPositiveText(String positiveText) {
|
||||
mPositiveText = positiveText;
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onPositiveClick();
|
||||
|
||||
void onNegativeClick();
|
||||
}
|
||||
|
||||
private OnClickListener mOnClickListener;
|
||||
|
||||
public void setOnClickListener(OnClickListener onClickListener) {
|
||||
mOnClickListener = onClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_dialog_move_app;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mContext = getActivity();
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
mViewModel.setContext(mContext);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView(Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData(Bundle savedInstanceState) {
|
||||
if (TextUtils.isEmpty(mTitile)) {
|
||||
mViewDataBinding.tvTitle.setText("提示");
|
||||
} else {
|
||||
mViewDataBinding.tvTitle.setText(mTitile);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(mTips)) {
|
||||
mViewDataBinding.tvMessage.setText("");
|
||||
mViewDataBinding.tvMessage.setVisibility(View.GONE);
|
||||
} else {
|
||||
mViewDataBinding.tvMessage.setText(mTips);
|
||||
mViewDataBinding.tvMessage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(mNegativeText)) {
|
||||
mViewDataBinding.btnCancel.setText("取消");
|
||||
} else {
|
||||
mViewDataBinding.btnCancel.setText(mNegativeText);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(mPositiveText)) {
|
||||
mViewDataBinding.btnConfirm.setText("确定");
|
||||
} else {
|
||||
mViewDataBinding.btnConfirm.setText(mPositiveText);
|
||||
}
|
||||
|
||||
if (mAppInfo != null) {
|
||||
try {
|
||||
ActivityInfo info = mPackageManager.getActivityInfo(mAppInfo.getComponentName(), 0);
|
||||
mViewDataBinding.tvAppName.setText(info.loadLabel(mPackageManager));
|
||||
Drawable rawIcon = info.loadIcon(mPackageManager);
|
||||
mViewDataBinding.ivAppIcon.setImageDrawable(rawIcon);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (getDialog() != null) {
|
||||
Window window = getDialog().getWindow();
|
||||
if (window == null) return;
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
params.gravity = Gravity.CENTER;
|
||||
window.setAttributes(params);
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
getDialog().setCancelable(true);
|
||||
getDialog().setCanceledOnTouchOutside(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(FragmentManager manager, String tag) {
|
||||
DialogFragment fragment = (DialogFragment) manager.findFragmentByTag(tag);
|
||||
if (fragment != null && fragment.isAdded()
|
||||
&& fragment.getDialog() != null && fragment.getDialog().isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
FragmentTransaction ft = manager.beginTransaction();
|
||||
ft.add(this, tag);
|
||||
ft.commitAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "show: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchData() {
|
||||
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void onPositive(View view) {
|
||||
if (mOnClickListener != null) {
|
||||
mOnClickListener.onPositiveClick();
|
||||
}
|
||||
}
|
||||
|
||||
public void onNegative(View view) {
|
||||
if (mOnClickListener != null) {
|
||||
mOnClickListener.onNegativeClick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private AppInfo mAppInfo;
|
||||
private String mTitil;
|
||||
private String mTitile;
|
||||
private String mTips;
|
||||
private String mNegativeText;
|
||||
private String mPositiveText;
|
||||
@@ -42,8 +42,8 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
|
||||
mAppInfo = appInfo;
|
||||
}
|
||||
|
||||
public void setTitil(String titil) {
|
||||
mTitil = titil;
|
||||
public void setTitile(String titile) {
|
||||
mTitile = titile;
|
||||
}
|
||||
|
||||
public void setTips(String tips) {
|
||||
@@ -92,10 +92,10 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
|
||||
|
||||
@Override
|
||||
protected void initData(Bundle savedInstanceState) {
|
||||
if (TextUtils.isEmpty(mTitil)) {
|
||||
if (TextUtils.isEmpty(mTitile)) {
|
||||
mViewDataBinding.tvTitle.setText("提示");
|
||||
} else {
|
||||
mViewDataBinding.tvTitle.setText(mTitil);
|
||||
mViewDataBinding.tvTitle.setText(mTitile);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(mTips)) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.databinding.FragmentHomeBinding;
|
||||
import com.ttstd.dialer.manager.WeatherManager;
|
||||
import com.ttstd.dialer.manager.WeatherUpdateManager;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.DateUtil;
|
||||
@@ -110,6 +111,7 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
mFestivalUtils = new LunarCalendarFestivalUtils();
|
||||
weatherUpdateManager = WeatherUpdateManager.Companion.getInstance();
|
||||
observeWeatherUpdates();
|
||||
WeatherManager.getInstance().refreshweather();
|
||||
setTime();
|
||||
}
|
||||
|
||||
@@ -163,6 +165,7 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
if (weatherNowResponse != null) {
|
||||
Logger.e(TAG, "observeWeatherUpdates", "weatherNowResponse = " + weatherNowResponse);
|
||||
mViewDataBinding.tvWeather.setText(weatherNowResponse.getNow().getText());
|
||||
mViewDataBinding.tvTempCurrent.setText(weatherNowResponse.getNow().getTemp() + "°");
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -180,10 +183,11 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
Logger.e(TAG, "observeWeatherUpdates", "weatherDailyResponse = " + weatherDailyResponse);
|
||||
|
||||
WeatherDaily weatherDaily = weatherDailyResponse.getDaily().get(0);
|
||||
mViewDataBinding.tvTemp.setText(weatherDaily.getTempMin() + "°/" + weatherDaily.getTempMax() + "°");
|
||||
String iconDay = weatherDaily.getIconDay();
|
||||
// 获取资源ID
|
||||
String fileName = "qweather_" + iconDay; // 替换为你的文件名
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "raw", mContext.getPackageName());
|
||||
int resId = mContext.getResources().getIdentifier(fileName, "drawable", mContext.getPackageName());
|
||||
if (resId != 0) {
|
||||
mViewDataBinding.ivQweatherIcon.setImageDrawable(mContext.getDrawable(resId));
|
||||
} else {
|
||||
|
||||
67
app/src/main/java/com/ttstd/dialer/livedata/LiveDataBus.java
Normal file
67
app/src/main/java/com/ttstd/dialer/livedata/LiveDataBus.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package com.ttstd.dialer.livedata;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public final class LiveDataBus {
|
||||
|
||||
private static final class Holder {
|
||||
static final LiveDataBus INSTANCE = new LiveDataBus();
|
||||
}
|
||||
|
||||
public static LiveDataBus get() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
// 每个事件类型 → 一个唯一的 key
|
||||
private final Map<String, MutableLiveData<Object>> bus = new HashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> MutableLiveData<T> liveData(String key) {
|
||||
synchronized (bus) {
|
||||
MutableLiveData<Object> ld = bus.get(key);
|
||||
if (ld == null) {
|
||||
// singleLiveEvent 风格:只消费一次
|
||||
ld = new SingleLiveData<>();
|
||||
bus.put(key, ld);
|
||||
}
|
||||
return (MutableLiveData<T>) ld;
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void send(String key, T value) {
|
||||
liveData(key).postValue(value);
|
||||
}
|
||||
|
||||
public <T> LiveData<T> on(String key) {
|
||||
return liveData(key);
|
||||
}
|
||||
|
||||
// ===== 只消费一次的 SingleLiveData =====
|
||||
private static class SingleLiveData<T> extends MutableLiveData<T> {
|
||||
private final AtomicBoolean pending = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void observe(@NonNull LifecycleOwner owner,
|
||||
@NonNull Observer<? super T> observer) {
|
||||
super.observe(owner, t -> {
|
||||
if (pending.compareAndSet(true, false)) {
|
||||
observer.onChanged(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T value) {
|
||||
pending.set(true);
|
||||
super.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,11 @@ import android.util.Log;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.bean.ApkInstalledInfo;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.config.LiveDataAction;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.db.app.AppRepository;
|
||||
import com.ttstd.dialer.gson.GsonUtils;
|
||||
import com.ttstd.dialer.livedata.LiveDataBus;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.HashUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
@@ -398,23 +400,73 @@ public class AppManager {
|
||||
}
|
||||
|
||||
public void updateApp(String packageName) {
|
||||
List<ResolveInfo> resolveInfos = ApkUtils.getResolveInfoByPackageName(mContext, packageName);
|
||||
if (resolveInfos.isEmpty()) return;
|
||||
boolean isInstalled = ApkUtils.isInstalled(mContext, packageName);
|
||||
|
||||
resolveInfos.stream()
|
||||
.map(this::resolveInfoToDesktopApp)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(app -> !isAppExists(app))
|
||||
.forEach(app -> {
|
||||
app.setPosition(mAppRepository.getTotalCount());
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "更新应用插入失败: " + app.getPackageName(), e);
|
||||
if (isInstalled) {
|
||||
Logger.i(TAG, "应用已安装: " + packageName + ", 开始更新数据库");
|
||||
|
||||
List<ResolveInfo> resolveInfos = ApkUtils.getResolveInfoByPackageName(mContext, packageName);
|
||||
|
||||
if (!resolveInfos.isEmpty()) {
|
||||
resolveInfos.stream()
|
||||
.map(this::resolveInfoToDesktopApp)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(app -> {
|
||||
try {
|
||||
int existingId = mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
|
||||
|
||||
if (existingId > 0) {
|
||||
// app.setId(existingId);
|
||||
// mAppRepository.update(app);
|
||||
// Logger.i(TAG, "更新应用信息: " + app.getPackageName());
|
||||
} else {
|
||||
app.setOutside(1);
|
||||
app.setPosition(mAppRepository.getTotalCount());
|
||||
mAppRepository.insert(app);
|
||||
Logger.i(TAG, "新增应用信息: " + app.getPackageName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "处理应用失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Logger.w(TAG, "应用已安装但无可启动的 Launcher Activity");
|
||||
}
|
||||
} else {
|
||||
Logger.i(TAG, "应用未安装或被禁用: " + packageName + ", 开始删除数据库数据");
|
||||
|
||||
try {
|
||||
List<AppInfo> allApps = mAppRepository.getAllApp();
|
||||
if (allApps != null) {
|
||||
List<Integer> idsToDelete = allApps.stream()
|
||||
.filter(app -> packageName.equals(app.getPackageName()))
|
||||
.map(AppInfo::getId)
|
||||
.filter(id -> id > 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!idsToDelete.isEmpty()) {
|
||||
idsToDelete.forEach(id -> {
|
||||
try {
|
||||
mAppRepository.deleteById(id);
|
||||
Logger.i(TAG, "删除应用数据库记录, ID: " + id);
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "删除应用记录失败, ID: " + id, e);
|
||||
}
|
||||
});
|
||||
Logger.i(TAG, "成功删除 " + idsToDelete.size() + " 条记录");
|
||||
} else {
|
||||
Logger.i(TAG, "数据库中未找到该应用的记录");
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "删除应用记录时发生异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
LiveDataBus.get().send(LiveDataAction.ACTION_UPDATE_APPS, TAG);
|
||||
}
|
||||
|
||||
|
||||
// 重构getAllApp方法,复用现有处理逻辑
|
||||
public void refreshAllApps() {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
|
||||
@@ -77,7 +77,6 @@ public class WeatherManager {
|
||||
WeatherNowResponse weatherNowResponse = getWeatherNowCache();
|
||||
if (weatherNowResponse != null) {
|
||||
mWeatherUpdateManager.publishWeatherNowUpdate(weatherNowResponse);
|
||||
|
||||
}
|
||||
WeatherHourlyResponse weatherHourlyResponse = getWeather24hCache();
|
||||
if (weatherHourlyResponse != null) {
|
||||
|
||||
@@ -101,4 +101,4 @@ class WeatherUpdateManager {
|
||||
.onEach { observer(it) }
|
||||
.launchIn(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
259
app/src/main/java/com/ttstd/dialer/network/OperationResult.java
Normal file
259
app/src/main/java/com/ttstd/dialer/network/OperationResult.java
Normal file
@@ -0,0 +1,259 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class OperationResult {
|
||||
/**
|
||||
* 操作状态枚举
|
||||
*/
|
||||
public enum Status {
|
||||
SUCCESS, // 成功
|
||||
NETWORK_ERROR, // 网络错误
|
||||
SERVER_ERROR, // 服务器错误
|
||||
LOCAL_ERROR, // 本地错误
|
||||
SYNC_PARTIAL_SUCCESS // 部分成功(如:网络失败但本地成功)
|
||||
}
|
||||
|
||||
private Status status;
|
||||
private String errorCode;
|
||||
private String errorMessage;
|
||||
|
||||
// 云端ID(在线同步返回)
|
||||
private Long cloudId;
|
||||
|
||||
// 本地ID(本地数据库返回)
|
||||
private Long localId;
|
||||
|
||||
// 数据来源标识
|
||||
private DataSource dataSource;
|
||||
|
||||
// 是否需要重试
|
||||
private boolean needRetry;
|
||||
|
||||
// 原始数据对象(可选)
|
||||
private Object rawData;
|
||||
|
||||
public enum DataSource {
|
||||
CLOUD, // 来自云端
|
||||
LOCAL, // 来自本地
|
||||
BOTH, // 两者都有
|
||||
UNKNOWN // 未知
|
||||
}
|
||||
|
||||
private OperationResult() {
|
||||
}
|
||||
|
||||
// ==================== 静态工厂方法 ====================
|
||||
|
||||
/**
|
||||
* 创建成功的结果(云端)
|
||||
*/
|
||||
public static OperationResult successFromCloud(Long cloudId) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.SUCCESS;
|
||||
result.cloudId = cloudId;
|
||||
result.dataSource = DataSource.CLOUD;
|
||||
result.needRetry = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成功的结果(本地)
|
||||
*/
|
||||
public static OperationResult successFromLocal(Long localId) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.SUCCESS;
|
||||
result.localId = localId;
|
||||
result.dataSource = DataSource.LOCAL;
|
||||
result.needRetry = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成功的结果(云端+本地)
|
||||
*/
|
||||
public static OperationResult successBoth(Long cloudId, Long localId) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.SUCCESS;
|
||||
result.cloudId = cloudId;
|
||||
result.localId = localId;
|
||||
result.dataSource = DataSource.BOTH;
|
||||
result.needRetry = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建部分成功的结果(网络失败但本地成功)
|
||||
*/
|
||||
public static OperationResult partialSuccess(Long localId, String errorMsg) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.SYNC_PARTIAL_SUCCESS;
|
||||
result.localId = localId;
|
||||
result.errorMessage = errorMsg;
|
||||
result.dataSource = DataSource.LOCAL;
|
||||
result.needRetry = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建网络错误的结果
|
||||
*/
|
||||
public static OperationResult networkError(String errorMsg) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.NETWORK_ERROR;
|
||||
result.errorCode = "NETWORK_ERROR";
|
||||
result.errorMessage = errorMsg;
|
||||
result.dataSource = DataSource.UNKNOWN;
|
||||
result.needRetry = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建服务器错误的结果
|
||||
*/
|
||||
public static OperationResult serverError(String errorCode, String errorMsg) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.SERVER_ERROR;
|
||||
result.errorCode = errorCode;
|
||||
result.errorMessage = errorMsg;
|
||||
result.dataSource = DataSource.UNKNOWN;
|
||||
result.needRetry = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建本地错误的结果
|
||||
*/
|
||||
public static OperationResult localError(String errorMsg) {
|
||||
OperationResult result = new OperationResult();
|
||||
result.status = Status.LOCAL_ERROR;
|
||||
result.errorCode = "LOCAL_ERROR";
|
||||
result.errorMessage = errorMsg;
|
||||
result.dataSource = DataSource.UNKNOWN;
|
||||
result.needRetry = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== Getter/Setter ====================
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(String errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public Long getCloudId() {
|
||||
return cloudId;
|
||||
}
|
||||
|
||||
public void setCloudId(Long cloudId) {
|
||||
this.cloudId = cloudId;
|
||||
}
|
||||
|
||||
public Long getLocalId() {
|
||||
return localId;
|
||||
}
|
||||
|
||||
public void setLocalId(Long localId) {
|
||||
this.localId = localId;
|
||||
}
|
||||
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public boolean isNeedRetry() {
|
||||
return needRetry;
|
||||
}
|
||||
|
||||
public void setNeedRetry(boolean needRetry) {
|
||||
this.needRetry = needRetry;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getRawData() {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public void setRawData(@Nullable Object rawData) {
|
||||
this.rawData = rawData;
|
||||
}
|
||||
|
||||
// ==================== 便捷判断方法 ====================
|
||||
|
||||
/**
|
||||
* 是否完全成功
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return status == Status.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有错误
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return status != Status.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否来自云端
|
||||
*/
|
||||
public boolean isFromCloud() {
|
||||
return dataSource == DataSource.CLOUD || dataSource == DataSource.BOTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否来自本地
|
||||
*/
|
||||
public boolean isFromLocal() {
|
||||
return dataSource == DataSource.LOCAL || dataSource == DataSource.BOTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效的ID(优先云端,其次本地)
|
||||
*/
|
||||
public Long getEffectiveId() {
|
||||
if (cloudId != null) {
|
||||
return cloudId;
|
||||
}
|
||||
return localId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OperationResult{" +
|
||||
"status=" + status +
|
||||
", errorCode='" + errorCode + '\'' +
|
||||
", errorMessage='" + errorMessage + '\'' +
|
||||
", cloudId=" + cloudId +
|
||||
", localId=" + localId +
|
||||
", dataSource=" + dataSource +
|
||||
", needRetry=" + needRetry +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.ttstd.dialer.receiver;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.ttstd.dialer.activity.alarm.AlarmAlertActivity;
|
||||
import com.ttstd.dialer.alarmclock.AlarmManagerHelper;
|
||||
import com.ttstd.dialer.alarmclock.AlarmRepeatConfig;
|
||||
import com.ttstd.dialer.alarmclock.AlarmTimeCalculator;
|
||||
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
private static final String CHANNEL_ID = "alarm_channel";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// 1. 执行原有的响铃和弹出通知/界面的逻辑
|
||||
// (调用上一次回答中创建的通知栏或全屏拉起逻辑)
|
||||
showAlarmNotification(context);
|
||||
|
||||
// 2. 核心:动态轮转,实现重复闹钟
|
||||
scheduleNextRepeatAlarm(context);
|
||||
}
|
||||
|
||||
private void scheduleNextRepeatAlarm(Context context) {
|
||||
// 从 SharedPreferences 中取出用户之前保存的闹钟时间和重复设置
|
||||
// 实际开发中此处可以替换为 SQLite 数据库
|
||||
SharedPreferences sp = context.getSharedPreferences("AlarmPrefs", Context.MODE_PRIVATE);
|
||||
int hour = sp.getInt("alarm_hour", 8);
|
||||
int minute = sp.getInt("alarm_minute", 0);
|
||||
int repeatType = sp.getInt("repeat_type", AlarmRepeatConfig.REPEAT_ONCE);
|
||||
|
||||
// 如果是“只响一次”,响完就结束了,不需要再设置
|
||||
if (repeatType == AlarmRepeatConfig.REPEAT_ONCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是 每天 或 周一至周五
|
||||
AlarmRepeatConfig config = new AlarmRepeatConfig(repeatType);
|
||||
|
||||
// 重新计算下一次的时间(由于此时已经过了当前闹钟点,计算出的必定是未来的时间)
|
||||
long nextTriggerTime = AlarmTimeCalculator.calculateNextAlarmTime(hour, minute, config);
|
||||
|
||||
// 再次调用第一步写好的精准闹钟设置工具,埋下新的定时炸弹
|
||||
AlarmManagerHelper.setExactAlarm(context, nextTriggerTime);
|
||||
}
|
||||
|
||||
private void showAlarmNotification(Context context) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null) return;
|
||||
|
||||
// 1. 创建通知渠道 (Android 8.0+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID, "闹钟响铃", NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription("用于展示闹钟响铃界面");
|
||||
channel.enableLights(true);
|
||||
channel.setBypassDnd(true); // 绕过免打扰
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// 2. 构建点击通知或全屏弹出的 Intent
|
||||
Intent alertIntent = new Intent(context, AlarmAlertActivity.class);
|
||||
alertIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE :
|
||||
PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, alertIntent, flags);
|
||||
|
||||
// 3. 构建高优先级通知(兼容 Android 10+ 后台全屏拉起)
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.ic_lock_idle_alarm)
|
||||
.setContentTitle("闹钟响了")
|
||||
.setContentText("时间到了,快起床!")
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_ALARM)
|
||||
.setAutoCancel(true)
|
||||
.setFullScreenIntent(pendingIntent, true); // 核心:锁屏时直接拉起 Activity
|
||||
|
||||
notificationManager.notify(1, builder.build());
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,24 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.manager.AppManager;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AppChangedReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "ApkInstallReceiver";
|
||||
private static final String TAG = "AppChangedReceiver";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
// 创建一个单线程池用于处理应用变更事件
|
||||
private static final ExecutorService sExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
@@ -35,6 +43,13 @@ public class AppChangedReceiver extends BroadcastReceiver {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sExecutor.execute(() -> {
|
||||
try {
|
||||
AppManager.getInstance().updateApp(packageName);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onReceive: updateApp " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -94,7 +95,7 @@ public class ApkUtils {
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Logger.e(TAG, "openApp: " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -154,6 +154,21 @@ public class GlideUtils {
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
public static void loadImageSafe(@Nullable Context context, int id,
|
||||
@NonNull ImageView imageView,
|
||||
@DrawableRes int errorId) {
|
||||
RequestBuilder<Drawable> requestManager = getSafeRequestManager(context);
|
||||
if (requestManager == null) {
|
||||
Logger.w(TAG, "Skip loading image due to invalid context");
|
||||
return;
|
||||
}
|
||||
|
||||
requestManager
|
||||
.load(id)
|
||||
.error(errorId)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
public static void loadImageSafe(@Nullable Context context, @Nullable String url,
|
||||
@NonNull ImageView imageView,
|
||||
Drawable drawable) {
|
||||
|
||||
@@ -62,7 +62,7 @@ public class ClearEditText extends AppCompatEditText {
|
||||
}
|
||||
|
||||
private void updateClearButton() {
|
||||
Drawable endDrawable = length() > 0 ? clearDrawable : null;
|
||||
Drawable endDrawable = (length() > 0 && hasFocus()) ? clearDrawable : null;
|
||||
setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, endDrawable, null);
|
||||
}
|
||||
|
||||
|
||||
117
app/src/main/java/com/ttstd/dialer/view/GlideEngine.java
Normal file
117
app/src/main/java/com/ttstd/dialer/view/GlideEngine.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.ttstd.dialer.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||
import com.luck.picture.lib.engine.ImageEngine;
|
||||
import com.luck.picture.lib.utils.ActivityCompatHelper;
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
/**
|
||||
* @author:luck
|
||||
* @date:2019-11-13 17:02
|
||||
* @describe:Glide加载引擎
|
||||
*/
|
||||
public class GlideEngine implements ImageEngine {
|
||||
|
||||
/**
|
||||
* 加载图片
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param url 资源url
|
||||
* @param imageView 图片承载控件
|
||||
*/
|
||||
@Override
|
||||
public void loadImage(Context context, String url, ImageView imageView) {
|
||||
if (!ActivityCompatHelper.assertValidRequest(context)) {
|
||||
return;
|
||||
}
|
||||
Glide.with(context)
|
||||
.load(url)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) {
|
||||
if (!ActivityCompatHelper.assertValidRequest(context)) {
|
||||
return;
|
||||
}
|
||||
Glide.with(context)
|
||||
.load(url)
|
||||
.override(maxWidth, maxHeight)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载相册目录封面
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param url 图片路径
|
||||
* @param imageView 承载图片ImageView
|
||||
*/
|
||||
@Override
|
||||
public void loadAlbumCover(Context context, String url, ImageView imageView) {
|
||||
if (!ActivityCompatHelper.assertValidRequest(context)) {
|
||||
return;
|
||||
}
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.override(180, 180)
|
||||
.sizeMultiplier(0.5f)
|
||||
.transform(new CenterCrop(), new RoundedCorners(8))
|
||||
.placeholder(R.drawable.ps_image_placeholder)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加载图片列表图片
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param url 图片路径
|
||||
* @param imageView 承载图片ImageView
|
||||
*/
|
||||
@Override
|
||||
public void loadGridImage(Context context, String url, ImageView imageView) {
|
||||
if (!ActivityCompatHelper.assertValidRequest(context)) {
|
||||
return;
|
||||
}
|
||||
Glide.with(context)
|
||||
.load(url)
|
||||
.override(200, 200)
|
||||
.centerCrop()
|
||||
.placeholder(R.drawable.ps_image_placeholder)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseRequests(Context context) {
|
||||
if (!ActivityCompatHelper.assertValidRequest(context)) {
|
||||
return;
|
||||
}
|
||||
Glide.with(context).pauseRequests();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeRequests(Context context) {
|
||||
if (!ActivityCompatHelper.assertValidRequest(context)) {
|
||||
return;
|
||||
}
|
||||
Glide.with(context).resumeRequests();
|
||||
}
|
||||
|
||||
private GlideEngine() {
|
||||
}
|
||||
|
||||
private static final class InstanceHolder {
|
||||
static final GlideEngine instance = new GlideEngine();
|
||||
}
|
||||
|
||||
public static GlideEngine createGlideEngine() {
|
||||
return InstanceHolder.instance;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -74,6 +73,10 @@ public class SettingItem extends ConstraintLayout {
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return tb.isToggleOn();
|
||||
}
|
||||
|
||||
public SettingItem(@NonNull @NotNull Context context) {
|
||||
super(context);
|
||||
init(context, null);
|
||||
@@ -215,6 +218,12 @@ public class SettingItem extends ConstraintLayout {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
tb.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
BIN
app/src/main/res/drawable-xxhdpi/ps_ic_placeholder.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ps_ic_placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
12
app/src/main/res/drawable/bg_app_info_card.xml
Normal file
12
app/src/main/res/drawable/bg_app_info_card.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<solid android:color="#FAFBFC" />
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E5E7EB" />
|
||||
|
||||
<corners android:radius="18dp" />
|
||||
|
||||
</shape>
|
||||
34
app/src/main/res/drawable/bg_btn_cancel.xml
Normal file
34
app/src/main/res/drawable/bg_btn_cancel.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- 按下状态:略微加深 -->
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<!-- 上下渐变 -->
|
||||
<gradient android:angle="270" android:startColor="#E8EBF0" android:endColor="#F5F7FA" />
|
||||
|
||||
<!-- 浅色边框 -->
|
||||
<stroke android:width="1dp" android:color="#E8EBF0" />
|
||||
|
||||
<corners android:radius="28dp" />
|
||||
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- 默认状态 -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<!-- 上亮下浅灰,接近图中白色按钮 -->
|
||||
<gradient android:angle="270" android:startColor="#FFFFFF" android:endColor="#F1F3F7" />
|
||||
|
||||
<!-- 浅色边框 -->
|
||||
<stroke android:width="1dp" android:color="#E8EBF0" />
|
||||
|
||||
<corners android:radius="28dp" />
|
||||
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
34
app/src/main/res/drawable/bg_btn_confirm.xml
Normal file
34
app/src/main/res/drawable/bg_btn_confirm.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- 按下状态:蓝色加深 -->
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<!-- 上下渐变 -->
|
||||
<gradient android:angle="270" android:startColor="#1D5FEF" android:endColor="#0E4FD8" />
|
||||
|
||||
<!-- 浅色蓝边框 -->
|
||||
<stroke android:width="1dp" android:color="#5B95FF" />
|
||||
|
||||
<corners android:radius="28dp" />
|
||||
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- 默认状态 -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<!-- 上亮下深,接近图中蓝色按钮 -->
|
||||
<gradient android:angle="270" android:startColor="#3F8BFF" android:endColor="#176CFF" />
|
||||
|
||||
<!-- 浅色边框 -->
|
||||
<stroke android:width="1dp" android:color="#76A9FF" />
|
||||
|
||||
<corners android:radius="28dp" />
|
||||
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/bg_dialog_card.xml
Normal file
5
app/src/main/res/drawable/bg_dialog_card.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:radius="28dp" />
|
||||
</shape>
|
||||
11
app/src/main/res/drawable/bg_tip_icon_circle.xml
Normal file
11
app/src/main/res/drawable/bg_tip_icon_circle.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="#EEF5FF" />
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#DCEBFF" />
|
||||
|
||||
</shape>
|
||||
@@ -5,8 +5,8 @@
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M105,480a8,8 0,0 1,8 -8h799a8,8 0,0 1,8 8v64a8,8 0,0 1,-8 8H113a8,8 0,0 1,-8 -8v-64z"
|
||||
android:fillColor="#323338"/>
|
||||
android:fillColor="#ffffff" />
|
||||
<path
|
||||
android:pathData="M480,920a8,8 0,0 1,-8 -8V112a8,8 0,0 1,8 -8h64a8,8 0,0 1,8 8v800a8,8 0,0 1,-8 8h-64z"
|
||||
android:fillColor="#323338"/>
|
||||
android:fillColor="#ffffff" />
|
||||
</vector>
|
||||
|
||||
16
app/src/main/res/drawable/ps_image_placeholder.xml
Normal file
16
app/src/main/res/drawable/ps_image_placeholder.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<size
|
||||
android:width="100dp"
|
||||
android:height="100dp" />
|
||||
<solid android:color="@color/ps_color_light_grey" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ps_ic_placeholder" />
|
||||
</item>
|
||||
</layer-list>
|
||||
22
app/src/main/res/layout/activity_alarm_alert.xml
Normal file
22
app/src/main/res/layout/activity_alarm_alert.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FF3333"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="闹钟响铃中..."
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_stop_alarm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="50dp"
|
||||
android:text="关闭闹钟" />
|
||||
</LinearLayout>
|
||||
@@ -78,6 +78,9 @@
|
||||
android:layout_height="@dimen/dp_100"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_32"
|
||||
android:adjustViewBounds="true"
|
||||
android:onClick="@{click::openSelector}"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_default_avatar"
|
||||
app:is_circle="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@@ -250,6 +253,44 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_56"
|
||||
android:layout_marginTop="@dimen/dp_8">
|
||||
|
||||
<ImageView
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_pinned"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/dp_8"
|
||||
android:text="联系人置顶"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_20"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sb_pinned"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.suke.widget.SwitchButton
|
||||
android:id="@+id/sb_pinned"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:sb_checked="true"
|
||||
app:sb_effect_duration="200"
|
||||
app:sb_show_indicator="false" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
@@ -91,8 +91,8 @@
|
||||
<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_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
@@ -161,6 +161,13 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:text="关于"
|
||||
android:layout_gravity="center"
|
||||
android:onClick="@{click::aboutUs}"
|
||||
android:textColor="@color/black"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
162
app/src/main/res/layout/fragment_dialog_move_app.xml
Normal file
162
app/src/main/res/layout/fragment_dialog_move_app.xml
Normal file
@@ -0,0 +1,162 @@
|
||||
<?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=".fragment.dialog.shortcut.MoveAppDialogFagment">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="click"
|
||||
type="com.ttstd.dialer.fragment.dialog.shortcut.MoveAppDialogFagment.BtnClick" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="@dimen/dp_320"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_dialog_card"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="28dp"
|
||||
android:paddingTop="32dp"
|
||||
android:paddingRight="28dp"
|
||||
android:paddingBottom="28dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<!-- 顶部提示图标 -->
|
||||
<FrameLayout
|
||||
android:layout_width="74dp"
|
||||
android:layout_height="74dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/bg_tip_icon_circle">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="!"
|
||||
android:textColor="#2F7BFF"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- 标题 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="22dp"
|
||||
android:gravity="center"
|
||||
android:text="温馨提示"
|
||||
android:textColor="#1F2430"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- 主提示 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:gravity="center"
|
||||
android:text="是否将应用放入更多应用"
|
||||
android:textColor="#2B2F38"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<!-- 说明文字 -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="将应用放入「更多应用」,可在应用抽屉中查看和管理,避免桌面空间不足。"
|
||||
android:textColor="#8A909D"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<!-- 应用信息区域 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="88dp"
|
||||
android:layout_marginTop="28dp"
|
||||
android:background="@drawable/bg_app_info_card"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_app_icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="38dp"
|
||||
android:layout_marginLeft="18dp"
|
||||
android:layout_marginRight="18dp"
|
||||
android:background="#E5E7EB" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="IndexableRecyclerView"
|
||||
android:textColor="#2B2F38"
|
||||
android:textSize="19sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 按钮区域 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="54dp"
|
||||
android:layout_marginTop="26dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnCancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_btn_cancel"
|
||||
android:gravity="center"
|
||||
android:text="取消"
|
||||
android:textColor="#4B5563"
|
||||
android:textSize="18sp"
|
||||
android:onClick="@{click::onNegative}"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnConfirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_btn_confirm"
|
||||
android:gravity="center"
|
||||
android:text="确定"
|
||||
android:onClick="@{click::onPositive}"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="@dimen/dp_300"
|
||||
android:layout_height="@dimen/dp_240"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@drawable/default_dialog_background"
|
||||
android:orientation="vertical"
|
||||
@@ -28,9 +28,8 @@
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -42,6 +41,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_24"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_18"
|
||||
@@ -54,108 +54,168 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp_16"
|
||||
android:visibility="visible"
|
||||
tools:text="消息" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@+id/linearLayout2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<com.shehuan.niv.NiceImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="@dimen/dp_40"
|
||||
android:layout_height="@dimen/dp_40"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
app:layout_constraintBottom_toTopOf="@+id/linearLayout2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_app_icon"
|
||||
android:layout_width="@dimen/dp_16"
|
||||
android:layout_height="@dimen/dp_16"
|
||||
android:layout_marginStart="@dimen/dp_26"
|
||||
android:layout_marginTop="@dimen/dp_26"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="@+id/iv_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
<com.shehuan.niv.NiceImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="@dimen/dp_64"
|
||||
android:layout_height="@dimen/dp_64"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_name"
|
||||
<ImageView
|
||||
android:id="@+id/iv_app_icon"
|
||||
android:layout_width="@dimen/dp_16"
|
||||
android:layout_height="@dimen/dp_16"
|
||||
android:layout_marginStart="@dimen/dp_52"
|
||||
android:layout_marginTop="@dimen/dp_52"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="@+id/iv_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_20"
|
||||
android:layout_marginTop="@dimen/dp_8"
|
||||
android:layout_marginEnd="@dimen/dp_20"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="@dimen/dp_3"
|
||||
android:maxLines="1"
|
||||
android:textColor="#676767"
|
||||
android:textSize="@dimen/sp_18"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iv_icon"
|
||||
tools:text="app" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_20"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
android:layout_marginEnd="@dimen/dp_20"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="@dimen/dp_3"
|
||||
android:maxLines="1"
|
||||
android:textColor="#676767"
|
||||
android:textSize="@dimen/sp_14"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iv_icon"
|
||||
tools:text="app" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_24"
|
||||
android:layout_marginEnd="@dimen/dp_24"
|
||||
android:layout_marginBottom="@dimen/dp_20"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/default_negative_background"
|
||||
android:gravity="center"
|
||||
android:onClick="@{click::onNegative}"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_14"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="取消" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/default_positive_background"
|
||||
android:gravity="center"
|
||||
android:onClick="@{click::onPositive}"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_14"
|
||||
android:layout_marginStart="@dimen/dp_24"
|
||||
android:layout_marginEnd="@dimen/dp_24"
|
||||
android:layout_marginTop="@dimen/dp_20"
|
||||
android:layout_marginBottom="@dimen/dp_20"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="确定" />
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<!-- 编辑按钮 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_44"
|
||||
android:background="@drawable/tv_edit_background"
|
||||
android:onClick="@{click::onPositive}">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:src="@drawable/ic_edit"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_positive"
|
||||
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="#1E88E5"
|
||||
android:textSize="@dimen/sp_15"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- 删除按钮 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_44"
|
||||
android:layout_marginTop="@dimen/dp_12"
|
||||
android:background="@drawable/tv_delete_background"
|
||||
android:onClick="@{click::onNegative}">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_delete"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:src="@drawable/ic_delete"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_negative"
|
||||
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="#D32F2F"
|
||||
android:textSize="@dimen/sp_15"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/iv_delete"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
android:layout_height="@dimen/dp_68"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/qweather_100"
|
||||
android:src="@drawable/ic_not_applicable"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
rootProject.name='拨号助手'
|
||||
rootProject.name = '拨号助手'
|
||||
include ':app', ':niceimageview', ':iconloader'
|
||||
|
||||
Reference in New Issue
Block a user