feat: 系统功能抽象
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
package com.ttstd.dialer.activity.app;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
@@ -13,21 +10,16 @@ import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityAppListBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.db.app.AppRepository;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, ActivityEvent> {
|
||||
@@ -55,23 +47,23 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
|
||||
.subscribe(new Observer<List<AppInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getDbAppList", "onSubscribe: ");
|
||||
Logger.e("getDbAppList", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull List<AppInfo> appInfos) {
|
||||
Log.e("getDbAppList", "onNext: " + appInfos);
|
||||
Logger.e("getDbAppList", "onNext: " + appInfos);
|
||||
mDesktopSortAppData.setValue(appInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getDbAppList", "onError: " + e.getMessage());
|
||||
Logger.e("getDbAppList", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getDbAppList", "onComplete: ");
|
||||
Logger.e("getDbAppList", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -90,23 +82,23 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
|
||||
.subscribe(new Observer<Integer>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("updateAppInfo", "onSubscribe: ");
|
||||
Logger.e("updateAppInfo", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull Integer row) {
|
||||
Log.e("updateAppInfo", "onNext: " + row);
|
||||
Logger.e("updateAppInfo", "onNext: " + row);
|
||||
mAppUpdateData.setValue(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("updateAppInfo", "onError: " + e.getMessage());
|
||||
Logger.e("updateAppInfo", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("updateAppInfo", "onComplete: ");
|
||||
Logger.e("updateAppInfo", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.activity.contact.add;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
@@ -11,6 +10,7 @@ import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityContactAddBinding;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.db.contact.ContactRepository;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
@@ -39,7 +39,7 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
|
||||
public void subscribe(@NonNull ObservableEmitter<Long> emitter) throws Throwable {
|
||||
int count = mRepository.getTotalCount() + 1;
|
||||
ContactInfo contactInfo = new ContactInfo(name, phone, count);
|
||||
Log.e(TAG, "saveContact: " + contactInfo);
|
||||
Logger.e(TAG, "saveContact: " + contactInfo);
|
||||
emitter.onNext(mRepository.insert(contactInfo));
|
||||
emitter.onComplete();
|
||||
}
|
||||
@@ -49,23 +49,23 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
|
||||
.subscribe(new Observer<Long>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("saveContact", "onSubscribe: ");
|
||||
Logger.e("saveContact", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull Long aLong) {
|
||||
Log.e("saveContact", "onNext: " + aLong);
|
||||
Logger.e("saveContact", "onNext: " + aLong);
|
||||
mIntegerMutableLiveData.setValue(aLong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("saveContact", "onError: " + e.getMessage());
|
||||
Logger.e("saveContact", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("saveContact", "onComplete: ");
|
||||
Logger.e("saveContact", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.ttstd.dialer.activity.contact.list;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -17,6 +16,7 @@ import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.databinding.ActivityContactListBinding;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.fragment.dialog.call.CallFragment;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.view.ItemTouchHelperCallback;
|
||||
|
||||
import java.util.List;
|
||||
@@ -59,7 +59,7 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
|
||||
mContactInfoAdapter.setItemMoveCallback(new ContactInfoAdapter.ItemMoveCallback() {
|
||||
@Override
|
||||
public void onItemMove(int fromPosition, int toPosition) {
|
||||
Log.e(TAG, "onItemMove: ");
|
||||
Logger.e(TAG, "onItemMove: ");
|
||||
ContactInfo fromContactInfo = mContactInfos.get(fromPosition);
|
||||
int fromContactPosition = fromContactInfo.getPosition();
|
||||
ContactInfo toContactInfo = mContactInfos.get(toPosition);
|
||||
@@ -72,7 +72,7 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
|
||||
|
||||
@Override
|
||||
public void onItemRemoved(int position) {
|
||||
Log.e(TAG, "onItemRemoved: ");
|
||||
Logger.e(TAG, "onItemRemoved: ");
|
||||
|
||||
}
|
||||
});
|
||||
@@ -98,7 +98,7 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
|
||||
mViewModel.mContactListData.observe(this, new Observer<List<ContactInfo>>() {
|
||||
@Override
|
||||
public void onChanged(List<ContactInfo> contactInfos) {
|
||||
Log.e(TAG, "mContactListData: " + contactInfos);
|
||||
Logger.e(TAG, "mContactListData: " + contactInfos);
|
||||
mContactInfos = contactInfos;
|
||||
mContactInfoAdapter.setContactInfos(mContactInfos);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.activity.contact.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
@@ -11,6 +10,7 @@ import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityContactListBinding;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.db.contact.ContactRepository;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -49,12 +49,12 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
|
||||
.subscribe(new Observer<List<ContactInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getAllContacts", "onSubscribe: ");
|
||||
Logger.e("getAllContacts", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull List<ContactInfo> contactInfos) {
|
||||
Log.e("getAllContacts", "onNext: ");
|
||||
Logger.e("getAllContacts", "onNext: ");
|
||||
mContactListData.setValue(contactInfos);
|
||||
|
||||
// List<Contact> sorted = contacts.stream().sorted(new Comparator<Contact>() {
|
||||
@@ -68,18 +68,18 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getAllContacts", "onError: " + e.getMessage());
|
||||
Logger.e("getAllContacts", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getAllContacts", "onComplete: ");
|
||||
Logger.e("getAllContacts", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateItemPosition(ContactInfo contactInfo) {
|
||||
Log.e(TAG, "updateItemPosition: " + contactInfo);
|
||||
Logger.e(TAG, "updateItemPosition: " + contactInfo);
|
||||
Observable.create(new ObservableOnSubscribe<Integer>() {
|
||||
@Override
|
||||
public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
|
||||
@@ -93,22 +93,22 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
|
||||
.subscribe(new Observer<Integer>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("updateItemPosition", "onComplete: ");
|
||||
Logger.e("updateItemPosition", "onComplete: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull Integer integer) {
|
||||
Log.e("updateItemPosition", "onNext: " + integer);
|
||||
Logger.e("updateItemPosition", "onNext: " + integer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("updateItemPosition", "onError: " + e.getMessage());
|
||||
Logger.e("updateItemPosition", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("updateItemPosition", "onComplete: ");
|
||||
Logger.e("updateItemPosition", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ 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 androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
@@ -22,6 +22,7 @@ import com.ttstd.dialer.fragment.contact.ContactFragment;
|
||||
import com.ttstd.dialer.fragment.home.HomeFragment;
|
||||
import com.ttstd.dialer.fragment.settings.SettingsFragment;
|
||||
import com.ttstd.dialer.service.main.MainService;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.view.ApkPagerAdapter;
|
||||
import com.ttstd.dialer.view.ScaleCircleNavigator;
|
||||
|
||||
@@ -124,7 +125,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
mViewDataBinding.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
Log.e(TAG, "onPageScrolled: position = " + position);
|
||||
Logger.e(TAG, "onPageScrolled: position = " + position);
|
||||
if (mCurrentIndex == -1 && position == 0) {
|
||||
mViewDataBinding.viewPager.setCurrentItem(mDefaultIndex);
|
||||
mCurrentIndex = mDefaultIndex;
|
||||
@@ -135,21 +136,21 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
Log.e(TAG, "onPageSelected: position = " + position);
|
||||
Logger.e(TAG, "onPageSelected: position = " + position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
Log.e(TAG, "onPageScrollStateChanged: state = " + state);
|
||||
Logger.e(TAG, "onPageScrollStateChanged: state = " + state);
|
||||
}
|
||||
});
|
||||
mViewDataBinding.magicIndicator.setNavigator(mScaleCircleNavigator);
|
||||
ViewPagerHelper.bind(mViewDataBinding.magicIndicator, mViewDataBinding.viewPager);
|
||||
mViewDataBinding.viewPager.setCurrentItem(mDefaultIndex);
|
||||
|
||||
Log.e(TAG, "initView: mCurrentIndex = " + mCurrentIndex);
|
||||
Log.e(TAG, "initView: mDefaultIndex = " + mDefaultIndex);
|
||||
Log.e(TAG, "initView: mFragmentSize = " + mFragmentSize);
|
||||
Logger.e(TAG, "initView: mCurrentIndex = " + mCurrentIndex);
|
||||
Logger.e(TAG, "initView: mDefaultIndex = " + mDefaultIndex);
|
||||
Logger.e(TAG, "initView: mFragmentSize = " + mFragmentSize);
|
||||
|
||||
Intent intent = new Intent(this, MainService.class);
|
||||
startService(intent);
|
||||
@@ -163,25 +164,37 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
@Override
|
||||
public void onChanged(List<AppInfo> appInfos) {
|
||||
mAppInfos = appInfos;
|
||||
Log.e(TAG, "onChanged: mAppInfos size = " + mAppInfos.size());
|
||||
Logger.e(TAG, "onChanged: mAppInfos size = " + mAppInfos.size());
|
||||
setAppList();
|
||||
}
|
||||
});
|
||||
mViewModel.getOutsideApp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
Logger.e(TAG, "onKeyDown: keyCode = " + keyCode);
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
return false;
|
||||
}
|
||||
if (keyCode == KeyEvent.KEYCODE_HOME) {
|
||||
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
//修补autozie fragment item大小不一致
|
||||
Log.e(TAG, "onResume: ");
|
||||
Logger.e(TAG, "onResume: ");
|
||||
mViewModel.getOutsideApp();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.e(TAG, "onDestroy: ");
|
||||
Logger.e(TAG, "onDestroy: ");
|
||||
unregisterReceivers();
|
||||
}
|
||||
|
||||
@@ -213,23 +226,23 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
private class PackageChangedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.e(TAG, "onReceive: " + intent.getAction());
|
||||
Logger.e(TAG, "onReceive: " + intent.getAction());
|
||||
}
|
||||
}
|
||||
|
||||
private void setAppList() {
|
||||
Log.e(TAG, "setAppList: mFragments size = " + mFragments.size());
|
||||
Logger.e(TAG, "setAppList: mFragments size = " + mFragments.size());
|
||||
mFragments = mFragments.subList(0, mFragmentSize);
|
||||
Log.e(TAG, "setAppList: subList mFragments size = " + mFragments.size());
|
||||
Logger.e(TAG, "setAppList: subList mFragments size = " + mFragments.size());
|
||||
int fragmentCount = (int) Math.ceil((double) mAppInfos.size() / APK_PER_FRAGMENT);
|
||||
for (int i = 0; i < fragmentCount; i++) {
|
||||
int start = i * APK_PER_FRAGMENT;
|
||||
int end = Math.min(start + APK_PER_FRAGMENT, mAppInfos.size());
|
||||
List<AppInfo> subList = new ArrayList<>(mAppInfos.subList(start, end));
|
||||
Log.e(TAG, "setAppList: subList size = " + subList.size());
|
||||
Logger.e(TAG, "setAppList: subList size = " + subList.size());
|
||||
// AppFragment appFragment = AppFragment.newInstance(subList);
|
||||
AppFragment appFragment = new AppFragment(subList);
|
||||
Log.e(TAG, "setAppList: appFragment hashCode = " + appFragment.hashCode());
|
||||
Logger.e(TAG, "setAppList: appFragment hashCode = " + appFragment.hashCode());
|
||||
appFragment.setUpdateCallback(new AppFragment.UpdateCallback() {
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
@@ -237,7 +250,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
}
|
||||
});
|
||||
mFragments.add(appFragment);
|
||||
Log.e(TAG, "setAppList: add mFragments size = " + mFragments.size());
|
||||
Logger.e(TAG, "setAppList: add mFragments size = " + mFragments.size());
|
||||
}
|
||||
mScaleCircleNavigator.setCircleCount(mFragments.size());
|
||||
mScaleCircleNavigator.notifyDataSetChanged();
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package com.ttstd.dialer.activity.main;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.trello.rxlifecycle4.android.FragmentEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityMainBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.db.app.AppRepository;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -48,24 +47,25 @@ public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEv
|
||||
.subscribe(new Observer<List<AppInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getOutsideApp", "onSubscribe: ");
|
||||
Logger.e("getOutsideApp", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull List<AppInfo> appInfos) {
|
||||
Log.e("getOutsideApp", "onNext: " + appInfos);
|
||||
Logger.e("getOutsideApp", "onNext: " + appInfos);
|
||||
mDesktopSortAppData.setValue(appInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getOutsideApp", "onError: " + e.getMessage());
|
||||
Logger.e("getOutsideApp", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getOutsideApp", "onComplete: ");
|
||||
Logger.e("getOutsideApp", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class SettingsActivity extends BaseMvvmActivity<SettingsViewModel, Activi
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
mViewModel.snRegister();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,41 @@ package com.ttstd.dialer.activity.settings.home;
|
||||
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsBinding;
|
||||
import com.ttstd.dialer.databinding.ActivityTemplateBinding;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class SettingsViewModel extends BaseViewModel<ActivitySettingsBinding, ActivityEvent> {
|
||||
|
||||
public void snRegister() {
|
||||
OkHttpManager.getInstance().getSnRegisterObservable(getLifecycle())
|
||||
.subscribe(new Observer<BaseResponse>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Logger.e("snRegister", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse baseResponse) {
|
||||
Logger.e("snRegister", "onNext: " + baseResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Logger.e("snRegister", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Logger.e("snRegister", "onComplete: ");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.ttstd.dialer.activity.settings.utils;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.view.View;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
@@ -13,9 +13,21 @@ import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.databinding.ActivitySettingsUtilsBinding;
|
||||
import com.ttstd.dialer.service.main.MainService;
|
||||
import com.ttstd.dialer.utils.CameraUtil;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
import com.ttstd.dialer.view.ToggleButton;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.internal.observers.BlockingBaseObserver;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewModel, ActivitySettingsUtilsBinding> {
|
||||
private static final String TAG = "SettingsActivity";
|
||||
|
||||
@@ -97,15 +109,15 @@ public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewMod
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
int enableFloatWindow = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_ENABLE, 0);
|
||||
Log.e(TAG, "initView: enableFloatWindow = " + enableFloatWindow);
|
||||
Logger.e(TAG, "initView: enableFloatWindow = " + enableFloatWindow);
|
||||
mViewDataBinding.siFloatWindow.setToggleStatu(enableFloatWindow == 1);
|
||||
|
||||
int floatWindowKillApp = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_KILL_APP, 0);
|
||||
Log.e(TAG, "initView: floatWindowKillApp = " + floatWindowKillApp);
|
||||
Logger.e(TAG, "initView: floatWindowKillApp = " + floatWindowKillApp);
|
||||
mViewDataBinding.siFloatWindowKill.setToggleStatu(floatWindowKillApp == 1);
|
||||
|
||||
boolean defaultLauncher = SystemUtils.isDefaultLauncher(SettingsUtilsActivity.this, MainActivity.class);
|
||||
Log.e(TAG, "initView: defaultLauncher = " + defaultLauncher);
|
||||
Logger.e(TAG, "initView: defaultLauncher = " + defaultLauncher);
|
||||
mViewDataBinding.siDefaultLauncher.setToggleStatu(defaultLauncher);
|
||||
}
|
||||
|
||||
@@ -116,10 +128,71 @@ public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewMod
|
||||
|
||||
public void screenshotSnap(View view) {
|
||||
// Bitmap bitmap = SystemUtils.takeFullScreenshot(SettingsUtilsActivity.this);
|
||||
Bitmap bitmap = SystemUtils.takeScreenshotHighVersion();
|
||||
if (bitmap != null) {
|
||||
mViewDataBinding.ivSnap.setImageBitmap(bitmap);
|
||||
}
|
||||
// Bitmap bitmap = SystemUtils.takeScreenshotHighVersion();
|
||||
// if (bitmap != null) {
|
||||
// mViewDataBinding.ivSnap.setImageBitmap(bitmap);
|
||||
// }
|
||||
String filepath = getExternalCacheDir().getAbsolutePath() + File.separator + "screenshot_" + System.currentTimeMillis() + ".png";
|
||||
SystemUtils.screenshotCmd(filepath, new BlockingBaseObserver<Integer>() {
|
||||
@Override
|
||||
public void onNext(@NonNull Integer integer) {
|
||||
if (integer == 0) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filepath);
|
||||
mViewDataBinding.ivSnap.setImageBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void getCameraSnap(View view) {
|
||||
long createTime = System.currentTimeMillis() / 1000;
|
||||
CameraUtil cameraUtil = new CameraUtil(SettingsUtilsActivity.this, new CameraUtil.CameraCallBack() {
|
||||
@Override
|
||||
public void onErr(String msg) {
|
||||
Logger.e("CameraUtil", "onErr: " + msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTakePhotoOk(String path) {
|
||||
Logger.e("CameraUtil", "onTakePhotoOk: " + path);
|
||||
File file = new File(path);
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(path);
|
||||
mViewDataBinding.ivCamera.setImageBitmap(bitmap);
|
||||
|
||||
MediaType mediaType = MediaType.Companion.parse("image/png");
|
||||
RequestBody fileBody = RequestBody.Companion.create(file, mediaType);
|
||||
//设置一个file文件
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), fileBody);
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("sn", SystemUtils.getSerial());
|
||||
params.put("createtime", String.valueOf(createTime));
|
||||
// Call<BaseResponse> call = NetInterfaceManager.getInstance().getScreenshotCall().sendScreenshot(params, body);
|
||||
// call.enqueue(new RetryCallback<BaseResponse>(call, 10, 30 * 1000) {
|
||||
// @Override
|
||||
// public void onRequestResponse(Call call, Response response) {
|
||||
// Logger.e(TAG, "onRequestResponse: " + response.body().toString());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onRequestFail(Call call, Throwable t) {
|
||||
// Logger.e(TAG, "onRequestFail: ");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onStartRetry() {
|
||||
// Logger.e(TAG, "onStartRetry: ");
|
||||
// }
|
||||
// });
|
||||
|
||||
}
|
||||
});
|
||||
cameraUtil.startTakePicture(getExternalCacheDir().getAbsolutePath() + File.separator + createTime + ".jpg");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package com.ttstd.dialer.activity.weather.main;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.baidu.location.BDLocation;
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus;
|
||||
import com.qweather.sdk.response.weather.WeatherDaily;
|
||||
import com.qweather.sdk.response.weather.WeatherHourly;
|
||||
@@ -15,9 +12,11 @@ import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.adapter.HourlyWeatherAdapter;
|
||||
import com.ttstd.dialer.adapter.WeatherAdapter;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.databinding.ActivityWeatherMainBinding;
|
||||
import com.ttstd.dialer.utils.DateUtil;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -71,7 +70,7 @@ public class WeatherMainActivity extends BaseMvvmActivity<WeatherMainViewModel,
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
Log.e(TAG, "initData: ");
|
||||
Logger.e(TAG, "initData: ");
|
||||
|
||||
mViewModel.mWeatherNowData.observe(this, new Observer<WeatherNow>() {
|
||||
@Override
|
||||
@@ -108,14 +107,14 @@ public class WeatherMainActivity extends BaseMvvmActivity<WeatherMainViewModel,
|
||||
if (manually) {
|
||||
|
||||
} else {
|
||||
LiveEventBus.get(CommonConfig.LOCATION_CHANGED_KEY, BDLocation.class)
|
||||
.observeSticky(this, new Observer<BDLocation>() {
|
||||
LiveEventBus.get(CommonConfig.LOCATION_CHANGED_KEY, SnLocationReq.class)
|
||||
.observeSticky(this, new Observer<SnLocationReq>() {
|
||||
@Override
|
||||
public void onChanged(BDLocation bdLocation) {
|
||||
mViewDataBinding.tvLocation.setText(bdLocation.getDistrict());
|
||||
mViewModel.getWeatherNow(bdLocation.getAdCode());
|
||||
mViewModel.getWeather24h(bdLocation.getAdCode());
|
||||
mViewModel.getWeather10D(bdLocation.getAdCode());
|
||||
public void onChanged(SnLocationReq snLocationReq) {
|
||||
mViewDataBinding.tvLocation.setText(snLocationReq.getDistrict());
|
||||
mViewModel.getWeatherNow(snLocationReq.getAdCode());
|
||||
mViewModel.getWeather24h(snLocationReq.getAdCode());
|
||||
mViewModel.getWeather10D(snLocationReq.getAdCode());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.ttstd.dialer.activity.weather.main;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.qweather.sdk.Callback;
|
||||
@@ -17,6 +15,7 @@ import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityWeatherMainBinding;
|
||||
import com.ttstd.dialer.gson.GsonUtils;
|
||||
import com.ttstd.dialer.manager.WeatherManager;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -26,25 +25,25 @@ public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBindi
|
||||
public MutableLiveData<WeatherNow> mWeatherNowData = new MutableLiveData<>();
|
||||
|
||||
public void getWeatherNow(String adCode) {
|
||||
Log.e(TAG, "getWeatherNow: " + adCode);
|
||||
Logger.e(TAG, "getWeatherNow: " + adCode);
|
||||
WeatherManager.getInstance().getWeatherNow(adCode, new Callback<WeatherNowResponse>() {
|
||||
@Override
|
||||
public void onSuccess(WeatherNowResponse response) {
|
||||
Log.e("getWeatherNow", "onSuccess: " + response);
|
||||
Logger.e("getWeatherNow", "onSuccess: " + response);
|
||||
WeatherNow weatherNow = response.getNow();
|
||||
Log.e("getWeatherNow", "onSuccess: " + weatherNow);
|
||||
Logger.e("getWeatherNow", "onSuccess: " + weatherNow);
|
||||
mWeatherNowData.postValue(weatherNow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ErrorResponse errorResponse) {
|
||||
Log.e("getWeatherNow", "onFailure: " + errorResponse.getError().getStatus());
|
||||
Log.e("getWeatherNow", "onFailure: " + errorResponse.getError().getDetail());
|
||||
Logger.e("getWeatherNow", "onFailure: " + errorResponse.getError().getStatus());
|
||||
Logger.e("getWeatherNow", "onFailure: " + errorResponse.getError().getDetail());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Throwable e) {
|
||||
Log.e("getWeatherNow", "onException: " + e.getMessage());
|
||||
Logger.e("getWeatherNow", "onException: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -52,25 +51,25 @@ public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBindi
|
||||
public MutableLiveData<List<WeatherHourly>> mWeather24hData = new MutableLiveData<>();
|
||||
|
||||
public void getWeather24h(String adCode) {
|
||||
Log.e(TAG, "getWeather24h: " + adCode);
|
||||
Logger.e(TAG, "getWeather24h: " + adCode);
|
||||
WeatherManager.getInstance().getWeather24h(adCode, new Callback<WeatherHourlyResponse>() {
|
||||
@Override
|
||||
public void onSuccess(WeatherHourlyResponse weatherHourlyResponse) {
|
||||
Log.e("getWeather24h", "onSuccess: " + weatherHourlyResponse);
|
||||
Logger.e("getWeather24h", "onSuccess: " + weatherHourlyResponse);
|
||||
List<WeatherHourly> weatherHourlies = weatherHourlyResponse.getHourly();
|
||||
Log.e("getWeather24h", "onSuccess: " + GsonUtils.toJSONString(weatherHourlies));
|
||||
Logger.e("getWeather24h", "onSuccess: " + GsonUtils.toJSONString(weatherHourlies));
|
||||
mWeather24hData.postValue(weatherHourlies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ErrorResponse errorResponse) {
|
||||
Log.e("getWeather24h", "onFailure: " + errorResponse.getError().getStatus());
|
||||
Log.e("getWeather24h", "onFailure: " + errorResponse.getError().getDetail());
|
||||
Logger.e("getWeather24h", "onFailure: " + errorResponse.getError().getStatus());
|
||||
Logger.e("getWeather24h", "onFailure: " + errorResponse.getError().getDetail());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Throwable throwable) {
|
||||
Log.e("getWeather24h", "onException: " + throwable.getMessage());
|
||||
Logger.e("getWeather24h", "onException: " + throwable.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -79,24 +78,24 @@ public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBindi
|
||||
public MutableLiveData<List<WeatherDaily>> mWeatherDailyListData = new MutableLiveData<>();
|
||||
|
||||
public void getWeather10D(String adCode) {
|
||||
Log.e(TAG, "getWeather10D: " + adCode);
|
||||
Logger.e(TAG, "getWeather10D: " + adCode);
|
||||
WeatherManager.getInstance().getWeather10D(adCode, new Callback<WeatherDailyResponse>() {
|
||||
@Override
|
||||
public void onSuccess(WeatherDailyResponse weatherDailyResponse) {
|
||||
Log.e("getWeather10D", "onSuccess: ");
|
||||
Logger.e("getWeather10D", "onSuccess: ");
|
||||
List<WeatherDaily> weatherDailyList = weatherDailyResponse.getDaily();
|
||||
Log.e("getWeather10D", "onSuccess: " + GsonUtils.toJSONString(weatherDailyList));
|
||||
Logger.e("getWeather10D", "onSuccess: " + GsonUtils.toJSONString(weatherDailyList));
|
||||
mWeatherDailyListData.postValue(weatherDailyList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ErrorResponse errorResponse) {
|
||||
Log.e("getWeather10D", "onFailure: ");
|
||||
Logger.e("getWeather10D", "onFailure: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException(Throwable throwable) {
|
||||
Log.e("getWeather10D", "onException: " + throwable.getMessage());
|
||||
Logger.e("getWeather10D", "onException: " + throwable.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.ttstd.dialer.adapter;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -24,6 +23,7 @@ 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.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.iconloader.IconCacheManager;
|
||||
import com.ttstd.iconloader.IconLoader;
|
||||
|
||||
@@ -132,7 +132,7 @@ public class AppAdapter extends RecyclerView.Adapter<AppAdapter.AppHolder> imple
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull @NotNull Loader<Drawable> loader) {
|
||||
Log.e(TAG, "onLoaderReset: ");
|
||||
Logger.e(TAG, "onLoaderReset: ");
|
||||
}
|
||||
|
||||
public class AppHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -14,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.shehuan.niv.NiceImageView;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -69,7 +69,7 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
|
||||
holder.root.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.e(TAG, "onClick: " + contactInfo);
|
||||
Logger.e(TAG, "onClick: " + contactInfo);
|
||||
if (mOnClickListener != null) {
|
||||
mOnClickListener.onClick(contactInfo);
|
||||
}
|
||||
@@ -85,8 +85,8 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
|
||||
|
||||
// 处理拖动交换位置
|
||||
public void onItemMove(int fromPosition, int toPosition) {
|
||||
Log.e(TAG, "onItemMove: fromPosition = " + fromPosition);
|
||||
Log.e(TAG, "onItemMove: toPosition = " + toPosition);
|
||||
Logger.e(TAG, "onItemMove: fromPosition = " + fromPosition);
|
||||
Logger.e(TAG, "onItemMove: toPosition = " + toPosition);
|
||||
if (fromPosition == toPosition) {
|
||||
return; // 直接返回,不执行后续操作
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -14,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.shehuan.niv.NiceImageView;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -69,7 +69,7 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
|
||||
holder.root.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.e(TAG, "onClick: " + contactInfo);
|
||||
Logger.e(TAG, "onClick: " + contactInfo);
|
||||
if (mOnClickListener != null) {
|
||||
mOnClickListener.onClick(contactInfo);
|
||||
}
|
||||
@@ -85,8 +85,8 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
|
||||
|
||||
// 处理拖动交换位置
|
||||
public void onItemMove(int fromPosition, int toPosition) {
|
||||
Log.e(TAG, "onItemMove: fromPosition = " + fromPosition);
|
||||
Log.e(TAG, "onItemMove: toPosition = " + toPosition);
|
||||
Logger.e(TAG, "onItemMove: fromPosition = " + fromPosition);
|
||||
Logger.e(TAG, "onItemMove: toPosition = " + toPosition);
|
||||
if (fromPosition == toPosition) {
|
||||
return; // 直接返回,不执行后续操作
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -9,17 +8,15 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
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.Logger;
|
||||
import com.ttstd.dialer.utils.TimeUtils;
|
||||
|
||||
import java.util.List;
|
||||
@@ -71,7 +68,7 @@ public class HourlyWeatherAdapter extends RecyclerView.Adapter<HourlyWeatherAdap
|
||||
requestBuilder.load(resId).into(holder.iv_icon);
|
||||
} else {
|
||||
// 处理错误
|
||||
Log.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
Logger.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
}
|
||||
|
||||
holder.tv_temp.setText(weatherHourly.getTemp() + "°");
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.ttstd.dialer.adapter;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -24,6 +23,7 @@ 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.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.iconloader.IconCacheManager;
|
||||
import com.ttstd.iconloader.IconLoader;
|
||||
|
||||
@@ -137,7 +137,7 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull @NotNull Loader<Drawable> loader) {
|
||||
Log.e(TAG, "onLoaderReset: ");
|
||||
Logger.e(TAG, "onLoaderReset: ");
|
||||
}
|
||||
|
||||
public class AppHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.adapter;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -19,9 +18,9 @@ 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.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
@@ -83,7 +82,7 @@ public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherH
|
||||
requestBuilder.load(resId).into(holder.iv_icon);
|
||||
} else {
|
||||
// 处理错误
|
||||
Log.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
Logger.e("GlideLoad", "Raw resource not found: " + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.multidex.MultiDex;
|
||||
|
||||
import com.alibaba.android.arouter.launcher.ARouter;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
@@ -18,7 +19,11 @@ import com.ttstd.dialer.config.CommonConfig;
|
||||
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.utils.Logger;
|
||||
import com.ttstd.dialer.utils.NativeUtils;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
import com.ttstd.iconloader.IconCacheManager;
|
||||
|
||||
@@ -30,21 +35,30 @@ import cn.jpush.android.api.JPushInterface;
|
||||
public class BaseApplication extends Application {
|
||||
private static final String TAG = "BaseApplication";
|
||||
|
||||
private MMKV mMMKV;
|
||||
|
||||
/**
|
||||
* ViewModel中因为经常旋转导致弱引用为空
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Context context;
|
||||
private static Context mAppContext;
|
||||
|
||||
public static Context getContext() {
|
||||
return context;
|
||||
return mAppContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
MultiDex.install(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
context = getApplicationContext();
|
||||
Logger.e(TAG, "onCreate: ");
|
||||
mAppContext = getApplicationContext();
|
||||
|
||||
if (!BuildConfig.DEBUG) {
|
||||
catchException();
|
||||
}
|
||||
@@ -56,34 +70,20 @@ public class BaseApplication extends Application {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
Log.e(TAG, "init: ");
|
||||
Logger.e(TAG, "init: ");
|
||||
Logger.e(TAG, "init: getNonce = " + NativeUtils.getNonce());
|
||||
if (SystemUtils.isMainProcessName(this, android.os.Process.myPid())) {
|
||||
Logger.initialize(this, BuildConfig.DEBUG);
|
||||
Logger.setLogLevel(Logger.LogLevel.DEBUG); // 开发阶段记录所有日志
|
||||
|
||||
String rootDir = MMKV.initialize(this);
|
||||
Log.e(TAG, "mmkv root: " + rootDir);
|
||||
MMKV mmkv = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
Logger.e(TAG, "mmkv root: " + rootDir);
|
||||
mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
/*jpush start*/
|
||||
JPushInterface.setDebugMode(true);
|
||||
// 调整点一:调用启用推送业务功能代码前增加setAuth调用
|
||||
boolean isPrivacyReady = true; // app根据是否已弹窗获取隐私授权来赋值
|
||||
if (!isPrivacyReady) {
|
||||
// JCore 5.0.4之前版本需要显式设置false
|
||||
if (JCoreInterface.getJCoreSDKVersionInt() < 504) { // 5.0.4版本号对应504
|
||||
JCollectionAuth.setAuth(context, false);
|
||||
}
|
||||
// 所有版本在未授权时都不应初始化SDK
|
||||
return;
|
||||
}
|
||||
JPushInterface.init(this);
|
||||
JPushInterface.setAlias(this, 0, SystemUtils.getSerial());
|
||||
|
||||
// 调整点二:App用户同意了隐私政策授权,并且开发者确定要开启推送服务后调用
|
||||
// JCore 5.0.4+会自动处理授权状态,可不需要显式设置true
|
||||
JCollectionAuth.setAuth(context, true);
|
||||
/*jpush end*/
|
||||
DeviceManagerService.init(this);
|
||||
OkHttpManager.init(this);
|
||||
PushExecutor.init(this);
|
||||
initJPush();
|
||||
|
||||
if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
|
||||
ARouter.openLog(); // 打印日志
|
||||
@@ -94,7 +94,7 @@ public class BaseApplication extends Application {
|
||||
// 初始化 Toast 框架
|
||||
Toaster.init(this);
|
||||
|
||||
Log.e(TAG, "slowInit: ");
|
||||
Logger.e(TAG, "slowInit: ");
|
||||
Aria.init(this);
|
||||
CrashReport.initCrashReport(getApplicationContext(), "845e3ed68c", false);
|
||||
CrashReport.setDeviceId(this, Build.MODEL);
|
||||
@@ -103,8 +103,8 @@ public class BaseApplication extends Application {
|
||||
AppManager.init(this);
|
||||
MapManager.init(this);
|
||||
MapManager.getInstance().initMap();
|
||||
boolean manually = mmkv.decodeBool(CommonConfig.MANUALLY_SELECT_LOCATION, false);
|
||||
Log.e(TAG, "init: manually location " + manually);
|
||||
boolean manually = mMMKV.decodeBool(CommonConfig.MANUALLY_SELECT_LOCATION, false);
|
||||
Logger.e(TAG, "init: manually location " + manually);
|
||||
if (manually) {
|
||||
|
||||
} else {
|
||||
@@ -115,12 +115,36 @@ public class BaseApplication extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
private void initJPush() {
|
||||
/*jpush start*/
|
||||
JPushInterface.setDebugMode(true);
|
||||
// 调整点一:调用启用推送业务功能代码前增加setAuth调用
|
||||
boolean isPrivacyReady = true; // app根据是否已弹窗获取隐私授权来赋值
|
||||
if (!isPrivacyReady) {
|
||||
// JCore 5.0.4之前版本需要显式设置false
|
||||
if (JCoreInterface.getJCoreSDKVersionInt() < 504) { // 5.0.4版本号对应504
|
||||
JCollectionAuth.setAuth(this, false);
|
||||
}
|
||||
// 所有版本在未授权时都不应初始化SDK
|
||||
return;
|
||||
}
|
||||
JPushInterface.init(this);
|
||||
JPushInterface.setAlias(this, 0, SystemUtils.getSerial());
|
||||
|
||||
// 调整点二:App用户同意了隐私政策授权,并且开发者确定要开启推送服务后调用
|
||||
// JCore 5.0.4+会自动处理授权状态,可不需要显式设置true
|
||||
JCollectionAuth.setAuth(this, true);
|
||||
/*jpush end*/
|
||||
Logger.e(TAG, "initJPush: inited JPush");
|
||||
|
||||
}
|
||||
|
||||
private void catchException() {
|
||||
Thread.setDefaultUncaughtExceptionHandler(
|
||||
new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.e("捕获异常子线程:", Thread.currentThread().getName() +
|
||||
Logger.e("捕获异常子线程:", Thread.currentThread().getName() +
|
||||
"在:" + e.getStackTrace()[0].getClassName());
|
||||
}
|
||||
}
|
||||
@@ -133,7 +157,7 @@ public class BaseApplication extends Application {
|
||||
try {
|
||||
Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法!
|
||||
} catch (Exception e) {
|
||||
Log.e("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName());
|
||||
Logger.e("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
43
app/src/main/java/com/ttstd/dialer/base/BaseService.java
Normal file
43
app/src/main/java/com/ttstd/dialer/base/BaseService.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LifecycleRegistry;
|
||||
|
||||
import com.ttstd.dialer.base.rx.BaseRxService;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class BaseService extends BaseRxService implements LifecycleOwner {
|
||||
|
||||
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
return lifecycleRegistry;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
|
||||
//修补在Service中LiveData不能触发
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.base.mvvm;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
@@ -10,6 +9,7 @@ import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.ttstd.dialer.base.BaseTransparentActivity;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -31,7 +31,7 @@ public abstract class BaseMvvmActivity<VM extends ViewModel, VDB extends ViewDat
|
||||
//ViewModel
|
||||
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
boolean isAbstract = Modifier.isAbstract(vmClass.getModifiers());
|
||||
Log.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract);
|
||||
Logger.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract);
|
||||
if (!isAbstract) {//不是一个抽象类
|
||||
mViewModel = new ViewModelProvider(this).get(vmClass);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -20,7 +19,7 @@ import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.ttstd.dialer.base.BaseDialogFragment;
|
||||
import com.ttstd.dialer.base.BaseFragment;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -76,7 +75,7 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
|
||||
*/
|
||||
public boolean isAttached() {
|
||||
boolean flag = getCtx() != null && isAdded();
|
||||
Log.e(" >> isAttached >>", "flag = " + flag);
|
||||
Logger.e(" >> isAttached >>", "flag = " + flag);
|
||||
return flag;
|
||||
}
|
||||
|
||||
@@ -234,7 +233,7 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
|
||||
//
|
||||
// public void updateLoadingTip(@StringRes int messageID, int percent) {
|
||||
// try {
|
||||
// if (mWaitDialog != null && mWaitDialog.isShow()) {
|
||||
// if (mWaitDialog != null && mWaitDiaLogger.isShow()) {
|
||||
// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip);
|
||||
// if (tvTip != null)
|
||||
// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%"));
|
||||
@@ -245,7 +244,7 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
|
||||
// }
|
||||
//
|
||||
// public boolean isShowLoading() {
|
||||
// return mWaitDialog != null && mWaitDialog.isShow();
|
||||
// return mWaitDialog != null && mWaitDiaLogger.isShow();
|
||||
// }
|
||||
//
|
||||
// public void hideLoading() {
|
||||
@@ -253,7 +252,7 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
|
||||
// boolean isShow = isShowLoading();
|
||||
// L.d(" >> hideLoading :: isShow: %s", isShow);
|
||||
// if (isShow)
|
||||
// mWaitDialog.dismiss();
|
||||
// mWaitDiaLogger.dismiss();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -20,6 +19,7 @@ import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.ttstd.dialer.base.BaseFragment;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -75,7 +75,7 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
|
||||
*/
|
||||
public boolean isAttached() {
|
||||
boolean flag = getCtx() != null && isAdded();
|
||||
Log.e(" >> isAttached >>", "flag = " + flag);
|
||||
Logger.e(" >> isAttached >>", "flag = " + flag);
|
||||
return flag;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
|
||||
//
|
||||
// public void updateLoadingTip(@StringRes int messageID, int percent) {
|
||||
// try {
|
||||
// if (mWaitDialog != null && mWaitDialog.isShow()) {
|
||||
// if (mWaitDialog != null && mWaitDiaLogger.isShow()) {
|
||||
// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip);
|
||||
// if (tvTip != null)
|
||||
// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%"));
|
||||
@@ -244,7 +244,7 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
|
||||
// }
|
||||
//
|
||||
// public boolean isShowLoading() {
|
||||
// return mWaitDialog != null && mWaitDialog.isShow();
|
||||
// return mWaitDialog != null && mWaitDiaLogger.isShow();
|
||||
// }
|
||||
//
|
||||
// public void hideLoading() {
|
||||
@@ -252,7 +252,7 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
|
||||
// boolean isShow = isShowLoading();
|
||||
// L.d(" >> hideLoading :: isShow: %s", isShow);
|
||||
// if (isShow)
|
||||
// mWaitDialog.dismiss();
|
||||
// mWaitDiaLogger.dismiss();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.base.rx;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -49,14 +48,9 @@ public abstract class BaseRxService extends Service implements LifecycleProvider
|
||||
lifecycleSubject.onNext(ActivityEvent.CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
lifecycleSubject.onNext(ActivityEvent.STOP);
|
||||
lifecycleSubject.onNext(ActivityEvent.DESTROY);
|
||||
}
|
||||
}
|
||||
}
|
||||
143
app/src/main/java/com/ttstd/dialer/bean/ApkInstalledInfo.java
Normal file
143
app/src/main/java/com/ttstd/dialer/bean/ApkInstalledInfo.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package com.ttstd.dialer.bean;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ApkInstalledInfo implements Serializable {
|
||||
private static final long serialVersionUID = 3322347581650924289L;
|
||||
|
||||
@SerializedName("package_name")
|
||||
private String packageName;
|
||||
|
||||
@SerializedName("app_name")
|
||||
private String appName;
|
||||
|
||||
@SerializedName("version_name")
|
||||
private String versionName;
|
||||
|
||||
@SerializedName("version_code")
|
||||
private long versionCode;
|
||||
|
||||
@SerializedName("install_time")
|
||||
private long installTime;
|
||||
|
||||
@SerializedName("last_update_time")
|
||||
private long lastUpdateTime;
|
||||
|
||||
@SerializedName("apk_size")
|
||||
private long apkSize;
|
||||
|
||||
@SerializedName("data_size")
|
||||
private long dataSize;
|
||||
|
||||
@SerializedName("cache_size")
|
||||
private long cacheSize;
|
||||
|
||||
@SerializedName("md5")
|
||||
private String md5;
|
||||
|
||||
@SerializedName("system_app")
|
||||
private boolean systemApp;
|
||||
|
||||
public ApkInstalledInfo() {
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
public String getVersionName() {
|
||||
return versionName;
|
||||
}
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
this.versionName = versionName;
|
||||
}
|
||||
|
||||
public long getVersionCode() {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
public void setVersionCode(long versionCode) {
|
||||
this.versionCode = versionCode;
|
||||
}
|
||||
|
||||
public long getInstallTime() {
|
||||
return installTime;
|
||||
}
|
||||
|
||||
public void setInstallTime(long installTime) {
|
||||
this.installTime = installTime;
|
||||
}
|
||||
|
||||
public long getLastUpdateTime() {
|
||||
return lastUpdateTime;
|
||||
}
|
||||
|
||||
public void setLastUpdateTime(long lastUpdateTime) {
|
||||
this.lastUpdateTime = lastUpdateTime;
|
||||
}
|
||||
|
||||
public long getApkSize() {
|
||||
return apkSize;
|
||||
}
|
||||
|
||||
public void setApkSize(long apkSize) {
|
||||
this.apkSize = apkSize;
|
||||
}
|
||||
|
||||
public long getDataSize() {
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
public void setDataSize(long dataSize) {
|
||||
this.dataSize = dataSize;
|
||||
}
|
||||
|
||||
public long getCacheSize() {
|
||||
return cacheSize;
|
||||
}
|
||||
|
||||
public void setCacheSize(long cacheSize) {
|
||||
this.cacheSize = cacheSize;
|
||||
}
|
||||
|
||||
public String getMd5() {
|
||||
return md5;
|
||||
}
|
||||
|
||||
public void setMd5(String md5) {
|
||||
this.md5 = md5;
|
||||
}
|
||||
|
||||
public boolean isSystemApp() {
|
||||
return systemApp;
|
||||
}
|
||||
|
||||
public void setSystemApp(boolean systemApp) {
|
||||
this.systemApp = systemApp;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
|
||||
}
|
||||
}
|
||||
52
app/src/main/java/com/ttstd/dialer/bean/BaseResponse.java
Normal file
52
app/src/main/java/com/ttstd/dialer/bean/BaseResponse.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package com.ttstd.dialer.bean;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* 通用响应实体基类
|
||||
*
|
||||
* @param <T> 具体数据实体
|
||||
*/
|
||||
public class BaseResponse<T> {
|
||||
private String code;
|
||||
private String msg;
|
||||
private T data;
|
||||
|
||||
// 判断请求是否成功(根据后端 code 定义)
|
||||
public boolean isSuccess() {
|
||||
return "00000".equals(code);
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ttstd.dialer.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class DeveloperOptions implements Serializable {
|
||||
private static final long serialVersionUID = 6146726170647484306L;
|
||||
|
||||
int developerOptions;
|
||||
|
||||
public int getDeveloperOptions() {
|
||||
return developerOptions;
|
||||
}
|
||||
|
||||
public void setDeveloperOptions(int developerOptions) {
|
||||
this.developerOptions = developerOptions;
|
||||
}
|
||||
}
|
||||
115
app/src/main/java/com/ttstd/dialer/bean/PushMessage.java
Normal file
115
app/src/main/java/com/ttstd/dialer/bean/PushMessage.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package com.ttstd.dialer.bean;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import cn.jpush.android.api.CustomMessage;
|
||||
|
||||
|
||||
/**
|
||||
* copy from
|
||||
* {
|
||||
* {@link cn.jpush.android.api.CustomMessage}
|
||||
* }
|
||||
* add Serializable
|
||||
*/
|
||||
public class PushMessage implements Serializable {
|
||||
private static final long serialVersionUID = -3312355696577675582L;
|
||||
|
||||
public String messageId;
|
||||
public String extra;
|
||||
public String message;
|
||||
public String contentType;
|
||||
public String title;
|
||||
public String senderId;
|
||||
public String appId;
|
||||
public byte platform;
|
||||
|
||||
public PushMessage() {
|
||||
}
|
||||
|
||||
public PushMessage(CustomMessage customMessage) {
|
||||
this.messageId = customMessage.messageId;
|
||||
this.extra = customMessage.extra;
|
||||
this.message = customMessage.message;
|
||||
this.contentType = customMessage.contentType;
|
||||
this.title = customMessage.title;
|
||||
this.senderId = customMessage.senderId;
|
||||
this.appId = customMessage.appId;
|
||||
this.platform = customMessage.platform;
|
||||
}
|
||||
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getSenderId() {
|
||||
return senderId;
|
||||
}
|
||||
|
||||
public void setSenderId(String senderId) {
|
||||
this.senderId = senderId;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public byte getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(byte platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.ttstd.dialer.bean.req;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class SnHardwareInfoReq implements Serializable {
|
||||
private static final long serialVersionUID = 2728925136862797858L;
|
||||
|
||||
@SerializedName("sn_imei")
|
||||
private String imei;
|
||||
|
||||
@SerializedName("sn_imsi")
|
||||
private String imsi;
|
||||
|
||||
@SerializedName("sn_wlan_mac")
|
||||
private String wlanMac;
|
||||
|
||||
@SerializedName("sn_device_mac")
|
||||
private String deviceMac;
|
||||
|
||||
@SerializedName("sn_bluetooth_mac")
|
||||
private String bluetoothMac;
|
||||
|
||||
@SerializedName("sn_model")
|
||||
private String model;
|
||||
|
||||
@SerializedName("sn_brand")
|
||||
private String brand;
|
||||
|
||||
@SerializedName("sn_board")
|
||||
private String board;
|
||||
|
||||
@SerializedName("sn_android_version")
|
||||
private String androidVersion;
|
||||
|
||||
@SerializedName("sn_android_api")
|
||||
private int androidApi;
|
||||
|
||||
@SerializedName("sn_build_id")
|
||||
private String buildId;
|
||||
|
||||
@SerializedName("sn_build_display_id")
|
||||
private String buildDisplayId;
|
||||
|
||||
public String getImei() {
|
||||
return imei;
|
||||
}
|
||||
|
||||
public void setImei(String imei) {
|
||||
this.imei = imei;
|
||||
}
|
||||
|
||||
public String getImsi() {
|
||||
return imsi;
|
||||
}
|
||||
|
||||
public void setImsi(String imsi) {
|
||||
this.imsi = imsi;
|
||||
}
|
||||
|
||||
public String getWlanMac() {
|
||||
return wlanMac;
|
||||
}
|
||||
|
||||
public void setWlanMac(String wlanMac) {
|
||||
this.wlanMac = wlanMac;
|
||||
}
|
||||
|
||||
public String getDeviceMac() {
|
||||
return deviceMac;
|
||||
}
|
||||
|
||||
public void setDeviceMac(String deviceMac) {
|
||||
this.deviceMac = deviceMac;
|
||||
}
|
||||
|
||||
public String getBluetoothMac() {
|
||||
return bluetoothMac;
|
||||
}
|
||||
|
||||
public void setBluetoothMac(String bluetoothMac) {
|
||||
this.bluetoothMac = bluetoothMac;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public String getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public void setBoard(String board) {
|
||||
this.board = board;
|
||||
}
|
||||
|
||||
public String getAndroidVersion() {
|
||||
return androidVersion;
|
||||
}
|
||||
|
||||
public void setAndroidVersion(String androidVersion) {
|
||||
this.androidVersion = androidVersion;
|
||||
}
|
||||
|
||||
public int getAndroidApi() {
|
||||
return androidApi;
|
||||
}
|
||||
|
||||
public void setAndroidApi(int androidApi) {
|
||||
this.androidApi = androidApi;
|
||||
}
|
||||
|
||||
public String getBuildId() {
|
||||
return buildId;
|
||||
}
|
||||
|
||||
public void setBuildId(String buildId) {
|
||||
this.buildId = buildId;
|
||||
}
|
||||
|
||||
public String getBuildDisplayId() {
|
||||
return buildDisplayId;
|
||||
}
|
||||
|
||||
public void setBuildDisplayId(String buildDisplayId) {
|
||||
this.buildDisplayId = buildDisplayId;
|
||||
}
|
||||
}
|
||||
224
app/src/main/java/com/ttstd/dialer/bean/req/SnLocationReq.java
Normal file
224
app/src/main/java/com/ttstd/dialer/bean/req/SnLocationReq.java
Normal file
@@ -0,0 +1,224 @@
|
||||
package com.ttstd.dialer.bean.req;
|
||||
|
||||
import com.baidu.location.BDLocation;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.ttstd.dialer.manager.MapManager;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 设备定位信息请求模型
|
||||
*/
|
||||
public class SnLocationReq implements Serializable {
|
||||
private static final long serialVersionUID = -1673953474925103299L;
|
||||
|
||||
@SerializedName("country")
|
||||
private String country;
|
||||
|
||||
@SerializedName("countryCode")
|
||||
private String countryCode;
|
||||
|
||||
@SerializedName("province")
|
||||
private String province;
|
||||
|
||||
@SerializedName("city")
|
||||
private String city;
|
||||
|
||||
@SerializedName("cityCode")
|
||||
private String cityCode;
|
||||
|
||||
@SerializedName("district")
|
||||
private String district;
|
||||
|
||||
@SerializedName("street")
|
||||
private String street;
|
||||
|
||||
@SerializedName("streetNumber")
|
||||
private String streetNumber;
|
||||
|
||||
@SerializedName("address")
|
||||
private String address;
|
||||
|
||||
@SerializedName("adCode")
|
||||
private String adCode;
|
||||
|
||||
@SerializedName("town")
|
||||
private String town;
|
||||
|
||||
@SerializedName("townCode")
|
||||
private String townCode;
|
||||
|
||||
@SerializedName("locationDescribe")
|
||||
private String locationDescribe;
|
||||
|
||||
@SerializedName("longitude")
|
||||
private String longitude;
|
||||
|
||||
@SerializedName("latitude")
|
||||
private String latitude;
|
||||
|
||||
@SerializedName("mapError")
|
||||
private String mapError;
|
||||
|
||||
@SerializedName("lastSuccessfulTime")
|
||||
private LocalDateTime lastSuccessfulTime;
|
||||
|
||||
public SnLocationReq() {
|
||||
}
|
||||
|
||||
public SnLocationReq(BDLocation bdLocation) {
|
||||
setCountry(bdLocation.getCountry());
|
||||
setCountryCode(bdLocation.getCountryCode());
|
||||
setProvince(bdLocation.getProvince());
|
||||
setCity(bdLocation.getCity());
|
||||
setCityCode(bdLocation.getCityCode());
|
||||
setDistrict(bdLocation.getDistrict());
|
||||
setStreet(bdLocation.getStreet());
|
||||
setStreetNumber(bdLocation.getStreetNumber());
|
||||
setAddress(bdLocation.getAddrStr());
|
||||
setAdCode(bdLocation.getAdCode());
|
||||
setTown(bdLocation.getTown());
|
||||
setTownCode(bdLocation.getTownCode());
|
||||
setLocationDescribe(bdLocation.getLocationDescribe());
|
||||
setLongitude(MapManager.doubleToStringExact(bdLocation.getLongitude()));
|
||||
setLatitude(MapManager.doubleToStringExact(bdLocation.getLatitude()));
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getCountryCode() {
|
||||
return countryCode;
|
||||
}
|
||||
|
||||
public void setCountryCode(String countryCode) {
|
||||
this.countryCode = countryCode;
|
||||
}
|
||||
|
||||
public String getProvince() {
|
||||
return province;
|
||||
}
|
||||
|
||||
public void setProvince(String province) {
|
||||
this.province = province;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getCityCode() {
|
||||
return cityCode;
|
||||
}
|
||||
|
||||
public void setCityCode(String cityCode) {
|
||||
this.cityCode = cityCode;
|
||||
}
|
||||
|
||||
public String getDistrict() {
|
||||
return district;
|
||||
}
|
||||
|
||||
public void setDistrict(String district) {
|
||||
this.district = district;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public String getStreetNumber() {
|
||||
return streetNumber;
|
||||
}
|
||||
|
||||
public void setStreetNumber(String streetNumber) {
|
||||
this.streetNumber = streetNumber;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getAdCode() {
|
||||
return adCode;
|
||||
}
|
||||
|
||||
public void setAdCode(String adCode) {
|
||||
this.adCode = adCode;
|
||||
}
|
||||
|
||||
public String getTown() {
|
||||
return town;
|
||||
}
|
||||
|
||||
public void setTown(String town) {
|
||||
this.town = town;
|
||||
}
|
||||
|
||||
public String getTownCode() {
|
||||
return townCode;
|
||||
}
|
||||
|
||||
public void setTownCode(String townCode) {
|
||||
this.townCode = townCode;
|
||||
}
|
||||
|
||||
public String getLocationDescribe() {
|
||||
return locationDescribe;
|
||||
}
|
||||
|
||||
public void setLocationDescribe(String locationDescribe) {
|
||||
this.locationDescribe = locationDescribe;
|
||||
}
|
||||
|
||||
public String getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public void setLongitude(String longitude) {
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public String getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public void setLatitude(String latitude) {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public String getMapError() {
|
||||
return mapError;
|
||||
}
|
||||
|
||||
public void setMapError(String mapError) {
|
||||
this.mapError = mapError;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastSuccessfulTime() {
|
||||
return lastSuccessfulTime;
|
||||
}
|
||||
|
||||
public void setLastSuccessfulTime(LocalDateTime lastSuccessfulTime) {
|
||||
this.lastSuccessfulTime = lastSuccessfulTime;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.ttstd.dialer.fragment.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
@@ -13,9 +12,9 @@ import com.ttstd.dialer.adapter.AppAdapter;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentAppBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.view.EqualHeightDecoration;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBinding> {
|
||||
@@ -30,7 +29,7 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
|
||||
// public static AppFragment newInstance(List<AppInfo> apkList) {
|
||||
// AppFragment appFragment = new AppFragment();
|
||||
// Log.e(TAG, "newInstance: " + appFragment);
|
||||
// Logger.e(TAG, "newInstance: " + appFragment);
|
||||
// Bundle args = new Bundle();
|
||||
// args.putSerializable(ARG_APP_LIST, (Serializable) apkList);
|
||||
// appFragment.setArguments(args);
|
||||
@@ -43,7 +42,7 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
|
||||
public AppFragment(List<AppInfo> appInfos) {
|
||||
mAppInfos = appInfos;
|
||||
Log.e(TAG, "AppFragment: mAppInfos = " + mAppInfos.hashCode());
|
||||
Logger.e(TAG, "AppFragment: mAppInfos = " + mAppInfos.hashCode());
|
||||
}
|
||||
|
||||
public interface UpdateCallback {
|
||||
@@ -72,7 +71,7 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
|
||||
@Override
|
||||
protected void initView(Bundle bundle) {
|
||||
Log.e(TAG, "initView: " + this.hashCode());
|
||||
Logger.e(TAG, "initView: " + this.hashCode());
|
||||
|
||||
mAppAdapter = new AppAdapter(LoaderManager.getInstance(this));
|
||||
mAppAdapter.setShortcutCallback(new AppAdapter.ShortcutCallback() {
|
||||
@@ -87,8 +86,8 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
mViewDataBinding.recyclerView.setAdapter(mAppAdapter);
|
||||
|
||||
if (mAppInfos != null) {
|
||||
Log.e(TAG, "initView: mAppInfos size = " + mAppInfos.size());
|
||||
Log.e(TAG, "initView: mAppInfos = " + mAppInfos.hashCode());
|
||||
Logger.e(TAG, "initView: mAppInfos size = " + mAppInfos.size());
|
||||
Logger.e(TAG, "initView: mAppInfos = " + mAppInfos.hashCode());
|
||||
mAppAdapter.setAppInfos(mAppInfos);
|
||||
}
|
||||
|
||||
@@ -96,8 +95,8 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
|
||||
@Override
|
||||
protected void initData(Bundle savedInstanceState) {
|
||||
Log.e(TAG, "initData: ");
|
||||
Log.e(TAG, "initData: " + mViewModel.mAppUpdateData);
|
||||
Logger.e(TAG, "initData: ");
|
||||
Logger.e(TAG, "initData: " + mViewModel.mAppUpdateData);
|
||||
mViewModel.mAppUpdateData.observe(this, new Observer<Integer>() {
|
||||
@Override
|
||||
public void onChanged(Integer integer) {
|
||||
@@ -117,14 +116,14 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
|
||||
@Override
|
||||
public void fetchData() {
|
||||
Log.e(TAG, "fetchData: ");
|
||||
Logger.e(TAG, "fetchData: ");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Log.e(TAG, "onResume: ");
|
||||
Logger.e(TAG, "onResume: ");
|
||||
if (mAppInfos != null) {
|
||||
mAppAdapter.setAppInfos(mAppInfos);
|
||||
}
|
||||
@@ -133,7 +132,7 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
Log.e(TAG, "onDestroyView: ");
|
||||
Logger.e(TAG, "onDestroyView: ");
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
package com.ttstd.dialer.fragment.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.trello.rxlifecycle4.android.FragmentEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.FragmentAppBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.db.app.AppRepository;
|
||||
import com.ttstd.dialer.livedata.SingleLiveEvent;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
@@ -52,23 +49,23 @@ public class AppViewModel extends BaseViewModel<FragmentAppBinding, FragmentEven
|
||||
.subscribe(new Observer<Integer>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("updateAppInfo", "onSubscribe: ");
|
||||
Logger.e("updateAppInfo", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull Integer row) {
|
||||
Log.e("updateAppInfo", "onNext: " + row);
|
||||
Logger.e("updateAppInfo", "onNext: " + row);
|
||||
mAppUpdateData.setValue(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("updateAppInfo", "onError: " + e.getMessage());
|
||||
Logger.e("updateAppInfo", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("updateAppInfo", "onComplete: ");
|
||||
Logger.e("updateAppInfo", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.ttstd.dialer.fragment.contact;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
@@ -13,6 +12,7 @@ import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentContactBinding;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.fragment.dialog.call.CallFragment;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.view.EqualHeightDecoration;
|
||||
|
||||
import java.util.List;
|
||||
@@ -63,7 +63,7 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
|
||||
mViewModel.mContactListData.observe(this, new Observer<List<ContactInfo>>() {
|
||||
@Override
|
||||
public void onChanged(List<ContactInfo> contactInfos) {
|
||||
Log.e(TAG, "mContactListData: " + contactInfos);
|
||||
Logger.e(TAG, "mContactListData: " + contactInfos);
|
||||
mContactInfos = contactInfos;
|
||||
mHomeContactAdapter.setContactInfos(mContactInfos);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ttstd.dialer.fragment.contact;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
@@ -11,6 +10,7 @@ import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.FragmentContactBinding;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.db.contact.ContactRepository;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -48,12 +48,12 @@ public class ContactViewModel extends BaseViewModel<FragmentContactBinding, Frag
|
||||
.subscribe(new Observer<List<ContactInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getAllContacts", "onSubscribe: ");
|
||||
Logger.e("getAllContacts", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull List<ContactInfo> contactInfos) {
|
||||
Log.e("getAllContacts", "onNext: ");
|
||||
Logger.e("getAllContacts", "onNext: ");
|
||||
mContactListData.setValue(contactInfos);
|
||||
|
||||
// List<Contact> sorted = contacts.stream().sorted(new Comparator<Contact>() {
|
||||
@@ -67,12 +67,12 @@ public class ContactViewModel extends BaseViewModel<FragmentContactBinding, Frag
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getAllContacts", "onError: " + e.getMessage());
|
||||
Logger.e("getAllContacts", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getAllContacts", "onComplete: ");
|
||||
Logger.e("getAllContacts", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
@@ -29,6 +28,7 @@ import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.service.DialerAccessibilityService;
|
||||
import com.ttstd.dialer.utils.AccessibilityServiceHelper;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, FragmentCallBinding> {
|
||||
private static final String TAG = "CallFragment";
|
||||
@@ -99,7 +99,7 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, Fragment
|
||||
ft.add(this, tag);
|
||||
ft.commitAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "show: " + e.getMessage());
|
||||
Logger.e(TAG, "show: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, Fragment
|
||||
dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(dialIntent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "callNumber: " + e.getMessage());
|
||||
Logger.e(TAG, "callNumber: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.Manifest;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -24,6 +23,7 @@ import androidx.fragment.app.FragmentTransaction;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.BaseDialogFragment;
|
||||
import com.ttstd.dialer.databinding.DialogFragmentPermissionsBinding;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class PermissionDialogFragment extends BaseDialogFragment {
|
||||
private static final String TAG = "PermissionDialog";
|
||||
@@ -45,7 +45,7 @@ public class PermissionDialogFragment extends BaseDialogFragment {
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
Log.e(TAG, "onCreateView: ");
|
||||
Logger.e(TAG, "onCreateView: ");
|
||||
mBinding = DataBindingUtil.inflate(inflater, R.layout.dialog_fragment_permissions, container, false);
|
||||
mBinding.setClick(new BtnClick());
|
||||
rootView = mBinding.getRoot();
|
||||
@@ -88,7 +88,7 @@ public class PermissionDialogFragment extends BaseDialogFragment {
|
||||
ft.add(this, tag);
|
||||
ft.commitAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "show: " + e.getMessage());
|
||||
Logger.e(TAG, "show: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
@@ -22,6 +21,7 @@ import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmDialogFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentDialogShortcutBinding;
|
||||
import com.ttstd.dialer.db.app.AppInfo;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewModel, FragmentDialogShortcutBinding> {
|
||||
private static final String TAG = "ShortcutDialogFagment";
|
||||
@@ -160,7 +160,7 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
|
||||
ft.add(this, tag);
|
||||
ft.commitAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "show: " + e.getMessage());
|
||||
Logger.e(TAG, "show: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -21,6 +20,7 @@ import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentHomeBinding;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.DateUtil;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.LunarCalendarFestivalUtils;
|
||||
|
||||
/**
|
||||
@@ -107,14 +107,14 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
Log.e(TAG, "onStart: ");
|
||||
Logger.e(TAG, "onStart: ");
|
||||
registerReceivers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
Log.e(TAG, "onStop: ");
|
||||
Logger.e(TAG, "onStop: ");
|
||||
unregisterReceivers();
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
Log.e("TimeReceiver", "onReceive: " + action);
|
||||
Logger.i("TimeReceiver", "onReceive: " + action);
|
||||
switch (action) {
|
||||
case Intent.ACTION_DATE_CHANGED:
|
||||
case Intent.ACTION_TIMEZONE_CHANGED:
|
||||
@@ -184,7 +184,7 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openSettings: " + e.getMessage());
|
||||
Logger.e(TAG, "openSettings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "launchWeChat: " + e.getMessage());
|
||||
Logger.e(TAG, "launchWeChat: " + e.getMessage());
|
||||
Toaster.show("打开微信失败");
|
||||
ApkUtils.openAppStore(mContext, "com.tencent.mm");
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.ttstd.dialer.fragment.settings;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
@@ -11,6 +10,7 @@ import com.ttstd.dialer.activity.app.AppListActivity;
|
||||
import com.ttstd.dialer.activity.settings.home.SettingsActivity;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentSettingsBinding;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class SettingsFragment extends BaseMvvmFragment<SettingsViewModel, FragmentSettingsBinding> {
|
||||
private static final String TAG = "SettingsFragment";
|
||||
@@ -55,7 +55,7 @@ public class SettingsFragment extends BaseMvvmFragment<SettingsViewModel, Fragme
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openSettings: " + e.getMessage());
|
||||
Logger.e(TAG, "openSettings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.ttstd.dialer.livedata;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
@@ -20,7 +20,7 @@ public class SingleLiveEvent<T> extends MutableLiveData<T> {
|
||||
public void observe(androidx.lifecycle.LifecycleOwner owner, final Observer<? super T> observer) {
|
||||
// 警告:多个观察者同时注册时,只有一个会收到通知
|
||||
if (hasActiveObservers()) {
|
||||
Log.w(TAG, "多个观察者注册,但仅第一个会收到事件");
|
||||
Logger.w(TAG, "多个观察者注册,但仅第一个会收到事件");
|
||||
}
|
||||
|
||||
// 包装观察者,仅在事件未处理时触发
|
||||
|
||||
@@ -1,33 +1,46 @@
|
||||
package com.ttstd.dialer.manager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.usage.StorageStats;
|
||||
import android.app.usage.StorageStatsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
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.db.app.AppInfo;
|
||||
import com.ttstd.dialer.db.app.AppRepository;
|
||||
import com.ttstd.dialer.gson.GsonUtils;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.HashUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@@ -44,8 +57,10 @@ public class AppManager {
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static AppManager INSTANCE;
|
||||
private static volatile AppManager INSTANCE;
|
||||
private Context mContext;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private AppRepository mAppRepository;
|
||||
|
||||
private Set<ProgressCallback> mProgressCallbacks = new CopyOnWriteArraySet<>();
|
||||
@@ -83,13 +98,17 @@ public class AppManager {
|
||||
|
||||
public static void init(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new AppManager(context);
|
||||
synchronized (AppManager.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new AppManager(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AppManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
throw new IllegalStateException("You must be init AppManager first");
|
||||
throw new IllegalStateException("You must first initialize the AppManager");
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
@@ -97,6 +116,7 @@ public class AppManager {
|
||||
private AppManager(Context context) {
|
||||
this.mContext = context.getApplicationContext();
|
||||
this.mAppRepository = new AppRepository(context);
|
||||
this.mPackageManager = mContext.getPackageManager();
|
||||
executeAppListProcessing();
|
||||
}
|
||||
|
||||
@@ -138,7 +158,7 @@ public class AppManager {
|
||||
}, ASYNC_EXECUTOR)
|
||||
.thenRun(() -> notifyCompleted(true, "应用列表处理完成"))
|
||||
.exceptionally(throwable -> {
|
||||
Log.e(TAG, "异步处理失败", throwable);
|
||||
Logger.e(TAG, "异步处理失败", throwable);
|
||||
notifyCompleted(false, "处理失败: " + throwable.getMessage());
|
||||
return null;
|
||||
});
|
||||
@@ -150,7 +170,7 @@ public class AppManager {
|
||||
List<AppInfo> result = mAppRepository.getAllApp();
|
||||
return result != null ? result : Collections.emptyList();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "获取桌面应用列表失败", e);
|
||||
Logger.e(TAG, "获取桌面应用列表失败", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -159,13 +179,13 @@ public class AppManager {
|
||||
private CompletableFuture<Void> processFirstTimeApps() {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
notifyProgress(STEP_FIRST_INIT, 1, TOTAL_STEPS_FIRST);
|
||||
Log.i(TAG, "进入首次应用数据初始化流程");
|
||||
Logger.i(TAG, "进入首次应用数据初始化流程");
|
||||
|
||||
try {
|
||||
// 获取所有可启动应用
|
||||
List<ResolveInfo> allLauncherApps = ApkUtils.getAllLauncherResolveInfo(mContext);
|
||||
if (allLauncherApps.isEmpty()) {
|
||||
Log.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
|
||||
Logger.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -196,13 +216,13 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
|
||||
Log.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + " 个");
|
||||
Logger.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + " 个");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "首次应用数据初始化失败", e);
|
||||
Logger.e(TAG, "首次应用数据初始化失败", e);
|
||||
throw new RuntimeException("首次初始化失败", e); // 抛出异常触发回调
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
@@ -213,7 +233,7 @@ public class AppManager {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL);
|
||||
if (appInfos.isEmpty()) {
|
||||
Log.w(TAG, "桌面应用列表为空,跳过删除处理");
|
||||
Logger.w(TAG, "桌面应用列表为空,跳过删除处理");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,7 +244,7 @@ public class AppManager {
|
||||
try {
|
||||
return mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "获取应用ID失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "获取应用ID失败: " + app.getPackageName(), e);
|
||||
return -1;
|
||||
}
|
||||
})
|
||||
@@ -236,10 +256,10 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.deleteById(id);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "删除应用失败, ID: " + id, e);
|
||||
Logger.e(TAG, "删除应用失败, ID: " + id, e);
|
||||
}
|
||||
});
|
||||
Log.i(TAG, "成功删除 " + ids.size() + " 个未安装应用");
|
||||
Logger.i(TAG, "成功删除 " + ids.size() + " 个未安装应用");
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
}
|
||||
@@ -250,7 +270,7 @@ public class AppManager {
|
||||
try {
|
||||
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
|
||||
if (resolveInfos.isEmpty()) {
|
||||
Log.w(TAG, "未获取到启动器应用列表,跳过新应用处理");
|
||||
Logger.w(TAG, "未获取到启动器应用列表,跳过新应用处理");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -269,15 +289,15 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "插入新应用失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "插入新应用失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
Log.i(TAG, "成功插入 " + newApps.size() + " 个新应用");
|
||||
Logger.i(TAG, "成功插入 " + newApps.size() + " 个新应用");
|
||||
} else {
|
||||
Log.i(TAG, "没有需要插入的新应用");
|
||||
Logger.i(TAG, "没有需要插入的新应用");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "处理新应用时发生异常", e);
|
||||
Logger.e(TAG, "处理新应用时发生异常", e);
|
||||
throw new RuntimeException("新应用处理失败", e);
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
@@ -289,7 +309,7 @@ public class AppManager {
|
||||
try {
|
||||
List<AppInfo> appInfos = getAllDesktopSortApps();
|
||||
if (appInfos.isEmpty()) {
|
||||
Log.w(TAG, "无应用数据,跳过位置更新");
|
||||
Logger.w(TAG, "无应用数据,跳过位置更新");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -305,12 +325,12 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.update(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "更新应用位置失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "更新应用位置失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
Log.i(TAG, "成功更新 " + sortedApps.size() + " 个应用的位置");
|
||||
Logger.i(TAG, "成功更新 " + sortedApps.size() + " 个应用的位置");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "更新应用位置时发生异常", e);
|
||||
Logger.e(TAG, "更新应用位置时发生异常", e);
|
||||
throw new RuntimeException("位置更新失败", e);
|
||||
}
|
||||
}, ASYNC_EXECUTOR);
|
||||
@@ -330,7 +350,7 @@ public class AppManager {
|
||||
try {
|
||||
return mAppRepository.checkAppInfoExists(app.getPackageName(), app.getClassName()) > 0;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "检查应用存在性失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "检查应用存在性失败: " + app.getPackageName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -353,7 +373,7 @@ public class AppManager {
|
||||
try {
|
||||
mAppRepository.insert(app);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "更新应用插入失败: " + app.getPackageName(), e);
|
||||
Logger.e(TAG, "更新应用插入失败: " + app.getPackageName(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -373,13 +393,12 @@ public class AppManager {
|
||||
}
|
||||
|
||||
public String getDefaultAppList() {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
|
||||
|
||||
List<AppInfo> defaultApps = resolveInfos.stream()
|
||||
.filter(ri -> DEFAULT_APP_PACKAGES.contains(ri.activityInfo.packageName))
|
||||
.sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE)
|
||||
.compare(o1.activityInfo.loadLabel(pm), o2.activityInfo.loadLabel(pm)))
|
||||
.compare(o1.activityInfo.loadLabel(mPackageManager), o2.activityInfo.loadLabel(mPackageManager)))
|
||||
.map(ri -> new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name))
|
||||
.map(component -> new AppInfo(mContext, component))
|
||||
.sorted((a, b) -> Boolean.compare(
|
||||
@@ -392,4 +411,64 @@ public class AppManager {
|
||||
|
||||
return GsonUtils.toJSONString(defaultApps);
|
||||
}
|
||||
|
||||
private static final Set<String> WHITE_SYSTEM_PACKAGES = new HashSet<String>() {{
|
||||
// this.add("com.android.luancher3");
|
||||
}};
|
||||
|
||||
public List<ApkInstalledInfo> getApkInstallInfos() {
|
||||
StorageStatsManager ssm = mContext.getSystemService(StorageStatsManager.class);
|
||||
// 获取所有已安装的应用
|
||||
List<PackageInfo> installedApps = mPackageManager.getInstalledPackages(0);
|
||||
// 遍历并筛选第三方应用
|
||||
List<ApkInstalledInfo> apkInstalledInfos = installedApps.stream().filter(new Predicate<PackageInfo>() {
|
||||
@Override
|
||||
public boolean test(PackageInfo packageInfo) {
|
||||
String packageName = packageInfo.packageName;
|
||||
return !ApkUtils.isSystemApp(mContext, packageName) || WHITE_SYSTEM_PACKAGES.contains(packageName);
|
||||
}
|
||||
}).map(new Function<PackageInfo, ApkInstalledInfo>() {
|
||||
@Override
|
||||
public ApkInstalledInfo apply(PackageInfo packageInfo) {
|
||||
ApkInstalledInfo apkInstalledInfo = new ApkInstalledInfo();
|
||||
apkInstalledInfo.setPackageName(packageInfo.packageName);
|
||||
apkInstalledInfo.setAppName(packageInfo.applicationInfo.loadLabel(mPackageManager).toString());
|
||||
apkInstalledInfo.setVersionName(packageInfo.versionName);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
apkInstalledInfo.setVersionCode(packageInfo.getLongVersionCode());
|
||||
} else {
|
||||
apkInstalledInfo.setVersionCode(packageInfo.versionCode);
|
||||
}
|
||||
apkInstalledInfo.setInstallTime(packageInfo.firstInstallTime);
|
||||
apkInstalledInfo.setLastUpdateTime(packageInfo.lastUpdateTime);
|
||||
|
||||
try {
|
||||
StorageStats stats = ssm.queryStatsForPackage(
|
||||
UUID.fromString(packageInfo.applicationInfo.storageUuid.toString()),
|
||||
mContext.getPackageName(),
|
||||
UserHandle.of(UserHandle.myUserId())
|
||||
);
|
||||
|
||||
long appSize = stats.getAppBytes(); // 应用大小
|
||||
Log.e(TAG, "apply: appSize = " + appSize);
|
||||
long dataSize = stats.getDataBytes(); // 用户数据
|
||||
long cacheSize = stats.getCacheBytes(); // 缓存
|
||||
|
||||
apkInstalledInfo.setApkSize(appSize);
|
||||
apkInstalledInfo.setDataSize(dataSize);
|
||||
apkInstalledInfo.setCacheSize(cacheSize);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getApkInstallInfos: " + e.getMessage());
|
||||
}
|
||||
|
||||
Log.e(TAG, "apply: " + packageInfo.applicationInfo.publicSourceDir);
|
||||
Log.e(TAG, "apply: publicSourceDir = " + new File(packageInfo.applicationInfo.publicSourceDir).length());
|
||||
Log.e(TAG, "apply: " + packageInfo.applicationInfo.dataDir);
|
||||
apkInstalledInfo.setMd5(HashUtils.getFileMd5(new File(packageInfo.applicationInfo.publicSourceDir)));
|
||||
apkInstalledInfo.setSystemApp(ApkUtils.isSystemApp(mContext, packageInfo.packageName));
|
||||
return apkInstalledInfo;
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return apkInstalledInfos;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.ttstd.dialer.manager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.opencsv.bean.CsvToBean;
|
||||
import com.opencsv.bean.CsvToBeanBuilder;
|
||||
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
|
||||
import com.ttstd.dialer.bean.CityInfo;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -46,14 +46,14 @@ public class CsvDeserializer {
|
||||
.withIgnoreEmptyLine(true) // 忽略空行
|
||||
.withThrowExceptions(false) // 不抛出异常,便于调试
|
||||
.build();
|
||||
Log.e(TAG, "deserializeFromAssets: finish " + (System.currentTimeMillis() - time) + "ms");
|
||||
Logger.e(TAG, "deserializeFromAssets: finish " + (System.currentTimeMillis() - time) + "ms");
|
||||
// 转换并返回结果列表
|
||||
return csvToBean.parse();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error reading CSV file: " + e.getMessage());
|
||||
Logger.e(TAG, "Error reading CSV file: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during CSV deserialization: " + e.getMessage());
|
||||
Logger.e(TAG, "Error during CSV deserialization: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
// 确保关闭流,避免资源泄漏
|
||||
@@ -65,7 +65,7 @@ public class CsvDeserializer {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to close stream: " + e.getMessage());
|
||||
Logger.w(TAG, "Failed to close stream: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();
|
||||
@@ -93,7 +93,7 @@ public class CsvDeserializer {
|
||||
return csvToBean.parse();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during CSV deserialization: " + e.getMessage());
|
||||
Logger.e(TAG, "Error during CSV deserialization: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,13 @@ import com.baidu.location.LocationClient;
|
||||
import com.baidu.location.LocationClientOption;
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.gson.GsonUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -237,39 +239,41 @@ public class MapManager {
|
||||
/**
|
||||
* 存储定位结果(解耦存储逻辑)
|
||||
*/
|
||||
private void saveLocationResult(BDLocation location) {
|
||||
if (location == null) return;
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_MAP_ADDRESS_KEY, location.getAddrStr());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_MAP_LOCATION_DESCRIBE_KEY, location.getLocationDescribe());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_PROVINCE_KEY, location.getProvince());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_CITY_KEY, location.getCity());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_DISTRICT_KEY, location.getDistrict());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_TOWN_KEY, location.getTown());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_STREET_KEY, location.getStreet());
|
||||
private void saveLocationResult(BDLocation bdLocation) {
|
||||
if (bdLocation == null) return;
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_MAP_ADDRESS_KEY, bdLocation.getAddrStr());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_MAP_LOCATION_DESCRIBE_KEY, bdLocation.getLocationDescribe());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_PROVINCE_KEY, bdLocation.getProvince());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_CITY_KEY, bdLocation.getCity());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_DISTRICT_KEY, bdLocation.getDistrict());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_TOWN_KEY, bdLocation.getTown());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_STREET_KEY, bdLocation.getStreet());
|
||||
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_LONGITUDE_KEY, location.getLongitude());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_LATITUDE_KEY, location.getLatitude());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_AD_CODE_KEY, location.getAdCode());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_LONGITUDE_KEY, bdLocation.getLongitude());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_LATITUDE_KEY, bdLocation.getLatitude());
|
||||
mMMKV.encode(CommonConfig.CURRENT_LOCATION_AD_CODE_KEY, bdLocation.getAdCode());
|
||||
|
||||
// mMMKV.encode(CommonConfig.CURRENT_LOCATION_LOCATION_ID_KEY, location.getLocationID());
|
||||
// mMMKV.encode(CommonConfig.CURRENT_LOCATION_LOCATION_ID_KEY, bdLocation.getLocationID());
|
||||
|
||||
mMMKV.encode("MapError", "-");
|
||||
|
||||
Map<String, Object> params = new LinkedHashMap<>();
|
||||
params.put("sn", SystemUtils.getSerial());
|
||||
params.put("address", location.getAddrStr());
|
||||
params.put("location_describe", location.getLocationDescribe());
|
||||
params.put("longitude", location.getLongitude());
|
||||
params.put("latitude", location.getLatitude());
|
||||
params.put("ad_code", location.getAdCode());
|
||||
params.put("coor_type", location.getCoorType());
|
||||
params.put("address", bdLocation.getAddrStr());
|
||||
params.put("location_describe", bdLocation.getLocationDescribe());
|
||||
params.put("longitude", bdLocation.getLongitude());
|
||||
params.put("latitude", bdLocation.getLatitude());
|
||||
params.put("ad_code", bdLocation.getAdCode());
|
||||
params.put("coor_type", bdLocation.getCoorType());
|
||||
|
||||
Logger.e(TAG, "saveLocationResult", "location = " + GsonUtils.toJSONString(location));
|
||||
Logger.e(TAG, "saveLocationResult", "bdLocation = " + GsonUtils.toJSONString(bdLocation));
|
||||
|
||||
WeatherManager.getInstance().setAdCode(location.getAdCode());
|
||||
WeatherManager.getInstance().setAdCode(bdLocation.getAdCode());
|
||||
|
||||
SnLocationReq snLocation = new SnLocationReq(bdLocation);
|
||||
|
||||
LiveEventBus.get(CommonConfig.LOCATION_CHANGED_KEY)
|
||||
.post(location);
|
||||
.post(snLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,4 +366,10 @@ public class MapManager {
|
||||
}
|
||||
mListeners.clear(); // 清除所有回调,避免内存泄漏
|
||||
}
|
||||
|
||||
public static String doubleToStringExact(double value) {
|
||||
// 使用 BigDecimal 避免科学计数法
|
||||
return new BigDecimal(Double.toString(value)).toPlainString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.ttstd.dialer.manager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus;
|
||||
import com.qweather.sdk.Callback;
|
||||
@@ -59,7 +58,7 @@ public class WeatherManager {
|
||||
.setLogEnable(BuildConfig.DEBUG) // 启用调试日志(生产环境建议设置为 false)
|
||||
.setTokenGenerator(jwt);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "QWeatherUtils: " + e.getMessage());
|
||||
Logger.e(TAG, "QWeatherUtils: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -72,7 +71,7 @@ public class WeatherManager {
|
||||
|
||||
public static WeatherManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
throw new IllegalStateException("You must be init WeatherManager first");
|
||||
throw new IllegalStateException("You must first initialize the WeatherManager");
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
@@ -99,7 +98,7 @@ public class WeatherManager {
|
||||
public void subscribe(@NonNull ObservableEmitter<List<CityInfo>> emitter) throws Throwable {
|
||||
long time = System.currentTimeMillis();
|
||||
List<CityInfo> cityInfos = CsvDeserializer.deserializeFromAssets(mContext, "China-City-List-latest.csv");
|
||||
Log.e(TAG, "subscribe: deserializeFromAssets time = " + (System.currentTimeMillis() - time) + "ms");
|
||||
Logger.e(TAG, "subscribe: deserializeFromAssets time = " + (System.currentTimeMillis() - time) + "ms");
|
||||
emitter.onNext(cityInfos);
|
||||
}
|
||||
})
|
||||
@@ -152,7 +151,7 @@ public class WeatherManager {
|
||||
|
||||
public void getWeatherNow(String adCode, Callback<WeatherNowResponse> callback) {
|
||||
String locationID = getLocationID(adCode);
|
||||
Log.e(TAG, "getWeatherNow: locationID = " + locationID);
|
||||
Logger.e(TAG, "getWeatherNow: locationID = " + locationID);
|
||||
WeatherParameter parameter = new WeatherParameter(locationID)
|
||||
.lang(Lang.ZH_HANS)
|
||||
.unit(Unit.METRIC);
|
||||
@@ -161,7 +160,7 @@ public class WeatherManager {
|
||||
|
||||
public void getWeather24h(String adCode, Callback<WeatherHourlyResponse> callback) {
|
||||
String locationID = getLocationID(adCode);
|
||||
Log.e(TAG, "getWeatherNow: locationID = " + locationID);
|
||||
Logger.e(TAG, "getWeatherNow: locationID = " + locationID);
|
||||
WeatherParameter parameter = new WeatherParameter(locationID)
|
||||
.lang(Lang.ZH_HANS)
|
||||
.unit(Unit.METRIC);
|
||||
@@ -170,7 +169,7 @@ public class WeatherManager {
|
||||
|
||||
public void getWeather10D(String adCode, Callback<WeatherDailyResponse> callback) {
|
||||
String locationID = getLocationID(adCode);
|
||||
Log.e(TAG, "getWeather10D: locationID = " + locationID);
|
||||
Logger.e(TAG, "getWeather10D: locationID = " + locationID);
|
||||
WeatherParameter parameter = new WeatherParameter(locationID)
|
||||
.lang(Lang.ZH_HANS)
|
||||
.unit(Unit.METRIC);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
/**
|
||||
* 设备控制统一异常:屏蔽不同SDK的异常差异
|
||||
*/
|
||||
public class DeviceControlException extends RuntimeException {
|
||||
public DeviceControlException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DeviceControlException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.ttstd.dialer.mdm.impl.EmuiMdmDeviceController;
|
||||
import com.ttstd.dialer.mdm.impl.HonorHemDeviceController;
|
||||
import com.ttstd.dialer.mdm.impl.SystemSignatureController;
|
||||
|
||||
/**
|
||||
* 设备控制器工厂:根据配置创建对应厂家的适配器实例
|
||||
*/
|
||||
public class DeviceControllerFactory {
|
||||
// 厂家类型枚举
|
||||
public enum VendorType {
|
||||
SYSTEM_SIGNATURE,//系统签名
|
||||
HONOR_HEM, // 荣耀HEM
|
||||
HUAWEI_MDM, // 华为MDM
|
||||
XIAOMI_MDM // 小米MDM(扩展用)
|
||||
}
|
||||
|
||||
// 单例工厂(可选,也可使用依赖注入)
|
||||
private static DeviceControllerFactory instance;
|
||||
private Context appContext;
|
||||
|
||||
private DeviceControllerFactory(Context context) {
|
||||
this.appContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
public static DeviceControllerFactory getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
synchronized (DeviceControllerFactory.class) {
|
||||
if (instance == null) {
|
||||
instance = new DeviceControllerFactory(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
// 创建对应厂家的控制器
|
||||
public IDeviceController createController(VendorType vendorType) {
|
||||
switch (vendorType) {
|
||||
case SYSTEM_SIGNATURE:
|
||||
return new SystemSignatureController(appContext);
|
||||
case HONOR_HEM:
|
||||
return new HonorHemDeviceController(appContext);
|
||||
case HUAWEI_MDM:
|
||||
// 扩展:实现华为MDM的适配器
|
||||
return new EmuiMdmDeviceController(appContext);
|
||||
// throw new UnsupportedOperationException("华为MDM适配器未实现");
|
||||
case XIAOMI_MDM:
|
||||
// 扩展:实现小米MDM的适配器
|
||||
// return new XiaomiMdmDeviceController(appContext);
|
||||
throw new UnsupportedOperationException("小米MDM适配器未实现");
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的厂家类型: " + vendorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* 业务层示例:只依赖抽象接口,不耦合具体SDK
|
||||
*/
|
||||
public class DeviceManagerService {
|
||||
private static DeviceManagerService sInstance;
|
||||
private Context mContext;
|
||||
private IDeviceController deviceController;
|
||||
|
||||
public static void init(@NonNull Context context) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Context cannot be null!");
|
||||
}
|
||||
if (sInstance == null) {
|
||||
synchronized (DeviceManagerService.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new DeviceManagerService(context.getApplicationContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static DeviceManagerService getInstance() {
|
||||
if (sInstance == null) {
|
||||
throw new IllegalStateException("DeviceManagerService must be initialized first! Call init() in Application.");
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// 初始化:通过工厂创建控制器(可配置化,比如从sp/配置文件读取厂家类型)
|
||||
public DeviceManagerService(Context context) {
|
||||
this.mContext = context;
|
||||
// 实际项目中可通过配置动态指定厂家(如从服务器下发、本地配置读取)
|
||||
DeviceControllerFactory.VendorType vendor = DeviceControllerFactory.VendorType.SYSTEM_SIGNATURE;
|
||||
this.deviceController = DeviceControllerFactory.getInstance(context)
|
||||
.createController(vendor);
|
||||
}
|
||||
|
||||
|
||||
public String getSerial() {
|
||||
return deviceController.getSerial();
|
||||
}
|
||||
|
||||
public String getImei() {
|
||||
return deviceController.getImei();
|
||||
}
|
||||
|
||||
public String getImsi() {
|
||||
return deviceController.getImsi();
|
||||
}
|
||||
|
||||
public String getWlanMac() {
|
||||
return deviceController.getWlanMac();
|
||||
}
|
||||
|
||||
public String getDeviceMac() {
|
||||
return deviceController.getDeviceMac();
|
||||
}
|
||||
|
||||
public String getBluetoothMac() {
|
||||
return deviceController.getBluetoothMac();
|
||||
}
|
||||
|
||||
public String getBuildId() {
|
||||
return deviceController.getBuildId();
|
||||
}
|
||||
|
||||
public String getBuildDisplayId() {
|
||||
return deviceController.getBuildDisplayId();
|
||||
}
|
||||
|
||||
public boolean setDevelopmentOption(boolean enabled) {
|
||||
return deviceController.setDevelopmentOption(enabled);
|
||||
}
|
||||
|
||||
public boolean setUsbDebugMode(boolean enabled) {
|
||||
return deviceController.setUsbDebugMode(enabled);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
/**
|
||||
* 设备控制抽象接口:定义所有需要的设备控制能力,与具体SDK解耦
|
||||
*/
|
||||
public interface IDeviceController {
|
||||
//获取设备SN
|
||||
String getSerial();
|
||||
|
||||
//IMEI
|
||||
String getImei();
|
||||
|
||||
//IMSI
|
||||
String getImsi();
|
||||
|
||||
//WLAN MAC
|
||||
String getWlanMac();
|
||||
|
||||
//设备WLAN MAC
|
||||
String getDeviceMac();
|
||||
|
||||
//蓝牙地址
|
||||
String getBluetoothMac();
|
||||
|
||||
String getBuildId();
|
||||
|
||||
String getBuildDisplayId();
|
||||
|
||||
boolean setDevelopmentOption(boolean enabled);
|
||||
|
||||
boolean setUsbDebugMode(boolean enabled);
|
||||
|
||||
//设置默认桌面
|
||||
boolean setDefaultLauncher(String packageNmae, String className);
|
||||
|
||||
//安装应用
|
||||
void installPackage(String path);
|
||||
|
||||
//卸载应用
|
||||
void uninstallPackage(String packageNmae);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.ttstd.dialer.mdm;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SystemDeviceController implements IDeviceController {
|
||||
private Context mContext;
|
||||
|
||||
public SystemDeviceController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDisallowedRunningApp(String pkg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDisallowedRunningApp(String pkg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisallowedRunningApp(String pkg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallPackage(String pkg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownDevice() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebootDevice() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultLauncher(String pkg, String className) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableBluetoothTransfer(boolean disable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInstallPackageTrustList(List<String> packages) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getInstallPackageTrustList() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.ttstd.dialer.mdm.impl;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
||||
import com.ttstd.dialer.mdm.IDeviceController;
|
||||
|
||||
/**
|
||||
* 华为MDM SDK适配器:适配抽象接口到华为MDM SDK的具体实现
|
||||
*/
|
||||
public class EmuiMdmDeviceController implements IDeviceController {
|
||||
private ComponentName adminName;
|
||||
|
||||
|
||||
public EmuiMdmDeviceController(Context context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerial() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImei() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImsi() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWlanMac() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceMac() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBluetoothMac() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildDisplayId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDevelopmentOption(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUsbDebugMode(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDefaultLauncher(String packageNmae, String className) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installPackage(String path) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallPackage(String packageNmae) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.ttstd.dialer.mdm.impl;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
||||
import com.ttstd.dialer.mdm.IDeviceController;
|
||||
|
||||
/**
|
||||
* 荣耀HEM SDK适配器:适配抽象接口到荣耀HEM SDK的具体实现
|
||||
*/
|
||||
public class HonorHemDeviceController implements IDeviceController {
|
||||
private ComponentName adminName; // 荣耀HEM需要的管理员组件
|
||||
|
||||
// 构造器:初始化荣耀HEM SDK的核心实例
|
||||
public HonorHemDeviceController(Context context) {
|
||||
|
||||
}
|
||||
|
||||
// 实现抽象接口:适配荣耀HEM的具体API
|
||||
@Override
|
||||
public String getSerial() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImei() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImsi() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWlanMac() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceMac() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBluetoothMac() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildDisplayId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDevelopmentOption(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUsbDebugMode(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDefaultLauncher(String packageNmae, String className) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installPackage(String path) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallPackage(String packageNmae) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.ttstd.dialer.mdm.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.ttstd.dialer.mdm.IDeviceController;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
/**
|
||||
* 具有系统签名时使用这个
|
||||
*/
|
||||
public class SystemSignatureController implements IDeviceController {
|
||||
private Context mContext;
|
||||
|
||||
public SystemSignatureController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getSerial() {
|
||||
return SystemUtils.getSerial();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImei() {
|
||||
return SystemUtils.getDefaultImei(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImsi() {
|
||||
return SystemUtils.getDefaultImSi(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWlanMac() {
|
||||
return SystemUtils.getWlanMacAddress(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceMac() {
|
||||
return SystemUtils.getFactoryMacAddresses(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBluetoothMac() {
|
||||
return SystemUtils.getBluetoothMacAddress(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildId() {
|
||||
return SystemUtils.getBuildId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildDisplayId() {
|
||||
return SystemUtils.getBuildDisplayId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDevelopmentOption(boolean enabled) {
|
||||
return Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUsbDebugMode(boolean enabled) {
|
||||
return Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setDefaultLauncher(String packageNmae, String className) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installPackage(String path) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstallPackage(String packageNmae) {
|
||||
|
||||
}
|
||||
}
|
||||
36
app/src/main/java/com/ttstd/dialer/network/BaseObserver.java
Normal file
36
app/src/main/java/com/ttstd/dialer/network/BaseObserver.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public abstract class BaseObserver<T> implements Observer<T> {
|
||||
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
// 订阅开始(可添加加载框)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull T t) {
|
||||
// 请求成功
|
||||
onSuccess(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
// 请求失败
|
||||
onFailure(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
// 请求完成
|
||||
}
|
||||
|
||||
// 成功回调(子类实现)
|
||||
public abstract void onSuccess(T t);
|
||||
|
||||
// 失败回调(子类实现)
|
||||
public abstract void onFailure(Throwable e);
|
||||
}
|
||||
340
app/src/main/java/com/ttstd/dialer/network/OkHttpManager.java
Normal file
340
app/src/main/java/com/ttstd/dialer/network/OkHttpManager.java
Normal file
@@ -0,0 +1,340 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.network.api.SnApi;
|
||||
import com.ttstd.dialer.network.interceptor.AuthInterceptor;
|
||||
import com.ttstd.dialer.utils.FileUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.Dispatcher;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
/**
|
||||
* OkHttp + Retrofit 管理类
|
||||
* 单例模式 + 避免内存泄漏 + 增强异常处理 + 提升扩展性
|
||||
*/
|
||||
public class OkHttpManager {
|
||||
private static final String TAG = "OkHttpManager";
|
||||
|
||||
// 超时时间(抽成常量,便于统一维护)
|
||||
private static final int DEFAULT_TIMEOUT_SECONDS = 5;
|
||||
// 缓存大小 64MB(使用更语义化的命名)
|
||||
private static final long CACHE_SIZE_BYTES = 1024 * 1024 * 64L;
|
||||
// 缓存目录名称(避免硬编码)
|
||||
private static final String OKHTTP_CACHE_DIR = "OkHttpCache";
|
||||
|
||||
// 静态单例(使用volatile保证可见性,防止指令重排)
|
||||
private static volatile OkHttpManager sInstance;
|
||||
|
||||
// 使用Application Context避免内存泄漏
|
||||
private final Context mAppContext;
|
||||
private Retrofit mRetrofit;
|
||||
private OkHttpClient mOkHttpClient;
|
||||
|
||||
/**
|
||||
* 初始化(建议在Application中调用)
|
||||
*
|
||||
* @param context 上下文(内部会转为Application Context)
|
||||
*/
|
||||
public static void init(@NonNull Context context) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Context cannot be null!");
|
||||
}
|
||||
if (sInstance == null) {
|
||||
synchronized (OkHttpManager.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new OkHttpManager(context.getApplicationContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*
|
||||
* @return OkHttpManager实例
|
||||
* @throws IllegalStateException 未初始化时抛出
|
||||
*/
|
||||
public static OkHttpManager getInstance() {
|
||||
if (sInstance == null) {
|
||||
throw new IllegalStateException("OkHttpManager must be initialized first! Call init() in Application.");
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有构造方法(禁止外部实例化)
|
||||
*
|
||||
* @param appContext Application Context
|
||||
*/
|
||||
private OkHttpManager(@NonNull Context appContext) {
|
||||
this.mAppContext = appContext;
|
||||
initOkHttpClient();
|
||||
initRetrofit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化OkHttpClient(拆分方法,单一职责)
|
||||
*/
|
||||
private void initOkHttpClient() {
|
||||
if (mOkHttpClient != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 构建调度器(控制并发)
|
||||
Dispatcher dispatcher = new Dispatcher();
|
||||
dispatcher.setMaxRequests(1); // 全局最大并发
|
||||
dispatcher.setMaxRequestsPerHost(1); // 单Host最大并发
|
||||
|
||||
// 2. 构建缓存(增强异常处理)
|
||||
Cache cache = createCache();
|
||||
|
||||
// 3. 构建OkHttpClient
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder()
|
||||
.connectTimeout(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.writeTimeout(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.readTimeout(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.dispatcher(dispatcher)
|
||||
.connectionPool(new ConnectionPool(20, 1, TimeUnit.SECONDS))
|
||||
.addInterceptor(new AuthInterceptor()) // 添加认证拦截器
|
||||
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
|
||||
@Override
|
||||
public void log(@NotNull String s) {
|
||||
Logger.e("HttpLoggingInterceptor", "log: " + s);
|
||||
}
|
||||
}).setLevel(HttpLoggingInterceptor.Level.BASIC))
|
||||
.retryOnConnectionFailure(true);// 连接失败重试
|
||||
|
||||
|
||||
// 缓存非空时才设置(避免空指针)
|
||||
if (cache != null) {
|
||||
builder.cache(cache);
|
||||
}
|
||||
|
||||
mOkHttpClient = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建OkHttp缓存(单独抽离,便于维护)
|
||||
*
|
||||
* @return Cache实例(创建失败返回null)
|
||||
*/
|
||||
private Cache createCache() {
|
||||
try {
|
||||
String cacheDirPath = FileUtils.getCacheDir(mAppContext);
|
||||
if (cacheDirPath == null || cacheDirPath.isEmpty()) {
|
||||
Log.w(TAG, "Cache directory path is empty, skip cache creation");
|
||||
return null;
|
||||
}
|
||||
|
||||
File cacheDir = new File(cacheDirPath, OKHTTP_CACHE_DIR);
|
||||
// 确保缓存目录存在(否则OkHttp会抛异常)
|
||||
if (!cacheDir.exists() && !cacheDir.mkdirs()) {
|
||||
Log.e(TAG, "Failed to create cache directory: " + cacheDir.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 校验缓存目录可写
|
||||
if (!cacheDir.canWrite()) {
|
||||
Log.e(TAG, "Cache directory is not writable: " + cacheDir.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
|
||||
Cache cache = new Cache(cacheDir, CACHE_SIZE_BYTES);
|
||||
// 验证缓存是否可用(避免创建了但不可用的情况)
|
||||
// cache.getCacheDirectory().getFreeSpace();
|
||||
return cache;
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "No permission to create cache directory", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "Cache directory path is null", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Retrofit(拆分方法,单一职责)
|
||||
*/
|
||||
private void initRetrofit() {
|
||||
if (mRetrofit != null || mOkHttpClient == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRetrofit = new Retrofit.Builder()
|
||||
.client(mOkHttpClient)
|
||||
.baseUrl(UrlConstants.ROOT_URL) // 抽离URL常量,便于环境切换(开发/测试/生产)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Retrofit实例(对外提供API创建入口)
|
||||
*
|
||||
* @return Retrofit实例
|
||||
*/
|
||||
public Retrofit getRetrofit() {
|
||||
if (mRetrofit == null) {
|
||||
// 双重检查,防止并发问题
|
||||
synchronized (this) {
|
||||
if (mRetrofit == null) {
|
||||
initRetrofit();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mRetrofit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取OkHttpClient实例(对外暴露,便于扩展)
|
||||
*
|
||||
* @return OkHttpClient实例
|
||||
*/
|
||||
public OkHttpClient getOkHttpClient() {
|
||||
return mOkHttpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁单例(可选,比如App退出时)
|
||||
* 避免内存泄漏(虽然用了AppContext,但主动销毁更规范)
|
||||
*/
|
||||
public static void destroy() {
|
||||
if (sInstance != null) {
|
||||
// 关闭OkHttp连接池,释放资源
|
||||
sInstance.mOkHttpClient.connectionPool().evictAll();
|
||||
// 关闭Dispatcher,取消所有请求
|
||||
sInstance.mOkHttpClient.dispatcher().cancelAll();
|
||||
sInstance.mRetrofit = null;
|
||||
sInstance.mOkHttpClient = null;
|
||||
sInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static MultipartBody.Part getBodyPart(String type, String name, File file) {
|
||||
MediaType mediaType = MediaType.Companion.parse(type);
|
||||
RequestBody fileBody = RequestBody.Companion.create(file, mediaType);
|
||||
MultipartBody.Part part = MultipartBody.Part.createFormData(name, file.getName(), fileBody);
|
||||
return part;
|
||||
}
|
||||
|
||||
public static RequestBody convertToRequestBodyjson(Object param) {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(param);
|
||||
Log.e(TAG, "convertToRequestBodyjson: " + json);
|
||||
MediaType mediaType = MediaType.Companion.parse("application/json; charset=utf-8");
|
||||
RequestBody requestBody = RequestBody.Companion.create(json, mediaType);
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
public static RequestBody convertToRequestBody(Object param) {
|
||||
MediaType mediaType = MediaType.Companion.parse("text/plain");
|
||||
RequestBody requestBody = RequestBody.Companion.create(String.valueOf(param), mediaType);
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
public static RequestBody convertToRequestBody(String param) {
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), param);
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
public static RequestBody convertToRequestBody(int param) {
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), String.valueOf(param));
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
public static RequestBody convertToRequestBody(long param) {
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), String.valueOf(param));
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 统一线程调度 + 生命周期管理
|
||||
*
|
||||
* @param observable 接口Observable
|
||||
* @param provider 生命周期管理者(Activity/Fragment)
|
||||
* @return 处理后的Observable
|
||||
*/
|
||||
public <T> Observable<T> schedule(Observable<T> observable, BehaviorSubject<ActivityEvent> provider) {
|
||||
return observable
|
||||
// 子线程执行请求
|
||||
.subscribeOn(Schedulers.io())
|
||||
// 主线程回调
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// 生命周期管理:页面销毁自动取消请求,防止内存泄漏
|
||||
.compose(RxLifecycle.bindUntilEvent(provider, ActivityEvent.DESTROY));
|
||||
}
|
||||
|
||||
public <T> Observable<T> schedule(Observable<T> observable) {
|
||||
return observable
|
||||
// 子线程执行请求
|
||||
.subscribeOn(Schedulers.io())
|
||||
// 主线程回调
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getSnRegisterObservable(BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(mRetrofit.create(SnApi.class).register(SystemUtils.getSerial(), Build.MODEL), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<Void>> getUpdateHardwareInfoObservable(SnHardwareInfoReq snHardwareInfoReq) {
|
||||
RequestBody body = convertToRequestBodyjson(snHardwareInfoReq);
|
||||
return schedule(mRetrofit.create(SnApi.class).updateHardwareInfo(body));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadScreenshotObservable(MultipartBody.Part part) {
|
||||
return schedule(mRetrofit.create(SnApi.class).uploadScreenshot(part));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadLocationObservable(SnLocationReq locationReq, BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(mRetrofit.create(SnApi.class).uploadLocation(convertToRequestBodyjson(locationReq)), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadLocationObservable(SnLocationReq locationReq) {
|
||||
return schedule(mRetrofit.create(SnApi.class).uploadLocation(convertToRequestBodyjson(locationReq)));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<DeveloperOptions>> getDeveloperOptionsObservable(BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(mRetrofit.create(SnApi.class).getDeveloperOptions(), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<DeveloperOptions>> getDeveloperOptionsObservable() {
|
||||
return schedule(mRetrofit.create(SnApi.class).getDeveloperOptions());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadInstallApksObservable(RequestBody body) {
|
||||
return schedule(mRetrofit.create(SnApi.class).uploadInstallApks(body));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public abstract class RetryCallback<T> implements Callback<T> {
|
||||
|
||||
private static final String TAG = "RetryCallback";
|
||||
|
||||
private int mRetryCount;
|
||||
private long mRetryInterval;
|
||||
|
||||
private int mCurrentRetryCount;
|
||||
|
||||
private boolean isExecuting;
|
||||
|
||||
private Call<T> mCall;
|
||||
|
||||
private Timer timer = new Timer();
|
||||
|
||||
public RetryCallback(Call<T> call, int retryCount, long retryInterval) {
|
||||
isExecuting = true;
|
||||
mCall = call;
|
||||
mRetryCount = retryCount;
|
||||
mRetryInterval = retryInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
|
||||
Log.e(TAG, "onResponse");
|
||||
isExecuting = false;
|
||||
if (!response.isSuccessful() && mCurrentRetryCount < mRetryCount) {
|
||||
mCurrentRetryCount++;
|
||||
retryRequest(call);
|
||||
} else {
|
||||
onRequestResponse(call, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
|
||||
Log.e(TAG, "onFailure");
|
||||
isExecuting = false;
|
||||
if (mCurrentRetryCount < mRetryCount) {
|
||||
mCurrentRetryCount++;
|
||||
Log.e(TAG, "onFailure: " + "RetryCount: " + mCurrentRetryCount);
|
||||
Log.e(TAG, "onFailure: " + "RetryResponse: " + call.request());
|
||||
retryRequest(call);
|
||||
} else {
|
||||
onRequestFail(call, t);
|
||||
}
|
||||
}
|
||||
|
||||
private void retryRequest(final Call<T> call) {
|
||||
Log.e(TAG, "retryRequest");
|
||||
onStartRetry();
|
||||
TimerTask timerTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (RetryCallback.this) {
|
||||
mCall = call.clone();
|
||||
mCall.enqueue(RetryCallback.this);
|
||||
isExecuting = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
timer.schedule(timerTask, mRetryInterval);
|
||||
}
|
||||
|
||||
public void cancelCall() {
|
||||
synchronized (this) {
|
||||
if (!isExecuting) {
|
||||
timer.cancel();
|
||||
} else {
|
||||
mCall.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void onRequestResponse(Call call, Response response);
|
||||
|
||||
public abstract void onRequestFail(Call call, Throwable t);
|
||||
|
||||
public abstract void onStartRetry();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.ttstd.dialer.utils.HashUtils;
|
||||
|
||||
import java.util.SortedMap;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class SecurityUtils {
|
||||
public static String generateSign(SortedMap<String, String> params) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
params.forEach(new BiConsumer<String, String>() {
|
||||
@Override
|
||||
public void accept(String key, String value) {
|
||||
sb.append(key).append("=").append(value).append("&");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (String key : params.keySet()) {
|
||||
sb.append(key).append("=").append(params.get(key)).append("&");
|
||||
}
|
||||
}
|
||||
sb.setLength(sb.length() - 1);
|
||||
return HashUtils.getSHA256String(sb.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
public class UrlAddress {
|
||||
|
||||
}
|
||||
19
app/src/main/java/com/ttstd/dialer/network/UrlConstants.java
Normal file
19
app/src/main/java/com/ttstd/dialer/network/UrlConstants.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
public class UrlConstants {
|
||||
public static final String ROOT_URL = "http://192.168.100.111:8000/api/v1/sn/";
|
||||
|
||||
//设备注册
|
||||
public static final String SN_REGISTER = "register";
|
||||
//上传硬件信息
|
||||
public static final String UPDATE_HARDWARE_INFO = "update_hardware_info";
|
||||
//上传屏幕截图
|
||||
public static final String UPLOAD_SCREENSHOT = "upload_screenshot";
|
||||
//上传定位地址
|
||||
public static final String UPLOAD_LOCATION = "upload_location";
|
||||
//获取开发者选项
|
||||
public static final String DEVELOPER_OPTIONS = "get_developer_options";
|
||||
/*上传设备已安装应用列表*/
|
||||
public static final String UPLOAD_INSTALL_APKS = "upload_install_apks";
|
||||
|
||||
}
|
||||
61
app/src/main/java/com/ttstd/dialer/network/api/SnApi.java
Normal file
61
app/src/main/java/com/ttstd/dialer/network/api/SnApi.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package com.ttstd.dialer.network.api;
|
||||
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.network.UrlConstants;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Multipart;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Part;
|
||||
|
||||
public interface SnApi {
|
||||
@FormUrlEncoded
|
||||
@POST(UrlConstants.SN_REGISTER)
|
||||
Observable<BaseResponse> register(
|
||||
@Field("sn") String sn,
|
||||
@Field("model") String model
|
||||
);
|
||||
|
||||
|
||||
@POST(UrlConstants.UPDATE_HARDWARE_INFO)
|
||||
Observable<BaseResponse<Void>> updateHardwareInfo(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
@Multipart
|
||||
@POST(UrlConstants.UPLOAD_SCREENSHOT)
|
||||
Observable<BaseResponse> uploadScreenshot(
|
||||
@Part MultipartBody.Part body
|
||||
);
|
||||
|
||||
@Multipart
|
||||
@POST(UrlConstants.UPLOAD_SCREENSHOT)
|
||||
Call<BaseResponse> uploadScreenshotCall(
|
||||
@Part MultipartBody.Part body
|
||||
);
|
||||
|
||||
@POST(UrlConstants.UPLOAD_LOCATION)
|
||||
Observable<BaseResponse> uploadLocation(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
|
||||
@GET(UrlConstants.DEVELOPER_OPTIONS)
|
||||
Observable<BaseResponse<DeveloperOptions>> getDeveloperOptions(
|
||||
|
||||
);
|
||||
|
||||
@POST(UrlConstants.UPLOAD_INSTALL_APKS)
|
||||
Observable<BaseResponse> uploadInstallApks(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.ttstd.dialer.network.interceptor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.network.SecurityUtils;
|
||||
import com.ttstd.dialer.utils.NativeUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* 认证拦截器
|
||||
* 功能:为指定请求自动添加 Authorization Header,支持白名单过滤。
|
||||
*/
|
||||
public class AuthInterceptor implements Interceptor {
|
||||
|
||||
// 定义白名单路径(这些路径的请求将不添加Header)
|
||||
private static final Set<String> WHITE_LIST_PATHS = new HashSet<>(Arrays.asList(
|
||||
"/api/v1/example"
|
||||
// 可以继续添加其他白名单路径
|
||||
));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Response intercept(@NonNull Chain chain) throws IOException {
|
||||
Request originalRequest = chain.request();
|
||||
|
||||
// 1. 白名单检查:如果请求的URL路径在白名单中,则直接放行,不添加Header
|
||||
String path = originalRequest.url().encodedPath();
|
||||
if (WHITE_LIST_PATHS.contains(path)) {
|
||||
return chain.proceed(originalRequest);
|
||||
}
|
||||
|
||||
String sn = DeviceManagerService.getInstance().getSerial();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String nonce = NativeUtils.getNonce();
|
||||
|
||||
SortedMap<String, String> headersMap = new TreeMap<>();
|
||||
headersMap.put("X-Device-SN", sn);
|
||||
headersMap.put("X-Nonce", nonce);
|
||||
headersMap.put("X-Timestamp", String.valueOf(timestamp));
|
||||
String sign = SecurityUtils.generateSign(headersMap);
|
||||
headersMap.put("X-Sign", sign);
|
||||
|
||||
// 2. 构建请求头(所有参数放header,防止URL被抓包泄露)
|
||||
Headers headers = Headers.of(headersMap);
|
||||
|
||||
Request newRequest = originalRequest.newBuilder()
|
||||
.headers(headers)
|
||||
.build();
|
||||
|
||||
// 3. 继续执行请求链
|
||||
return chain.proceed(newRequest);
|
||||
}
|
||||
}
|
||||
281
app/src/main/java/com/ttstd/dialer/push/PushExecutor.java
Normal file
281
app/src/main/java/com/ttstd/dialer/push/PushExecutor.java
Normal file
@@ -0,0 +1,281 @@
|
||||
package com.ttstd.dialer.push;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
import com.ttstd.dialer.bean.ApkInstalledInfo;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.PushMessage;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.manager.AppManager;
|
||||
import com.ttstd.dialer.manager.MapManager;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.network.BaseObserver;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import com.ttstd.dialer.utils.CmdUtil;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.RebootUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
public class PushExecutor {
|
||||
private static final String TAG = "PushExecutor";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static PushExecutor sInstance;
|
||||
|
||||
private Context mContext;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private static final String DEVICE_REFRESH = "1";
|
||||
private static final String DEVICE_SCREEN_SNAPSHOT = "2";
|
||||
private static final String DEVICE_REBOOT = "3";
|
||||
private static final String DEVICE_SHUTDOEN = "4";
|
||||
private static final String DEVICE_LOCATE = "5";
|
||||
private static final String DEVICE_RESTORE = "6";
|
||||
private static final String DEVICE_DEVELOPER = "7";
|
||||
|
||||
private PushExecutor(Context context) {
|
||||
if (context == null) {
|
||||
throw new RuntimeException("Context is NULL");
|
||||
}
|
||||
this.mContext = context;
|
||||
this.mPackageManager = mContext.getPackageManager();
|
||||
}
|
||||
|
||||
public static void init(Context context) {
|
||||
if (sInstance == null) {
|
||||
synchronized (PushExecutor.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new PushExecutor(context.getApplicationContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PushExecutor getInstance() {
|
||||
if (sInstance == null) {
|
||||
throw new IllegalStateException("You must first initialize the PushExecutor");
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pushMessage 将厂家推送消息转换为自定义消息
|
||||
* 方便以后更换推送sdk
|
||||
*/
|
||||
public void setPushMessage(PushMessage pushMessage) {
|
||||
String contentType = pushMessage.contentType;
|
||||
String title = pushMessage.title;
|
||||
String content = pushMessage.message;
|
||||
String extra = pushMessage.extra;
|
||||
|
||||
switch (contentType) {
|
||||
case DEVICE_REFRESH:
|
||||
getDeviceSettings();
|
||||
uploadDeviceInfo();
|
||||
break;
|
||||
case DEVICE_SCREEN_SNAPSHOT:
|
||||
screenSnapshot();
|
||||
break;
|
||||
case DEVICE_REBOOT:
|
||||
RebootUtils.rebootDevice(mContext, "push");
|
||||
break;
|
||||
case DEVICE_SHUTDOEN:
|
||||
RebootUtils.shutdownDevice(mContext);
|
||||
case DEVICE_LOCATE:
|
||||
MapManager.getInstance().startLocation();
|
||||
break;
|
||||
case DEVICE_RESTORE:
|
||||
|
||||
break;
|
||||
case DEVICE_DEVELOPER:
|
||||
|
||||
break;
|
||||
default:
|
||||
Logger.e(TAG, "setPushMessage: default");
|
||||
}
|
||||
}
|
||||
|
||||
private void getDeviceSettings() {
|
||||
OkHttpManager.getInstance().getDeveloperOptionsObservable()
|
||||
.subscribe(new Observer<BaseResponse<DeveloperOptions>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Logger.e("getDeveloperOptions", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<DeveloperOptions> baseResponse) {
|
||||
Logger.e("getDeveloperOptions", "onNext: " + baseResponse);
|
||||
if (baseResponse.isSuccess()) {
|
||||
DeveloperOptions developerOptions = baseResponse.getData();
|
||||
if (!BuildConfig.DEBUG) {
|
||||
DeviceManagerService.getInstance().setDevelopmentOption(developerOptions.getDeveloperOptions() == 1);
|
||||
DeviceManagerService.getInstance().setUsbDebugMode(developerOptions.getDeveloperOptions() == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Logger.e("getDeveloperOptions", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Logger.e("getDeveloperOptions", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void uploadDeviceInfo() {
|
||||
Observable
|
||||
.fromCallable(new Callable<List<ApkInstalledInfo>>() {
|
||||
@Override
|
||||
public List<ApkInstalledInfo> call() throws Exception {
|
||||
long time = System.currentTimeMillis();
|
||||
List<ApkInstalledInfo> apkInstalledInfos = AppManager.getInstance().getApkInstallInfos();
|
||||
Log.e(TAG, "call: get apkInstalledInfos time: " + (System.currentTimeMillis() - time) + "ms");
|
||||
//io 14738ms
|
||||
//newThread 14351ms
|
||||
|
||||
return apkInstalledInfos;
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.flatMap(new Function<List<ApkInstalledInfo>, Observable<BaseResponse>>() {
|
||||
@Override
|
||||
public Observable<BaseResponse> apply(List<ApkInstalledInfo> apkInstalledInfos) throws Throwable {
|
||||
RequestBody body = OkHttpManager.convertToRequestBodyjson(apkInstalledInfos);
|
||||
return OkHttpManager.getInstance().getUploadInstallApksObservable(body);
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<BaseResponse>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Logger.e("uploadDeviceInfo", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse baseResponse) {
|
||||
Logger.e("uploadDeviceInfo", "onNext: " + baseResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Logger.e("uploadDeviceInfo", "onError: ", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Logger.e("uploadDeviceInfo", "onComplete: ");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
SnHardwareInfoReq snHardwareInfoReq = new SnHardwareInfoReq();
|
||||
snHardwareInfoReq.setImei(DeviceManagerService.getInstance().getImei());
|
||||
snHardwareInfoReq.setImsi(DeviceManagerService.getInstance().getImsi());
|
||||
snHardwareInfoReq.setWlanMac(DeviceManagerService.getInstance().getWlanMac());
|
||||
snHardwareInfoReq.setDeviceMac(DeviceManagerService.getInstance().getDeviceMac());
|
||||
snHardwareInfoReq.setBluetoothMac(DeviceManagerService.getInstance().getBluetoothMac());
|
||||
|
||||
snHardwareInfoReq.setModel(Build.MODEL);
|
||||
snHardwareInfoReq.setBrand(Build.BRAND);
|
||||
snHardwareInfoReq.setBoard(Build.BOARD);
|
||||
snHardwareInfoReq.setAndroidApi(Build.VERSION.SDK_INT);
|
||||
snHardwareInfoReq.setAndroidVersion(Build.VERSION.RELEASE);
|
||||
snHardwareInfoReq.setBuildId(Build.ID);
|
||||
snHardwareInfoReq.setBuildDisplayId(Build.DISPLAY);
|
||||
|
||||
OkHttpManager.getInstance().getUpdateHardwareInfoObservable(snHardwareInfoReq)
|
||||
.subscribe(new Observer<BaseResponse<Void>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("updateHardwareInfo", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<Void> voidBaseResponse) {
|
||||
Log.e("updateHardwareInfo", "onNext: " + voidBaseResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("updateHardwareInfo", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("updateHardwareInfo", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void screenSnapshot() {
|
||||
String filepath = mContext.getExternalCacheDir().getAbsolutePath() + File.separator + "screenshot_" + System.currentTimeMillis() + ".png";
|
||||
|
||||
Observable.fromCallable(new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
CmdUtil.Result result = CmdUtil.execute("screencap -p " + filepath);
|
||||
Logger.e(TAG, "call: " + result.error);
|
||||
return result.code;
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io()) // 在子线程执行截图
|
||||
.flatMap(new Function<Integer, Observable<BaseResponse>>() {
|
||||
@Override
|
||||
public Observable<BaseResponse> apply(Integer integer) throws Throwable {
|
||||
if (integer == 0) {
|
||||
// 截图成功,上传文件
|
||||
MultipartBody.Part part = OkHttpManager.getBodyPart("image/png", "file", new File(filepath));
|
||||
return OkHttpManager.getInstance().getUploadScreenshotObservable(part)
|
||||
.subscribeOn(Schedulers.io()); // 确保上传操作在子线程执行
|
||||
} else {
|
||||
// 截图失败,返回一个空的Observable
|
||||
return Observable.empty();
|
||||
}
|
||||
}
|
||||
})
|
||||
.observeOn(AndroidSchedulers.mainThread()) // 在主线程回调
|
||||
.subscribe(new BaseObserver<BaseResponse>() { // 注意这里应该是BaseResponse,不是Observable<BaseResponse>
|
||||
@Override
|
||||
public void onSuccess(BaseResponse baseResponse) {
|
||||
Log.e("screenSnapshot", "onSuccess: " + baseResponse.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Log.e("onFailure", "onFailure: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,30 +1,95 @@
|
||||
package com.ttstd.dialer.push.jpush;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.ttstd.dialer.bean.PushMessage;
|
||||
import com.ttstd.dialer.push.PushExecutor;
|
||||
import com.ttstd.dialer.service.main.MainService;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import cn.jpush.android.api.CustomMessage;
|
||||
import cn.jpush.android.api.JPushInterface;
|
||||
import cn.jpush.android.api.JPushMessage;
|
||||
import cn.jpush.android.api.NotificationMessage;
|
||||
import cn.jpush.android.service.JPushMessageReceiver;
|
||||
|
||||
public class PushMessageService extends JPushMessageReceiver {
|
||||
private static final String TAG = "PushMessageService";
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(Context context, CustomMessage customMessage) {
|
||||
super.onMessage(context, customMessage);
|
||||
Logger.e(TAG, "onMessage: " + customMessage);
|
||||
|
||||
PushMessage pushMessage = new PushMessage(customMessage);
|
||||
PushExecutor.getInstance().setPushMessage(pushMessage);
|
||||
|
||||
Intent intent = new Intent(context, MainService.class);
|
||||
intent.putExtra("PushMessage", pushMessage);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
String contentType = customMessage.contentType;
|
||||
String title = customMessage.title;
|
||||
String content = customMessage.message;
|
||||
String extra = customMessage.extra;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAliasOperatorResult(Context context, JPushMessage jPushMessage) {
|
||||
super.onAliasOperatorResult(context, jPushMessage);
|
||||
int errorCode = jPushMessage.getErrorCode();
|
||||
Log.e(TAG, "onAliasOperatorResult: " + errorCode);
|
||||
Logger.e(TAG, "onAliasOperatorResult: errorCode = " + errorCode);
|
||||
if (errorCode == 0) {
|
||||
Log.e(TAG, "onAliasOperatorResult: " + jPushMessage.getAlias());
|
||||
|
||||
Logger.e(TAG, "onAliasOperatorResult: " + jPushMessage.getAlias());
|
||||
Logger.e(TAG, "onAliasOperatorResult: registrationId = " + JPushInterface.getRegistrationID(context));
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTagOperatorResult(Context context, JPushMessage jPushMessage) {
|
||||
super.onTagOperatorResult(context, jPushMessage);
|
||||
Logger.e(TAG, "onTagOperatorResult: ");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onNotifyMessageOpened(Context context, NotificationMessage notificationMessage) {
|
||||
super.onNotifyMessageOpened(context, notificationMessage);
|
||||
Logger.e(TAG, "onNotifyMessageOpened: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyMessageArrived(Context context, NotificationMessage notificationMessage) {
|
||||
super.onNotifyMessageArrived(context, notificationMessage);
|
||||
Logger.e(TAG, "onNotifyMessageArrived: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyMessageUnShow(Context context, NotificationMessage notificationMessage) {
|
||||
super.onNotifyMessageUnShow(context, notificationMessage);
|
||||
Logger.e(TAG, "onNotifyMessageUnShow: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotifyMessageDismiss(Context context, NotificationMessage notificationMessage) {
|
||||
super.onNotifyMessageDismiss(context, notificationMessage);
|
||||
Logger.e(TAG, "onNotifyMessageDismiss: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInAppMessageShow(Context context, NotificationMessage notificationMessage) {
|
||||
super.onInAppMessageShow(context, notificationMessage);
|
||||
Logger.e(TAG, "onInAppMessageShow: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInAppMessageClick(Context context, NotificationMessage notificationMessage) {
|
||||
super.onInAppMessageClick(context, notificationMessage);
|
||||
Logger.e(TAG, "onInAppMessageClick: ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ 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.utils.Logger;
|
||||
|
||||
public class AppChangedReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "ApkInstallReceiver";
|
||||
@@ -17,20 +17,20 @@ public class AppChangedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
Log.e(TAG, "onReceive: " + "action = " + action);
|
||||
Logger.e(TAG, "onReceive: " + "action = " + action);
|
||||
if (TextUtils.isEmpty(action)) {
|
||||
return;
|
||||
}
|
||||
String packageName = intent.getDataString().replace("package:", "");
|
||||
switch (action) {
|
||||
case Intent.ACTION_PACKAGE_ADDED:
|
||||
Log.e(TAG, "onReceive: added " + packageName);
|
||||
Logger.e(TAG, "onReceive: added " + packageName);
|
||||
break;
|
||||
case Intent.ACTION_PACKAGE_REPLACED:
|
||||
Log.e(TAG, "onReceive: replaced " + packageName);
|
||||
Logger.e(TAG, "onReceive: replaced " + packageName);
|
||||
break;
|
||||
case Intent.ACTION_PACKAGE_REMOVED:
|
||||
Log.e(TAG, "onReceive: removed " + packageName);
|
||||
Logger.e(TAG, "onReceive: removed " + packageName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
package com.ttstd.dialer.service.main;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
@@ -15,22 +14,27 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus;
|
||||
import com.shehuan.niv.NiceImageView;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.activity.main.MainActivity;
|
||||
import com.ttstd.dialer.base.BaseService;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class MainService extends Service {
|
||||
public class MainService extends BaseService {
|
||||
private static final String TAG = "MainService";
|
||||
|
||||
public static final String SHOW_FLOAT_WINDOW_ACTION = "show_float_window";
|
||||
@@ -38,6 +42,8 @@ public class MainService extends Service {
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
private MainServiceModel mViewModel;
|
||||
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
private View floatingView;
|
||||
@@ -53,12 +59,25 @@ public class MainService extends Service {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
Logger.e(TAG, "onCreate: ");
|
||||
// 初始化ViewModel(绑定上下文+View回调)
|
||||
mViewModel = new MainServiceModel();
|
||||
// 绑定Service生命周期(关键:让请求跟随生命周期)
|
||||
mViewModel.setLifecycleSubject(getLifecycleSubject());
|
||||
|
||||
LiveEventBus.get(CommonConfig.LOCATION_CHANGED_KEY, SnLocationReq.class)
|
||||
.observeSticky(this, new Observer<SnLocationReq>() {
|
||||
@Override
|
||||
public void onChanged(SnLocationReq snLocationReq) {
|
||||
Log.e(TAG, "onChanged: receive snLocationReq");
|
||||
mViewModel.uploadLocation(snLocationReq);
|
||||
}
|
||||
});
|
||||
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
|
||||
boolean enableFloatWindow = mMMKV.decodeInt(CommonConfig.FLOAT_WINDOW_ENABLE, 0) == 1;
|
||||
Log.e(TAG, "onCreate: enableFloatWindow = " + enableFloatWindow);
|
||||
Logger.e(TAG, "onCreate: enableFloatWindow = " + enableFloatWindow);
|
||||
if (enableFloatWindow) {
|
||||
showFloatWindow();
|
||||
}
|
||||
@@ -67,9 +86,10 @@ public class MainService extends Service {
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
if (intent != null) {
|
||||
String action = intent.getAction();
|
||||
Log.e(TAG, "onStartCommand: action = " + action);
|
||||
Logger.e(TAG, "onStartCommand: action = " + action);
|
||||
if (!TextUtils.isEmpty(action)) {
|
||||
switch (action) {
|
||||
case SHOW_FLOAT_WINDOW_ACTION:
|
||||
@@ -85,7 +105,7 @@ public class MainService extends Service {
|
||||
}
|
||||
|
||||
private void showFloatWindow() {
|
||||
Log.e(TAG, "showFloatWindow: ");
|
||||
Logger.e(TAG, "showFloatWindow: ");
|
||||
initFloatingView();
|
||||
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
@@ -94,14 +114,14 @@ public class MainService extends Service {
|
||||
mWindowManager.addView(floatingView, mLayoutParams);
|
||||
mFloatWindowShowing = true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: addView = " + e.getMessage());
|
||||
Logger.e(TAG, "onCreate: addView = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initFloatingView() {
|
||||
Log.e(TAG, "initFloatingView: ");
|
||||
Logger.e(TAG, "initFloatingView: ");
|
||||
floatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_window, null);
|
||||
|
||||
int layoutType;
|
||||
@@ -130,7 +150,7 @@ public class MainService extends Service {
|
||||
nvBack.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.e(TAG, "onClick: ");
|
||||
Logger.e(TAG, "onClick: ");
|
||||
// stopSelf(); // 停止服务,触发 onDestroy 移除悬浮窗
|
||||
// mWindowManager.removeView(floatingView);
|
||||
// floatingView = null;
|
||||
@@ -159,7 +179,7 @@ public class MainService extends Service {
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
Log.e(TAG, "onTouch: ");
|
||||
Logger.e(TAG, "onTouch: ");
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 记录初始位置
|
||||
@@ -187,7 +207,7 @@ public class MainService extends Service {
|
||||
}
|
||||
|
||||
private void hideFloatWindow() {
|
||||
Log.e(TAG, "hideFloatWindow: ");
|
||||
Logger.e(TAG, "hideFloatWindow: ");
|
||||
if (mFloatWindowShowing) {
|
||||
if (floatingView != null) {
|
||||
mWindowManager.removeView(floatingView);
|
||||
@@ -211,7 +231,7 @@ public class MainService extends Service {
|
||||
}};
|
||||
|
||||
private void killApp() {
|
||||
Log.e(TAG, "killApp: ");
|
||||
Logger.e(TAG, "killApp: ");
|
||||
List<String> runningPackages = SystemUtils.getRunningTaskPackages(MainService.this);
|
||||
int i = 0;
|
||||
for (String pkg : runningPackages) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.ttstd.dialer.service.main;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.trello.rxlifecycle4.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.network.BaseObserver;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
|
||||
public class MainServiceModel extends ViewModel {
|
||||
private static final String TAG = "MainServiceModel";
|
||||
|
||||
private BehaviorSubject<ActivityEvent> lifecycleSubject;
|
||||
|
||||
// 设置Service生命周期Subject
|
||||
public void setLifecycleSubject(BehaviorSubject<ActivityEvent> lifecycleSubject) {
|
||||
this.lifecycleSubject = lifecycleSubject;
|
||||
}
|
||||
|
||||
// 绑定请求到Service销毁事件(自动取消请求)
|
||||
private <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
if (lifecycleSubject == null) {
|
||||
throw new IllegalStateException("请先设置LifecycleSubject!");
|
||||
}
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, ActivityEvent.DESTROY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
// 清空生命周期引用,防止内存泄漏
|
||||
this.lifecycleSubject = null;
|
||||
}
|
||||
|
||||
|
||||
public void uploadLocation(SnLocationReq locationReq) {
|
||||
Log.e(TAG, "uploadLocation: ");
|
||||
OkHttpManager.getInstance().getUploadLocationObservable(locationReq)
|
||||
.compose(bindToLifecycle())
|
||||
.subscribe(new BaseObserver<BaseResponse>() {
|
||||
@Override
|
||||
public void onSuccess(BaseResponse baseResponse) {
|
||||
Log.e("uploadLocation", "onSuccess: " + baseResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Log.e("uploadLocation", "onFailure: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.ttstd.dialer.service.DialerAccessibilityService;
|
||||
@@ -35,9 +34,9 @@ public class AccessibilityServiceHelper {
|
||||
|
||||
// 构建你服务的完整组件标识符
|
||||
String expectedServiceId = new ComponentName(context, serviceClass).flattenToShortString();
|
||||
Log.e(TAG, "isAccessibilityServiceEnabled: expectedServiceId = " + expectedServiceId);
|
||||
Logger.e(TAG, "isAccessibilityServiceEnabled: expectedServiceId = " + expectedServiceId);
|
||||
for (AccessibilityServiceInfo serviceInfo : enabledServices) {
|
||||
Log.e(TAG, "isAccessibilityServiceEnabled: serviceInfo.getId() = " + serviceInfo.getId());
|
||||
Logger.e(TAG, "isAccessibilityServiceEnabled: serviceInfo.getId() = " + serviceInfo.getId());
|
||||
if (expectedServiceId.equals(serviceInfo.getId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.ttstd.dialer.R;
|
||||
@@ -40,7 +39,7 @@ public class ApkUtils {
|
||||
pkgContext.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
Log.e(TAG, "openPackage: can not open " + packageName);
|
||||
Logger.e(TAG, "openPackage: can not open " + packageName);
|
||||
Toaster.show("打开失败");
|
||||
return false;
|
||||
}
|
||||
@@ -96,7 +95,7 @@ public class ApkUtils {
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openApp: " + e.getMessage());
|
||||
Logger.e(TAG, "openApp: " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -166,7 +165,7 @@ public class ApkUtils {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pi = pm.getPackageInfo(pkgName, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e("isSystemApp: ", "NameNotFoundException:" + e.getMessage());
|
||||
Logger.e("isSystemApp: ", "NameNotFoundException:" + e.getMessage());
|
||||
}
|
||||
// 是系统中已安装的应用
|
||||
if (pi != null) {
|
||||
@@ -185,7 +184,7 @@ public class ApkUtils {
|
||||
try {
|
||||
context.startActivity(storeIntent);
|
||||
} catch (Exception e1) {
|
||||
Log.e(TAG, "openWeixin storeIntent: " + e1.getMessage());
|
||||
Logger.e(TAG, "openWeixin storeIntent: " + e1.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
567
app/src/main/java/com/ttstd/dialer/utils/CameraUtil.java
Normal file
567
app/src/main/java/com/ttstd/dialer/utils/CameraUtil.java
Normal file
@@ -0,0 +1,567 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Size;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @作者 Liushihua
|
||||
* @创建时间 2021-2-3 10:54
|
||||
* @描述
|
||||
*/
|
||||
public class CameraUtil {
|
||||
private static final String TAG = "CameraUtil";
|
||||
|
||||
private Context context;
|
||||
private CameraCallBack cameraCallBack;
|
||||
private CameraManager cameraManager;
|
||||
// 默认相机id是0 LENS_FACING_FRONT,LENS_FACING_BACK
|
||||
private int cameraId = CameraCharacteristics.LENS_FACING_FRONT;
|
||||
private CameraDevice mCameraDevice;
|
||||
private String savePath;
|
||||
|
||||
private CaptureRequest.Builder mPreviewRequestBuilder;
|
||||
private CameraCaptureSession mCameraCaptureSession;
|
||||
private CaptureRequest request;
|
||||
private ExecutorService service;
|
||||
private boolean isTakedPicture = false;//是否已经拍照
|
||||
|
||||
private int needSetOrientation = 0;// 设置默认的拍照方向
|
||||
private boolean isInitOk = false;// 是否初始化成功
|
||||
private boolean isSessionClosed = true;// captureSession是否被关闭
|
||||
private boolean isCameraDoing = false;// 是否正在使用相机
|
||||
private final long CAPTURE_DELAY_TIME_LONG = 1200;// 延时拍照——聚焦需要时间
|
||||
|
||||
private final int HANDLER_ERR = 3;// 拍照失败
|
||||
private final int HANDLER_TAKE_PHOTO_SUCCESS = 5;// 拍照成功
|
||||
private List<Integer> enableCameraList;//可用摄像头列表
|
||||
private boolean mFlashSupported = false;//是否支持闪光灯
|
||||
private List<Size> recordSizeList;// 录制尺寸
|
||||
private Size mPreviewOutputSize;// 预览尺寸
|
||||
private List<Size> imgOutputSizes;// 拍照尺寸
|
||||
|
||||
private final int PREVIEW_TYPE_NORMAL = 0;// 默认预览
|
||||
private final int PREVIEW_TYPE_RECORD = 1;// 录屏预览
|
||||
private final int PREVIEW_TYPE_TAKE_PHOTO = 2;// 拍照预览
|
||||
private int previewType = 0;//默认预览
|
||||
|
||||
private boolean mTakePictureFinish = false;
|
||||
|
||||
private long lastSaveFileTime = 0;
|
||||
|
||||
public interface CameraCallBack {
|
||||
void onErr(String msg);
|
||||
|
||||
void onTakePhotoOk(String path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理静态图片的输出
|
||||
*/
|
||||
private ImageReader imageReader;
|
||||
|
||||
private Handler handler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
switch (msg.what) {
|
||||
case HANDLER_ERR:
|
||||
cameraCallBack.onErr("" + msg.obj);
|
||||
isCameraDoing = false;
|
||||
break;
|
||||
case HANDLER_TAKE_PHOTO_SUCCESS:
|
||||
if (!mTakePictureFinish) {
|
||||
cameraCallBack.onTakePhotoOk(savePath);
|
||||
mTakePictureFinish = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 当相机设备的状态发生改变的时候,将会回调。
|
||||
*/
|
||||
protected final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
|
||||
/**
|
||||
* 当相机打开的时候,调用
|
||||
* @param cameraDevice
|
||||
*/
|
||||
@Override
|
||||
public void onOpened(@NonNull CameraDevice cameraDevice) {
|
||||
Logger.e(TAG, "onOpened");
|
||||
mCameraDevice = cameraDevice;
|
||||
startPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
|
||||
Logger.e(TAG, "onDisconnected");
|
||||
cameraDevice.close();
|
||||
mCameraDevice = null;
|
||||
Message message = new Message();
|
||||
message.what = HANDLER_ERR;
|
||||
message.obj = "后台相机断开连接";
|
||||
handler.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发生异常的时候调用
|
||||
*
|
||||
* 这里释放资源,然后关闭界面
|
||||
* @param cameraDevice
|
||||
* @param error
|
||||
*/
|
||||
@Override
|
||||
public void onError(@NonNull CameraDevice cameraDevice, int error) {
|
||||
Logger.e(TAG, "onError 相机设备异常,请重启!");
|
||||
cameraDevice.close();
|
||||
mCameraDevice = null;
|
||||
Message messagef = new Message();
|
||||
messagef.what = HANDLER_ERR;
|
||||
messagef.obj = "相机设备异常,请重启!";
|
||||
handler.sendMessage(messagef);
|
||||
}
|
||||
|
||||
/**
|
||||
*当相机被关闭的时候
|
||||
*/
|
||||
@Override
|
||||
public void onClosed(@NonNull CameraDevice camera) {
|
||||
super.onClosed(camera);
|
||||
Logger.e(TAG, "onClosed");
|
||||
mCameraDevice = null;
|
||||
isCameraDoing = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 相机状态回调
|
||||
*/
|
||||
private CameraManager.AvailabilityCallback callback = new CameraManager.AvailabilityCallback() {
|
||||
@Override
|
||||
public void onCameraAvailable(@NonNull String cameraId) {// 相机可用
|
||||
super.onCameraAvailable(cameraId);
|
||||
Logger.e(TAG, "相机可用");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraUnavailable(@NonNull String cameraId) {// 相机不可用
|
||||
super.onCameraUnavailable(cameraId);
|
||||
Logger.e(TAG, "相机不可用");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param activity
|
||||
* @param cameraCallBack 回调
|
||||
*/
|
||||
public CameraUtil(Context activity, @NonNull CameraCallBack cameraCallBack) {
|
||||
this.context = activity;
|
||||
this.cameraCallBack = cameraCallBack;
|
||||
cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
|
||||
// 对于静态图片,使用可用的最大值来拍摄。
|
||||
if (cameraManager != null) {
|
||||
isInitOk = true;
|
||||
cameraManager.registerAvailabilityCallback(callback, null);
|
||||
getCameraInfo();
|
||||
setupImageReader();
|
||||
service = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupImageReader() {
|
||||
// Size size = getOutputSize(imgOutputSizes);
|
||||
//前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据,本例的3代表ImageReader中最多可以获取2帧图像流
|
||||
imageReader = ImageReader.newInstance(1600, 1200, ImageFormat.JPEG, 1);
|
||||
//监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理
|
||||
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
|
||||
@Override
|
||||
public void onImageAvailable(ImageReader reader) {
|
||||
Image image = reader.acquireLatestImage();
|
||||
//我们可以将这帧数据转成字节数组,类似于Camera1的PreviewCallback回调的预览帧数据
|
||||
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
|
||||
byte[] data = new byte[buffer.remaining()];
|
||||
buffer.get(data);
|
||||
image.close();
|
||||
saveFile(data, savePath);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开相机
|
||||
*/
|
||||
private void openCamera() {
|
||||
cameraId = getFrontCameraId();
|
||||
Logger.e(TAG, "openCamera: getFrontCameraId = " + getFrontCameraId());
|
||||
Logger.e(TAG, "openCamera:" + cameraId);
|
||||
isCameraDoing = true;
|
||||
// 设置TextureView的缓冲区大小
|
||||
// 获取Surface显示预览数据
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
Message message = new Message();
|
||||
message.what = HANDLER_ERR;
|
||||
message.obj = "权限不足";
|
||||
handler.sendMessage(message);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
cameraManager.openCamera(Integer.toString(cameraId), stateCallback, null);
|
||||
Logger.e(TAG, "打开相机成功!");
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "打开相机失败-Exception:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
Message messagef = new Message();
|
||||
messagef.what = HANDLER_ERR;
|
||||
messagef.obj = "打开相机失败";
|
||||
handler.sendMessage(messagef);
|
||||
}
|
||||
}
|
||||
|
||||
private int getFrontCameraId() {
|
||||
int numberOfCameras = Camera.getNumberOfCameras();
|
||||
for (int i = 0; i < numberOfCameras; i++) {
|
||||
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(i, cameraInfo);
|
||||
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始视频录制预览
|
||||
*/
|
||||
private void startPreview() {
|
||||
Logger.e(TAG, "startPreview");
|
||||
// CaptureRequest添加imageReaderSurface,不加的话就会导致ImageReader的onImageAvailable()方法不会回调
|
||||
// 创建CaptureSession时加上imageReaderSurface,如下,这样预览数据就会同时输出到previewSurface和imageReaderSurface了
|
||||
try {
|
||||
// 创建CaptureRequestBuilder,TEMPLATE_PREVIEW比表示预览请求
|
||||
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||
mPreviewRequestBuilder.addTarget(imageReader.getSurface());// 设置Surface作为预览数据的显示界面
|
||||
// 创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行
|
||||
mCameraDevice.createCaptureSession(Arrays.asList(imageReader.getSurface()), captureSessionStateCallBack, null);
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
Message messagef = new Message();
|
||||
messagef.what = HANDLER_ERR;
|
||||
messagef.obj = "捕获帧失败";
|
||||
handler.sendMessage(messagef);
|
||||
Logger.e(TAG, "Camera获取成功,创建录制请求或捕获Session失败" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获图片数据
|
||||
*/
|
||||
private CameraCaptureSession.StateCallback captureSessionStateCallBack = new CameraCaptureSession.StateCallback() {
|
||||
@Override
|
||||
public void onConfigured(CameraCaptureSession session) {
|
||||
try {
|
||||
mCameraCaptureSession = session;
|
||||
isSessionClosed = false;
|
||||
request = mPreviewRequestBuilder.build();
|
||||
// 设置反复捕获数据的请求,这样预览界面就会一直有数据显示
|
||||
mCameraCaptureSession.setRepeatingRequest(request, null, null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Message messagef = new Message();
|
||||
messagef.what = HANDLER_ERR;
|
||||
messagef.obj = "开启图像预览失败";
|
||||
handler.sendMessage(messagef);
|
||||
}
|
||||
if (!isTakedPicture) {
|
||||
isTakedPicture = true;
|
||||
handler.postDelayed(() -> takePicture(), CAPTURE_DELAY_TIME_LONG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed(CameraCaptureSession session) {
|
||||
Logger.e(TAG, "onConfigureFailed");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void startTakePicture(String savePath) {
|
||||
Logger.e(TAG, "拍照:" + savePath);
|
||||
this.savePath = savePath;
|
||||
if (isCameraDoing) {
|
||||
Logger.e(TAG, "相机使用中...");
|
||||
} else {
|
||||
isTakedPicture = false;
|
||||
openCamera();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拍照
|
||||
*/
|
||||
private void takePicture() {
|
||||
Logger.e(TAG, "takePicture");
|
||||
try {
|
||||
if (mCameraDevice == null || mPreviewRequestBuilder == null) return;
|
||||
mPreviewRequestBuilder.addTarget(imageReader.getSurface());
|
||||
//设置拍照方向
|
||||
mPreviewRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, this.needSetOrientation);
|
||||
//这个回调接口用于拍照结束时重启预览,因为拍照会导致预览停止
|
||||
CameraCaptureSession.CaptureCallback mImageSavedCallback = new CameraCaptureSession.CaptureCallback() {
|
||||
@Override
|
||||
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
|
||||
Logger.e(TAG, "拍照完成");
|
||||
onStop();
|
||||
}
|
||||
};
|
||||
//开始拍照,然后回调上面的接口重启预览,因为mCaptureBuilder设置ImageReader作为target,所以会自动回调ImageReader的onImageAvailable()方法保存图片
|
||||
mCameraCaptureSession.capture(mPreviewRequestBuilder.build(), mImageSavedCallback, null);
|
||||
} catch (CameraAccessException e) {
|
||||
Logger.e(TAG, "takePhoto CameraAccessException:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
Message messagef = new Message();
|
||||
messagef.what = HANDLER_ERR;
|
||||
messagef.obj = "拍照失败";
|
||||
handler.sendMessage(messagef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止预览,释放资源
|
||||
*/
|
||||
public void stopRecord() {
|
||||
Logger.e(TAG, "停止预览,释放资源");
|
||||
try {
|
||||
if (mCameraCaptureSession != null && !isSessionClosed) {
|
||||
mCameraCaptureSession.stopRepeating();
|
||||
mCameraCaptureSession.abortCaptures();
|
||||
mCameraCaptureSession.close();
|
||||
isSessionClosed = true;
|
||||
imageReader.close();
|
||||
imageReader = null;
|
||||
}
|
||||
if (mCameraDevice != null)
|
||||
mCameraDevice.close();
|
||||
isCameraDoing = false;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Logger.e(TAG, "stopRecord-Exception:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置后,开始预览
|
||||
*/
|
||||
public void reset() {
|
||||
previewType = PREVIEW_TYPE_NORMAL;
|
||||
stopRecord();
|
||||
openCamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 activity,fragment的onStop中调用
|
||||
*/
|
||||
public void onStop() {
|
||||
stopRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销 回调
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.cameraCallBack = null;
|
||||
if (cameraManager != null)
|
||||
cameraManager.unregisterAvailabilityCallback(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得可用的摄像头
|
||||
*
|
||||
* @return SparseArray of available cameras ids。key为摄像头方位,见CameraCharacteristics#LENS_FACING,value为对应的摄像头id
|
||||
*/
|
||||
public void getCameras() {
|
||||
if (cameraManager == null) return;
|
||||
enableCameraList = new ArrayList<>();
|
||||
try {
|
||||
String[] camerasAvailable = cameraManager.getCameraIdList();
|
||||
CameraCharacteristics cam;
|
||||
Integer characteristic;
|
||||
Logger.e(TAG, "-------------------------------------");
|
||||
for (String id : camerasAvailable) {
|
||||
Logger.e(TAG, "getCameras:" + id);
|
||||
try {
|
||||
enableCameraList.add(Integer.parseInt(id));
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Logger.e(TAG, "-------------------------------------");
|
||||
} catch (CameraAccessException e) {
|
||||
Logger.e(TAG, "getCameras CameraAccessException:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出数据尺寸选择器,在selectCamera之前设置有效
|
||||
* (一般手机支持多种输出尺寸,请用户根据自身需要选择最合适的一种)
|
||||
* 举例:
|
||||
* SizeSelector maxPreview = SizeSelectors.and(SizeSelectors.maxWidth(720), SizeSelectors.maxHeight(480));
|
||||
* SizeSelector minPreview = SizeSelectors.and(SizeSelectors.minWidth(320), SizeSelectors.minHeight(240));
|
||||
* camera.setmOutputSizeSelector(SizeSelectors.or(
|
||||
* SizeSelectors.and(maxPreview, minPreview)//先在最大和最小中寻找
|
||||
* , SizeSelectors.and(maxPreview, SizeSelectors.biggest())//找不到则按不超过最大尺寸的那个选择
|
||||
* ));
|
||||
*/
|
||||
public void getOutputSizeSelector() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取摄像头信息
|
||||
*/
|
||||
public void getCameraInfo() {
|
||||
if (enableCameraList == null) {
|
||||
getCameras();
|
||||
}
|
||||
try {
|
||||
CameraCharacteristics mCameraCharacteristics = cameraManager.getCameraCharacteristics(String.valueOf(cameraId));
|
||||
// 设置是否支持闪光灯
|
||||
Boolean available = mCameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
|
||||
mFlashSupported = available == null ? false : available;
|
||||
StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
if (map == null) {
|
||||
Logger.e(TAG, "Could not get configuration map.");
|
||||
return;
|
||||
}
|
||||
int mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);//这个方法来获取CameraSensor的方向。
|
||||
Logger.e(TAG, "camera sensor orientation:" + mSensorOrientation + ",display rotation=" + context.getDisplay().getRotation());
|
||||
|
||||
int[] formats = map.getOutputFormats();//获得手机支持的输出格式,其中jpeg是一定会支持的,yuv_420_888是api21才开始支持的
|
||||
for (int format : formats) {
|
||||
Logger.e(TAG, "手机格式支持: " + format);
|
||||
}
|
||||
Size[] yuvOutputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);
|
||||
Size[] mediaOutputSizes = map.getOutputSizes(MediaRecorder.class);
|
||||
Size[] previewOutputSizes = map.getOutputSizes(SurfaceTexture.class);
|
||||
Size[] jpegOutputSizes = map.getOutputSizes(ImageFormat.JPEG);
|
||||
|
||||
recordSizeList = new ArrayList<>();
|
||||
imgOutputSizes = new ArrayList<>();
|
||||
|
||||
Logger.e(TAG, "---------------------------------------------------");
|
||||
for (Size size : mediaOutputSizes) {
|
||||
recordSizeList.add(new Size(size.getWidth(), size.getHeight()));
|
||||
Logger.e(TAG, "mediaOutputSizes: " + size.toString());
|
||||
}
|
||||
for (Size size : jpegOutputSizes) {
|
||||
imgOutputSizes.add(new Size(size.getWidth(), size.getHeight()));
|
||||
Logger.e(TAG, "jpegOutputSizes: " + size.toString());
|
||||
}
|
||||
for (Size size : previewOutputSizes) {
|
||||
Logger.e(TAG, "previewOutputSizes: " + size.toString());
|
||||
}
|
||||
for (Size size : yuvOutputSizes) {
|
||||
Logger.e(TAG, "yuvOutputSizes: " + size.toString());
|
||||
}
|
||||
Logger.e(TAG, "---------------------------------------------------");
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "selectCamera Exception:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Size getOutputSize(List<Size> sizes) {
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
int screenWidth = dm.widthPixels;
|
||||
int screenHeight = dm.heightPixels;
|
||||
Logger.e(TAG, "getOutputSize: screenWidth = " + screenWidth);
|
||||
Logger.e(TAG, "getOutputSize: screenHeight = " + screenHeight);
|
||||
List<Size> sorted = sizes.stream().sorted(new Comparator<Size>() {
|
||||
@Override
|
||||
public int compare(Size o1, Size o2) {
|
||||
return Long.compare(o1.getHeight() * o1.getWidth(), o2.getHeight() * o2.getWidth());
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
for (Size size : sorted) {
|
||||
if (size.getWidth() > screenHeight && size.getHeight() > screenWidth) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return new Size(1600, 1200);
|
||||
}
|
||||
|
||||
//覆盖性保存
|
||||
private void saveFile(final byte[] data, final String savePath) {
|
||||
if (data == null || data.length == 0) return;
|
||||
if (System.currentTimeMillis() - lastSaveFileTime > 1000)
|
||||
service.execute(() -> {
|
||||
File file = new File(savePath);
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null && !parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
try {
|
||||
file.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write(data);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
lastSaveFileTime = System.currentTimeMillis();
|
||||
Message message = new Message();
|
||||
message.what = HANDLER_TAKE_PHOTO_SUCCESS;
|
||||
message.obj = savePath;
|
||||
handler.sendMessage(message);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Logger.e(TAG, "图片保存-IOException:" + e.getMessage());
|
||||
Message messagef = new Message();
|
||||
messagef.what = HANDLER_ERR;
|
||||
messagef.obj = "图片保存失败";
|
||||
handler.sendMessage(messagef);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
144
app/src/main/java/com/ttstd/dialer/utils/CmdUtil.java
Normal file
144
app/src/main/java/com/ttstd/dialer/utils/CmdUtil.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class CmdUtil {
|
||||
private static final String TAG = "CmdUtil";
|
||||
|
||||
private static final String COMMAND_SH = "sh";
|
||||
private static final String COMMAND_EXIT = "exit\n";
|
||||
private static final String COMMAND_LINE_END = "\n";
|
||||
|
||||
|
||||
/**
|
||||
* 运行命令
|
||||
*
|
||||
* @param command 命令
|
||||
* @return 结果
|
||||
*/
|
||||
public static Result execute(String command) {
|
||||
Logger.i(TAG, "execute() command = " + command);
|
||||
Result result = new Result();
|
||||
|
||||
if (TextUtils.isEmpty(command)) {
|
||||
Logger.w(TAG, "WARNING: command should not be null or empty");
|
||||
return result;
|
||||
}
|
||||
|
||||
Process process = null;
|
||||
DataOutputStream dos = null;
|
||||
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(COMMAND_SH);
|
||||
dos = new DataOutputStream(process.getOutputStream());
|
||||
dos.write(command.trim().getBytes());
|
||||
dos.writeBytes(COMMAND_LINE_END);
|
||||
dos.flush();
|
||||
dos.writeBytes(COMMAND_EXIT);
|
||||
dos.flush();
|
||||
result.code = process.waitFor();
|
||||
result.success = readBuffer(new BufferedReader(new InputStreamReader(process.getInputStream())));
|
||||
result.error = readBuffer(new BufferedReader(new InputStreamReader(process.getErrorStream())));
|
||||
Logger.i(TAG, "result = " + result);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
Logger.e(TAG, ioe.getMessage());
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
Logger.e(TAG, ie.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (null != dos) {
|
||||
dos.close();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
Logger.e(TAG, ioe.getMessage());
|
||||
}
|
||||
if (null != process) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String readBuffer(BufferedReader bufferedReader) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String s;
|
||||
while ((s = bufferedReader.readLine()) != null) {
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Command执行结果
|
||||
*/
|
||||
public static final class Result {
|
||||
|
||||
public static final int SUCCESS = 0;
|
||||
public static final int ERROR = -1;
|
||||
|
||||
public int code = ERROR;
|
||||
public String error;
|
||||
public String success;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Result{" +
|
||||
"code=" + code +
|
||||
", error='" + error + '\'' +
|
||||
", success='" + success + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行 adb 命令
|
||||
*
|
||||
* @param cmd 命令
|
||||
* @return
|
||||
*/
|
||||
public static StringBuffer shellExec(String cmd) {
|
||||
Runtime mRuntime = Runtime.getRuntime(); //执行命令的方法
|
||||
try {
|
||||
//Process中封装了返回的结果和执行错误的结果
|
||||
Process mProcess = mRuntime.exec(cmd); //加入参数
|
||||
//使用BufferReader缓冲各个字符,实现高效读取
|
||||
//InputStreamReader将执行命令后得到的字节流数据转化为字符流
|
||||
//mProcess.getInputStream()获取命令执行后的的字节流结果
|
||||
BufferedReader mReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
|
||||
//实例化一个字符缓冲区
|
||||
StringBuffer mRespBuff = new StringBuffer();
|
||||
//实例化并初始化一个大小为1024的字符缓冲区,char类型
|
||||
char[] buff = new char[1024];
|
||||
int ch = 0;
|
||||
//read()方法读取内容到buff缓冲区中,大小为buff的大小,返回一个整型值,即内容的长度
|
||||
//如果长度不为null
|
||||
while ((ch = mReader.read(buff)) != -1) {
|
||||
//就将缓冲区buff的内容填进字符缓冲区
|
||||
mRespBuff.append(buff, 0, ch);
|
||||
}
|
||||
//结束缓冲
|
||||
mReader.close();
|
||||
Logger.i("shell", "shellExec: " + mRespBuff);
|
||||
//弹出结果
|
||||
// Logger.i("shell", "执行命令: " + cmd + "执行成功");
|
||||
return mRespBuff;
|
||||
|
||||
} catch (IOException e) {
|
||||
// 异常处理
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
24
app/src/main/java/com/ttstd/dialer/utils/FileUtils.java
Normal file
24
app/src/main/java/com/ttstd/dialer/utils/FileUtils.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
public class FileUtils {
|
||||
public static String getCacheDir(Context context) {
|
||||
String cachePath;
|
||||
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|
||||
|| !Environment.isExternalStorageRemovable()) {
|
||||
if (context.getExternalCacheDir() != null) {
|
||||
cachePath = context.getExternalCacheDir().getPath();
|
||||
} else if (context.getExternalFilesDir("cache") != null) {
|
||||
cachePath = context.getExternalFilesDir("cache").getPath();
|
||||
} else {
|
||||
cachePath = context.getCacheDir().getPath();
|
||||
}
|
||||
} else {
|
||||
cachePath = context.getCacheDir().getPath();
|
||||
}
|
||||
return cachePath;
|
||||
}
|
||||
|
||||
}
|
||||
145
app/src/main/java/com/ttstd/dialer/utils/HashUtils.java
Normal file
145
app/src/main/java/com/ttstd/dialer/utils/HashUtils.java
Normal file
@@ -0,0 +1,145 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
public class HashUtils {
|
||||
|
||||
public static String getFileMd5(File file) {
|
||||
if (file == null || !file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MessageDigest digest;
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
digest = MessageDigest.getInstance("MD5");
|
||||
inputStream = new FileInputStream(file);
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, len);
|
||||
}
|
||||
|
||||
return bytesToHex(digest.digest());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算字符串的 SHA-256 哈希值
|
||||
*
|
||||
* @param input 需要计算的字符串
|
||||
* @return 64位长度的十六进制哈希字符串
|
||||
*/
|
||||
public static String getSHA256String(String input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 获取 SHA-256 实例 (原生支持,无需外部依赖)
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
// 将字符串转换为字节数组并进行 Hash 计算
|
||||
byte[] hashBytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
// 将字节数组转换为十六进制字符串
|
||||
return bytesToHex(hashBytes);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算文件的 SHA-256 哈希值 (常用于文件完整性校验)
|
||||
*
|
||||
* @param file 需要计算的文件
|
||||
* @return 64位长度的十六进制哈希字符串
|
||||
*/
|
||||
public static String getFileSHA256(File file) {
|
||||
if (file == null || !file.exists()) {
|
||||
return null;
|
||||
}
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
fis = new FileInputStream(file);
|
||||
byte[] buffer = new byte[8192]; // 8KB 缓冲区
|
||||
int length;
|
||||
// 分块读取文件并更新 Hash
|
||||
while ((length = fis.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, length);
|
||||
}
|
||||
return bytesToHex(digest.digest());
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换为小写的十六进制字符串
|
||||
*/
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder hexString = new StringBuilder(2 * bytes.length);
|
||||
for (byte b : bytes) {
|
||||
// 将 byte 转换为无符号整数并格式化为两位的十六进制数
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ⚠️ 特别提示:如果是为了“加密密码”
|
||||
* 如果你的目的是在本地或传输前对用户密码进行 Hash,单纯的 SHA-256 是不够安全的(容易被彩虹表破解)。
|
||||
* 对于密码存储,建议使用加盐的迭代算法。
|
||||
* 在低版本 Android 上兼容性最好的原生方案是 PBKDF2WithHmacSHA1:
|
||||
*
|
||||
* @param password
|
||||
* @param salt
|
||||
* @return
|
||||
*/
|
||||
public static String hashPasswordPBKDF2(String password, byte[] salt) {
|
||||
try {
|
||||
// 迭代 10000 次,生成 256 位的密钥。PBKDF2WithHmacSHA1 兼容 API 10+
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||
byte[] hash = skf.generateSecret(spec).getEncoded();
|
||||
return bytesToHex(hash);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import com.ttstd.dialer.BuildConfig;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
@@ -16,113 +18,228 @@ import java.util.Locale;
|
||||
public class Logger {
|
||||
// 日志级别枚举
|
||||
public enum LogLevel {
|
||||
DEBUG, INFO, WARN, ERROR
|
||||
VERBOSE, DEBUG, INFO, WARN, ERROR
|
||||
}
|
||||
|
||||
private static final String TAG = "AppLogger";
|
||||
private static final String LOG_FILE_NAME = "app_log.txt";
|
||||
private static File logFile;
|
||||
private static LogLevel logLevel = LogLevel.DEBUG; // 默认日志级别
|
||||
private static boolean isDebugMode = BuildConfig.DEBUG; // 默认关闭Debug输出
|
||||
private static boolean isDebugMode = BuildConfig.DEBUG; // Debug模式开关
|
||||
|
||||
// 初始化日志系统(需在应用启动时调用)
|
||||
public static void initialize(Context context, boolean debugMode) {
|
||||
if (context == null) {
|
||||
Log.e(TAG, "Logger initialize failed: context is null");
|
||||
return;
|
||||
}
|
||||
isDebugMode = debugMode;
|
||||
try {
|
||||
File storageDir = context.getExternalFilesDir("log");
|
||||
logFile = new File(storageDir, LOG_FILE_NAME);
|
||||
if (storageDir != null) {
|
||||
// 确保日志目录存在
|
||||
if (!storageDir.exists()) {
|
||||
boolean mkdirSuccess = storageDir.mkdirs();
|
||||
if (!mkdirSuccess) {
|
||||
Log.e(TAG, "Create log directory failed");
|
||||
}
|
||||
}
|
||||
logFile = new File(storageDir, LOG_FILE_NAME);
|
||||
} else {
|
||||
Log.e(TAG, "Get external files dir failed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Log file init failed: " + e.getMessage());
|
||||
Log.e(TAG, "Log file init failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置全局日志级别
|
||||
public static void setLogLevel(LogLevel level) {
|
||||
logLevel = level;
|
||||
if (level != null) {
|
||||
logLevel = level;
|
||||
}
|
||||
}
|
||||
|
||||
// 核心日志方法
|
||||
private static void log(LogLevel level, Object context, String name, String message) {
|
||||
if (level.ordinal() < logLevel.ordinal()) return; // 低于设定级别不记录
|
||||
String tag;
|
||||
if (TextUtils.isEmpty(context.getClass().getSimpleName())) {
|
||||
tag = context.toString();
|
||||
} else {
|
||||
tag = context.getClass().getSimpleName();
|
||||
// 核心日志方法(支持异常)
|
||||
private static void log(LogLevel level, Object contextObj, String name, String message, Throwable throwable) {
|
||||
// 级别过滤
|
||||
if (level == null || level.ordinal() < logLevel.ordinal()) {
|
||||
return;
|
||||
}
|
||||
String logMsg = formatLog(level, tag, name, message);
|
||||
writeToFile(logMsg); // 始终写入文件
|
||||
|
||||
// 处理Context TAG
|
||||
String tag = getTagFromContext(contextObj);
|
||||
// 格式化日志内容
|
||||
String logMsg = formatLog(level, tag, name, message, throwable);
|
||||
|
||||
// 写入文件
|
||||
writeToFile(logMsg);
|
||||
|
||||
// Debug模式输出到控制台
|
||||
if (isDebugMode) {
|
||||
outputToConsole(level, tag, name, message); // 仅Debug模式输出到控制台
|
||||
outputToConsole(level, tag, name, message, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日志(含时间戳、线程信息)
|
||||
private static String formatLog(LogLevel level, String tag, String name, String message) {
|
||||
// 从Context对象获取TAG
|
||||
private static String getTagFromContext(Object contextObj) {
|
||||
if (contextObj == null) {
|
||||
return TAG;
|
||||
}
|
||||
if (contextObj instanceof Class<?>) {
|
||||
return ((Class<?>) contextObj).getSimpleName();
|
||||
} else if (contextObj instanceof String) {
|
||||
return (String) contextObj;
|
||||
} else {
|
||||
String simpleName = contextObj.getClass().getSimpleName();
|
||||
return TextUtils.isEmpty(simpleName) ? contextObj.toString() : simpleName;
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日志(含时间戳、线程信息、异常堆栈)
|
||||
private static String formatLog(LogLevel level, String tag, String name, String message, Throwable throwable) {
|
||||
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(new Date());
|
||||
String threadName = Thread.currentThread().getName();
|
||||
return String.format("%s [%s] %s/%s: %s %s\n", time, threadName, level.name(), tag, name, message);
|
||||
|
||||
// 基础日志内容
|
||||
StringBuilder logBuilder = new StringBuilder();
|
||||
logBuilder.append(String.format("%s [%s] %s/%s: %s %s",
|
||||
time, threadName, level.name(), tag,
|
||||
TextUtils.isEmpty(name) ? "" : name,
|
||||
TextUtils.isEmpty(message) ? "" : message));
|
||||
|
||||
// 追加异常堆栈
|
||||
if (throwable != null) {
|
||||
logBuilder.append("\n").append(getStackTraceString(throwable));
|
||||
}
|
||||
|
||||
logBuilder.append("\n");
|
||||
return logBuilder.toString();
|
||||
}
|
||||
|
||||
// 将异常堆栈转为字符串
|
||||
private static String getStackTraceString(Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
return "";
|
||||
}
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
throwable.printStackTrace(pw);
|
||||
pw.flush();
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
// 写入日志文件
|
||||
private static void writeToFile(String logMsg) {
|
||||
if (logFile == null) return;
|
||||
if (logFile == null || TextUtils.isEmpty(logMsg)) {
|
||||
return;
|
||||
}
|
||||
try (FileWriter writer = new FileWriter(logFile, true)) {
|
||||
writer.append(logMsg);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "File write failed: " + e.getMessage());
|
||||
Log.e(TAG, "File write failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 输出到Android控制台(Logcat)
|
||||
private static void outputToConsole(LogLevel level, String tag, String name, String message) {
|
||||
private static void outputToConsole(LogLevel level, String tag, String name, String message, Throwable throwable) {
|
||||
String logContent = TextUtils.isEmpty(name) ? message : (name + " : " + (message == null ? "" : message));
|
||||
|
||||
switch (level) {
|
||||
case VERBOSE:
|
||||
Log.v(tag, logContent, throwable);
|
||||
break;
|
||||
case DEBUG:
|
||||
Log.d(tag, name + " : " + message);
|
||||
Log.d(tag, logContent, throwable);
|
||||
break;
|
||||
case INFO:
|
||||
Log.i(tag, name + " : " + message);
|
||||
Log.i(tag, logContent, throwable);
|
||||
break;
|
||||
case WARN:
|
||||
Log.w(tag, name + " : " + message);
|
||||
Log.w(tag, logContent, throwable);
|
||||
break;
|
||||
case ERROR:
|
||||
Log.e(tag, name + " : " + message);
|
||||
Log.e(tag, logContent, throwable);
|
||||
break;
|
||||
default:
|
||||
Log.d(tag, logContent, throwable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 快捷调用方法(自动使用类名作为TAG)
|
||||
// ===================== 基础快捷方法 =====================
|
||||
public static void v(Object context, String name, String message) {
|
||||
log(LogLevel.VERBOSE, context, name, message, null);
|
||||
}
|
||||
|
||||
public static void d(Object context, String name, String message) {
|
||||
log(LogLevel.DEBUG, context.getClass().getSimpleName(), name, message);
|
||||
log(LogLevel.DEBUG, context, name, message, null);
|
||||
}
|
||||
|
||||
public static void i(Object context, String name, String message) {
|
||||
log(LogLevel.INFO, context.getClass().getSimpleName(), name, message);
|
||||
log(LogLevel.INFO, context, name, message, null);
|
||||
}
|
||||
|
||||
public static void w(Object context, String name, String message) {
|
||||
log(LogLevel.WARN, context.getClass().getSimpleName(), name, message);
|
||||
log(LogLevel.WARN, context, name, message, null);
|
||||
}
|
||||
|
||||
public static void e(Object context, String name, String message) {
|
||||
log(LogLevel.ERROR, context, name, message);
|
||||
log(LogLevel.ERROR, context, name, message, null);
|
||||
}
|
||||
|
||||
// ===================== 支持异常的快捷方法 =====================
|
||||
public static void e(Object context, String name, String message, Throwable throwable) {
|
||||
log(LogLevel.ERROR, context, name, message, throwable);
|
||||
}
|
||||
|
||||
public static void w(Object context, String name, String message, Throwable throwable) {
|
||||
log(LogLevel.WARN, context, name, message, throwable);
|
||||
}
|
||||
|
||||
// ===================== 简化参数的快捷方法 =====================
|
||||
public static void v(Object context, String name) {
|
||||
log(LogLevel.VERBOSE, context, name, "", null);
|
||||
}
|
||||
|
||||
public static void d(Object context, String name) {
|
||||
log(LogLevel.DEBUG, context.getClass().getSimpleName(), name, "");
|
||||
log(LogLevel.DEBUG, context, name, "", null);
|
||||
}
|
||||
|
||||
public static void i(Object context, String name) {
|
||||
log(LogLevel.INFO, context.getClass().getSimpleName(), name, "");
|
||||
log(LogLevel.INFO, context, name, "", null);
|
||||
}
|
||||
|
||||
public static void w(Object context, String name) {
|
||||
log(LogLevel.WARN, context.getClass().getSimpleName(), name, "");
|
||||
log(LogLevel.WARN, context, name, "", null);
|
||||
}
|
||||
|
||||
public static void e(Object context, String name) {
|
||||
log(LogLevel.ERROR, context.getClass().getSimpleName(), name, "");
|
||||
log(LogLevel.ERROR, context, name, "", null);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 兼容原生Log调用的静态方法 =====================
|
||||
public static void v(String tag, String msg) {
|
||||
if (isDebugMode) Log.v(tag, msg);
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg) {
|
||||
if (isDebugMode) Log.d(tag, msg);
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg) {
|
||||
if (isDebugMode) Log.i(tag, msg);
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
if (isDebugMode) Log.w(tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
if (isDebugMode) Log.e(tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg, Throwable tr) {
|
||||
if (isDebugMode) Log.e(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public class NativeUtils {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static native String getKey();
|
||||
public static native String getHashKey();
|
||||
|
||||
/**
|
||||
* 获取 获取绑定状态的code
|
||||
@@ -20,6 +20,21 @@ public class NativeUtils {
|
||||
*/
|
||||
public static native String getBindStatuSigCode();
|
||||
|
||||
|
||||
/**
|
||||
* 获取app接口请求安全密钥
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static native String getAppSecureSecretKey();
|
||||
|
||||
/**
|
||||
* 获取随机字符串
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static native String getNonce();
|
||||
|
||||
/**
|
||||
* 和风天气API Host
|
||||
*
|
||||
|
||||
165
app/src/main/java/com/ttstd/dialer/utils/RebootUtils.java
Normal file
165
app/src/main/java/com/ttstd/dialer/utils/RebootUtils.java
Normal file
@@ -0,0 +1,165 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class RebootUtils {
|
||||
|
||||
/**
|
||||
* 重启设备(兼容高低版本)
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param reason 重启原因(可为空)
|
||||
*/
|
||||
public static void rebootDevice(Context context, String reason) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// Android 9.0+ 使用PowerManager.reboot()
|
||||
rebootWithPowerManager(context, reason);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Android 6.0+ 尝试多种方式
|
||||
rebootWithMultipleMethods(context);
|
||||
} else {
|
||||
// 低版本使用传统方式
|
||||
rebootLegacy(context);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("RebootUtils", "重启失败: " + e.getMessage());
|
||||
// 尝试最终的回退方案
|
||||
rebootFallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 9.0+ 推荐方式
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.P)
|
||||
private static void rebootWithPowerManager(Context context, String reason) {
|
||||
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (powerManager != null) {
|
||||
powerManager.reboot(reason);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 6.0+ 多方法尝试
|
||||
*/
|
||||
private static void rebootWithMultipleMethods(Context context) {
|
||||
// 方法1: 使用Intent.ACTION_REBOOT
|
||||
try {
|
||||
Intent rebootIntent = new Intent(Intent.ACTION_REBOOT);
|
||||
rebootIntent.putExtra("nowait", 1);
|
||||
rebootIntent.putExtra("interval", 1);
|
||||
rebootIntent.putExtra("window", 0);
|
||||
context.sendBroadcast(rebootIntent);
|
||||
} catch (SecurityException e) {
|
||||
// 方法2: 使用su命令(需要root权限)
|
||||
rebootWithSuCommand();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 低版本传统方式
|
||||
*/
|
||||
private static void rebootLegacy(Context context) {
|
||||
try {
|
||||
// 尝试发送重启广播
|
||||
Intent intent = new Intent("android.intent.action.REBOOT");
|
||||
intent.putExtra("nowait", 1);
|
||||
context.sendBroadcast(intent);
|
||||
} catch (Exception e) {
|
||||
rebootWithSuCommand();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用su命令重启(需要root权限)
|
||||
*/
|
||||
private static void rebootWithSuCommand() {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "reboot"});
|
||||
process.waitFor();
|
||||
} catch (Exception e) {
|
||||
Log.e("RebootUtils", "su命令执行失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 最终回退方案
|
||||
*/
|
||||
private static void rebootFallback() {
|
||||
try {
|
||||
// 尝试直接执行reboot命令
|
||||
Runtime.getRuntime().exec("reboot");
|
||||
} catch (Exception e) {
|
||||
Log.e("RebootUtils", "所有重启方法都失败了");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void shutdownDevice(Context context) {
|
||||
try {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null) {
|
||||
Method method = pm.getClass().getMethod("shutdown",
|
||||
boolean.class, String.class, boolean.class);
|
||||
method.invoke(pm, false, null, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 备用方案
|
||||
fallbackShutdown(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fallbackShutdown(Context context) {
|
||||
try {
|
||||
String action;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Android 8.1+ 使用新的action
|
||||
action = "com.android.internal.intent.action.REQUEST_SHUTDOWN";
|
||||
} else {
|
||||
// Android 8.1以下使用旧的action
|
||||
action = "android.intent.action.ACTION_REQUEST_SHUTDOWN";
|
||||
}
|
||||
|
||||
Intent shutdown = new Intent(action);
|
||||
shutdown.putExtra("android.intent.extra.KEY_CONFIRM", false);
|
||||
shutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(shutdown);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shutdownWithRoot() {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec("su");
|
||||
DataOutputStream os = new DataOutputStream(process.getOutputStream());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Android 5.0+
|
||||
os.writeBytes("svc power shutdown\n");
|
||||
} else {
|
||||
// 旧版本
|
||||
os.writeBytes("reboot -p\n");
|
||||
}
|
||||
|
||||
os.writeBytes("exit\n");
|
||||
os.flush();
|
||||
int exitCode = process.waitFor();
|
||||
return exitCode == 0;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package com.ttstd.dialer.utils;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@@ -47,19 +46,19 @@ public class ScreenUtils {
|
||||
Method getMethod = systemPropertiesClass.getDeclaredMethod("get", String.class);
|
||||
// 3. 调用方法获取属性值
|
||||
String characteristics = (String) getMethod.invoke(null, "ro.build.characteristics");
|
||||
Log.e(TAG, "isTablet: " + characteristics);
|
||||
Logger.e(TAG, "isTablet: " + characteristics);
|
||||
// 4. 判断是否包含 "tablet" 标识
|
||||
return characteristics != null && characteristics.contains("tablet");
|
||||
} catch (Exception e) {
|
||||
// 反射失败时的处理(如属性不存在或权限问题)
|
||||
Log.e(TAG, "Reflection failed: " + e.getMessage());
|
||||
Logger.e(TAG, "Reflection failed: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTablet(Context context) {
|
||||
boolean isTablet = (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
Log.e(TAG, "isTablet: " + isTablet);
|
||||
Logger.e(TAG, "isTablet: " + isTablet);
|
||||
return isTablet;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.role.RoleManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
@@ -14,61 +16,55 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.os.Process;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
|
||||
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
|
||||
|
||||
public class SystemUtils {
|
||||
private static final String TAG = "SystemUtils";
|
||||
|
||||
/**
|
||||
* 获取设备序列号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
public static String getSerial() {
|
||||
String serial = "unknow";
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+
|
||||
serial = Build.getSerial();
|
||||
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
|
||||
serial = Build.SERIAL;
|
||||
} else {//8.0-
|
||||
Class<?> c = Class.forName("android.os.SystemProperties");
|
||||
Method get = c.getMethod("get", String.class);
|
||||
serial = (String) get.invoke(c, "ro.serialno");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("e", "读取设备序列号异常:" + e.toString());
|
||||
}
|
||||
return serial;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置信息
|
||||
*
|
||||
@@ -81,7 +77,7 @@ public class SystemUtils {
|
||||
try {
|
||||
Class<?> c = Class.forName("android.os.SystemProperties");
|
||||
Method get = c.getMethod("get", String.class, String.class);
|
||||
value = (String) (get.invoke(c, key, "unknown"));
|
||||
value = (String) (get.invoke(c, key, Build.UNKNOWN));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
@@ -89,6 +85,251 @@ public class SystemUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备序列号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
public static String getSerial() {
|
||||
String serial = Build.UNKNOWN;
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+
|
||||
serial = Build.getSerial();
|
||||
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
|
||||
serial = Build.SERIAL;
|
||||
} else {//8.0-
|
||||
// Class<?> c = Class.forName("android.os.SystemProperties");
|
||||
// Method get = c.getMethod("get", String.class);
|
||||
// serial = (String) get.invoke(c, "ro.serialno");
|
||||
getProperty("ro.serialno", serial);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Logger.e("e", "读取设备序列号异常:" + e.toString());
|
||||
}
|
||||
return serial;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取卡槽 0 (主卡) 的 IMEI
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return IMEI 字符串,如果获取失败则返回空字符串
|
||||
*/
|
||||
@SuppressLint("MissingPermission") // 系统应用已通过 manifest 赋予权限,此处屏蔽 Lint 警告
|
||||
public static String getDefaultImei(Context context) {
|
||||
return getImei(context, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据卡槽 ID 获取对应的 IMEI
|
||||
* 该方法适用于 Android 6.0 (API 23) 及以上的所有版本。
|
||||
* 在 Android 10+ 上,只有系统应用能成功返回数据。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param slotIndex 卡槽索引 (0 代表卡槽1, 1 代表卡槽2)
|
||||
* @return IMEI 字符串,如果获取失败则返回空字符串
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
public static String getImei(Context context, int slotIndex) {
|
||||
String imei = "";
|
||||
try {
|
||||
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tm != null) {
|
||||
// 优先使用标准的 getImei(int slotIndex) 方法 (API 23+)
|
||||
// 对于低版本系统,可以使用反射调用,但现代系统推荐直接调用
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
imei = tm.getImei(slotIndex);
|
||||
} else {
|
||||
// 针对极少数的 Android 6.0 以下的老旧系统兜底
|
||||
imei = tm.getDeviceId(slotIndex);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return imei == null ? "" : imei;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有卡槽的 IMEI 数组
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 包含各个卡槽 IMEI 的字符串数组
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
public static String[] getAllImeis(Context context) {
|
||||
String[] imeis = new String[2]; // 假设最多两个卡槽
|
||||
imeis[0] = getImei(context, 0);
|
||||
imeis[1] = getImei(context, 1);
|
||||
return imeis;
|
||||
}
|
||||
|
||||
public static String getDefaultImSi(Context context) {
|
||||
return getImsiBySubId(context, 0);
|
||||
}
|
||||
|
||||
public static String getImsiBySubId(Context context, int count) {
|
||||
SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
|
||||
SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(count);
|
||||
if (subInfo != null) {
|
||||
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
int subId = subInfo.getSubscriptionId();
|
||||
TelephonyManager tm = telephonyManager.createForSubscriptionId(subId); //关键点,拿到对应subId的tm
|
||||
String mImsi = tm.getSubscriberId(subId); //imsi?not subid?
|
||||
Log.e(TAG, "getSubInfo: mImsi = " + mImsi);
|
||||
return mImsi;
|
||||
} else {
|
||||
return Build.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MAC地址
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static String getWlanMacAddress(Context context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return getMacDefault(context);
|
||||
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
return getMacAddressM();
|
||||
} else {
|
||||
return getMacFromHardware();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 6.0 之前(不包括6.0)
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private static String getMacDefault(Context context) {
|
||||
String mac = "未获取到设备Mac地址";
|
||||
if (context == null) {
|
||||
return mac;
|
||||
}
|
||||
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
WifiInfo info = null;
|
||||
try {
|
||||
info = wifi.getConnectionInfo();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (info == null) {
|
||||
return mac;
|
||||
}
|
||||
mac = info.getMacAddress();
|
||||
if (!TextUtils.isEmpty(mac)) {
|
||||
mac = mac.toUpperCase(Locale.ENGLISH);
|
||||
return mac;
|
||||
} else {
|
||||
return "02:00:00:00:00:00";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 6.0(包括) - Android 7.0(不包括)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static String getMacAddressM() {
|
||||
String mac = "未获取到设备Mac地址";
|
||||
|
||||
try {
|
||||
mac = new BufferedReader(new FileReader("/sys/class/net/wlan0/address")).readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return mac;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历循环所有的网络接口,找到接口是 wlan0
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static String getMacFromHardware() {
|
||||
try {
|
||||
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
|
||||
for (NetworkInterface nif : all) {
|
||||
if (!nif.getName().equalsIgnoreCase("wlan0")) {
|
||||
continue;
|
||||
}
|
||||
byte[] macBytes = nif.getHardwareAddress();
|
||||
StringBuilder res1 = new StringBuilder();
|
||||
for (byte b : macBytes) {
|
||||
res1.append(String.format("%02X:", b));
|
||||
}
|
||||
if (res1 != null) {
|
||||
res1.deleteCharAt(res1.length() - 1);
|
||||
}
|
||||
return res1.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "未获取到设备Mac地址";
|
||||
}
|
||||
|
||||
public static String getFactoryMacAddresses(Context context) {
|
||||
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
String[] factoryMacAddresses = wifiManager.getFactoryMacAddresses();
|
||||
String str = (factoryMacAddresses == null || factoryMacAddresses.length <= 0) ? Build.UNKNOWN : factoryMacAddresses[0];
|
||||
return str.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
public static String getBluetoothMacAddress(Context context) {
|
||||
String mac = Settings.Secure.getString(
|
||||
context.getContentResolver(),
|
||||
"bluetooth_address"
|
||||
);
|
||||
return mac;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String getBtAddressByReflection() {
|
||||
String def = "02:00:00:00:00:00";
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter == null) {
|
||||
return def;
|
||||
}
|
||||
return bluetoothAdapter.getAddress();
|
||||
|
||||
// Field field;
|
||||
// try {
|
||||
// field = BluetoothAdapter.class.getDeclaredField("mService");
|
||||
// field.setAccessible(true);
|
||||
// Object bluetoothManagerService = field.get(bluetoothAdapter);
|
||||
// if (bluetoothManagerService == null) {
|
||||
// return def;
|
||||
// }
|
||||
// Method method = bluetoothManagerService.getClass().getMethod("getAddress");
|
||||
// if (method != null) {
|
||||
// Object obj = method.invoke(bluetoothManagerService);
|
||||
// if (obj != null) {
|
||||
// return obj.toString();
|
||||
// }
|
||||
// }
|
||||
// } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// return def;
|
||||
}
|
||||
|
||||
public static String getBuildId() {
|
||||
return getProperty(Build.ID, Build.UNKNOWN);
|
||||
}
|
||||
|
||||
public static String getBuildDisplayId() {
|
||||
return getProperty(Build.DISPLAY, Build.UNKNOWN);
|
||||
}
|
||||
|
||||
public static boolean isMainProcessName(Context cxt, int pid) {
|
||||
String packageName = cxt.getPackageName();
|
||||
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
@@ -147,7 +388,7 @@ public class SystemUtils {
|
||||
}
|
||||
|
||||
public static void killBackgroundProcesses(Context context, String processName) {
|
||||
Log.e(TAG, "killBackgroundProcesses: " + processName);
|
||||
Logger.e(TAG, "killBackgroundProcesses: " + processName);
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
String packageName;
|
||||
try {
|
||||
@@ -170,7 +411,7 @@ public class SystemUtils {
|
||||
}
|
||||
|
||||
public static void removeTask(Context context, String packageName) {
|
||||
Log.e(TAG, "removeTask: " + packageName);
|
||||
Logger.e(TAG, "removeTask: " + packageName);
|
||||
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
List<ActivityManager.RecentTaskInfo> list = getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), getCurrentUserId());
|
||||
HashMap<String, Integer> taskMap = new HashMap<>();
|
||||
@@ -181,9 +422,9 @@ public class SystemUtils {
|
||||
ActivityManagerNative.getDefault().removeTask(taskMap.get(packageName));
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "removeTask: " + e.getMessage());
|
||||
Logger.e(TAG, "removeTask: " + e.getMessage());
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "removeTask: " + e.getMessage());
|
||||
Logger.e(TAG, "removeTask: " + e.getMessage());
|
||||
}
|
||||
// } else {
|
||||
// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
@@ -200,7 +441,7 @@ public class SystemUtils {
|
||||
return ActivityTaskManager.getService().getRecentTasks(numTasks,
|
||||
RECENT_IGNORE_UNAVAILABLE, userId).getList();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to get recent tasks " + e);
|
||||
Logger.e(TAG, "Failed to get recent tasks " + e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -221,13 +462,13 @@ public class SystemUtils {
|
||||
|
||||
public static boolean isDefaultLauncher(Context context, Class<?> launcherActivityClass) {
|
||||
ComponentName componentName = new ComponentName(context, launcherActivityClass);
|
||||
Log.e(TAG, "isDefaultLauncher: componentName = " + componentName);
|
||||
Logger.e(TAG, "isDefaultLauncher: componentName = " + componentName);
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_HOME);
|
||||
ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
|
||||
if (resolveInfo != null && resolveInfo.activityInfo != null) {
|
||||
ComponentName defaultComponentName = resolveInfo.getComponentInfo().getComponentName();
|
||||
Log.e(TAG, "isDefaultLauncher: defaultComponentName = " + defaultComponentName);
|
||||
Logger.e(TAG, "isDefaultLauncher: defaultComponentName = " + defaultComponentName);
|
||||
return componentName.equals(defaultComponentName);
|
||||
}
|
||||
return false;
|
||||
@@ -269,7 +510,7 @@ public class SystemUtils {
|
||||
try {
|
||||
// 注意:API 29 (Android 10) 中此方法对第三方应用废弃,但对系统签名应用依然有效
|
||||
pm.replacePreferredActivity(filter, bestMatch, set, myLauncher);
|
||||
Log.i("LauncherHelper", "成功设置为默认桌面");
|
||||
Logger.i("LauncherHelper", "成功设置为默认桌面");
|
||||
|
||||
// 可选:发送一个回到桌面的 Intent 来验证
|
||||
// Intent startHome = new Intent(Intent.ACTION_MAIN);
|
||||
@@ -278,9 +519,9 @@ public class SystemUtils {
|
||||
// context.startActivity(startHome);
|
||||
|
||||
} catch (SecurityException e) {
|
||||
Log.e("LauncherHelper", "缺少权限或未生效,请检查系统签名是否正确", e);
|
||||
Logger.e("LauncherHelper", "缺少权限或未生效,请检查系统签名是否正确", e);
|
||||
} catch (Exception e) {
|
||||
Log.e("LauncherHelper", "设置默认桌面失败", e);
|
||||
Logger.e("LauncherHelper", "设置默认桌面失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +555,7 @@ public class SystemUtils {
|
||||
*/
|
||||
public static void addRoleHolderAsUser(Context context, String packageName) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
Log.e(TAG, "RoleManager requires Android 10 or higher.");
|
||||
Logger.e(TAG, "RoleManager requires Android 10 or higher.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -326,7 +567,7 @@ public class SystemUtils {
|
||||
// 1. 检查是否已经是当前角色持有者,避免重复调用
|
||||
if (roleManager.isRoleHeld(roleName)) {
|
||||
// 注意:这里最好再判断一下持有的包名是否为目标包名
|
||||
Log.i(TAG, "Role " + roleName + " is already held by some package.");
|
||||
Logger.i(TAG, "Role " + roleName + " is already held by some package.");
|
||||
// 如果已经是自己,直接返回
|
||||
}
|
||||
|
||||
@@ -337,9 +578,9 @@ public class SystemUtils {
|
||||
// 定义回调
|
||||
Consumer<Boolean> callback = successful -> {
|
||||
if (successful) {
|
||||
Log.i(TAG, "Successfully set " + packageName + " as " + roleName);
|
||||
Logger.i(TAG, "Successfully set " + packageName + " as " + roleName);
|
||||
} else {
|
||||
Log.e(TAG, "Failed to set " + packageName + " as " + roleName + ". Check system logs.");
|
||||
Logger.e(TAG, "Failed to set " + packageName + " as " + roleName + ". Check system logs.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -348,15 +589,15 @@ public class SystemUtils {
|
||||
Method method = RoleManager.class.getMethod("addRoleHolderAsUser",
|
||||
String.class, String.class, int.class, UserHandle.class, Executor.class, Consumer.class);
|
||||
|
||||
Log.d(TAG, "Invoking addRoleHolderAsUser for package: " + packageName);
|
||||
Logger.d(TAG, "Invoking addRoleHolderAsUser for package: " + packageName);
|
||||
method.invoke(roleManager, roleName, packageName, 0, user, executor, callback);
|
||||
|
||||
// roleManager.addRoleHolderAsUser(roleName, packageName, 0, user, executor, callback);
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(TAG, "Method not found. Is this a non-standard ROM?", e);
|
||||
Logger.e(TAG, "Method not found. Is this a non-standard ROM?", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error invoking addRoleHolderAsUser", e);
|
||||
Logger.e(TAG, "Error invoking addRoleHolderAsUser", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,8 +639,8 @@ public class SystemUtils {
|
||||
int screenWidth = metrics.widthPixels;
|
||||
int screenHeight = metrics.heightPixels;
|
||||
|
||||
Log.e(TAG, "takeFullScreenshot: screenWidth " + screenWidth);
|
||||
Log.e(TAG, "takeFullScreenshot: screenHeight " + screenHeight);
|
||||
Logger.e(TAG, "takeFullScreenshot: screenWidth " + screenWidth);
|
||||
Logger.e(TAG, "takeFullScreenshot: screenHeight " + screenHeight);
|
||||
|
||||
try {
|
||||
// 注意:不同 Android 版本的 SurfaceControl.screenshot 方法签名可能不同
|
||||
@@ -425,6 +666,7 @@ public class SystemUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap takeScreenshotHighVersion() {
|
||||
try {
|
||||
// 1. 获取主屏幕的 Token (Internal Display)
|
||||
@@ -465,4 +707,43 @@ public class SystemUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存Bitmap到公共图库(Android 10+ 无需存储权限)
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param bitmap 截图Bitmap
|
||||
* @return 保存成功返回true
|
||||
*/
|
||||
public static boolean saveScreenshotToGallery(Context context, Bitmap bitmap) {
|
||||
if (bitmap == null) return false;
|
||||
|
||||
// 使用MediaStore保存到Pictures目录(Android 10+ 推荐)
|
||||
ContentValues values = new android.content.ContentValues();
|
||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
|
||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Screenshot_" + System.currentTimeMillis() + ".png");
|
||||
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/SystemScreenshots");
|
||||
|
||||
android.net.Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
if (uri == null) return false;
|
||||
|
||||
try (OutputStream outputStream = context.getContentResolver().openOutputStream(uri)) {
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
|
||||
return true;
|
||||
} catch (java.io.IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void screenshotCmd(String filepath, Observer<Integer> observer) {
|
||||
Observable.fromCallable(new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
CmdUtil.Result result = CmdUtil.execute("screencap -p " + filepath);
|
||||
Logger.e(TAG, "call: " + result.error);
|
||||
return result.code;
|
||||
}
|
||||
}).subscribe(observer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
@@ -18,6 +17,7 @@ import com.facebook.rebound.SpringConfig;
|
||||
import com.facebook.rebound.SpringSystem;
|
||||
import com.facebook.rebound.SpringUtil;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class ToggleButton extends View {
|
||||
private static final String TAG = "ToggleButton";
|
||||
@@ -172,7 +172,7 @@ public class ToggleButton extends View {
|
||||
|
||||
public void toggle(boolean animate) {
|
||||
toggleOn = !toggleOn;
|
||||
Log.e(TAG, "toggle: toggleOn = " + toggleOn);
|
||||
Logger.e(TAG, "toggle: toggleOn = " + toggleOn);
|
||||
takeEffect(animate);
|
||||
if (listener != null) {
|
||||
listener.onToggle(toggleOn);
|
||||
@@ -239,12 +239,12 @@ public class ToggleButton extends View {
|
||||
|
||||
|
||||
public int getToggleOnStatu() {
|
||||
Log.e(TAG, "getToggleOnStatu: " + toggleOn);
|
||||
Logger.e(TAG, "getToggleOnStatu: " + toggleOn);
|
||||
return toggleOn ? 1 : 0;
|
||||
}
|
||||
|
||||
public boolean isToggleOn() {
|
||||
Log.e(TAG, "isToggleOn: " + toggleOn);
|
||||
Logger.e(TAG, "isToggleOn: " + toggleOn);
|
||||
return toggleOn;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user