build: 电脑损坏,不知道改了啥

This commit is contained in:
2026-06-11 01:20:13 +08:00
parent d7346e478a
commit aac6b98c92
16 changed files with 914 additions and 376 deletions

View File

@@ -1,5 +1,6 @@
package com.ttstd.dialer.activity.app;
import android.view.DragEvent;
import android.view.View;
import androidx.lifecycle.Observer;
@@ -8,6 +9,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.R;
import com.ttstd.dialer.adapter.AppGridAdapter;
import com.ttstd.dialer.adapter.MoreAppAdapter;
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
import com.ttstd.dialer.config.CommonConfig;
@@ -15,6 +17,7 @@ import com.ttstd.dialer.config.LiveDataAction;
import com.ttstd.dialer.databinding.ActivityAppListBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.livedata.LiveDataBus;
import com.ttstd.dialer.utils.Logger;
import java.util.List;
@@ -24,6 +27,7 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private MoreAppAdapter mMoreAppAdapter;
private AppGridAdapter mAppGridAdapter;
@Override
@@ -49,27 +53,95 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
mViewDataBinding.setClick(new BtnClick());
}
@Override
protected void initView() {
mAppGridAdapter = new AppGridAdapter(this, LoaderManager.getInstance(this));
mViewDataBinding.gvApp.setAdapter(mAppGridAdapter);
mMoreAppAdapter = new MoreAppAdapter(LoaderManager.getInstance(this));
mMoreAppAdapter.setShortcutCallback(new MoreAppAdapter.ShortcutCallback() {
@Override
public void setAppOutside(AppInfo appInfo) {
appInfo.setOutside(1);
mViewModel.updateAppInfo(appInfo);
}
});
mViewDataBinding.recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
mViewDataBinding.recyclerView.setAdapter(mMoreAppAdapter);
mViewDataBinding.recyclerView.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_ENTERED:
v.setAlpha(0.7f);
break;
case DragEvent.ACTION_DRAG_EXITED:
case DragEvent.ACTION_DRAG_ENDED:
v.setAlpha(1.0f);
break;
case DragEvent.ACTION_DROP:
AppInfo appInfo = (AppInfo) event.getLocalState();
if (appInfo != null && appInfo.getHotseat() == 1) {
appInfo.setHotseat(0);
appInfo.setOutside(0);
appInfo.setPosition(mMoreAppAdapter.getItemCount());
mViewModel.updateAppInfo(appInfo);
}
return true;
}
return true;
}
});
mViewDataBinding.clHotseat.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_ENTERED:
v.setAlpha(0.7f);
break;
case DragEvent.ACTION_DRAG_EXITED:
case DragEvent.ACTION_DRAG_ENDED:
v.setAlpha(1.0f);
break;
case DragEvent.ACTION_DROP:
AppInfo appInfo = (AppInfo) event.getLocalState();
if (appInfo != null && appInfo.getHotseat() == 0) {
// 检查数量限制
if (mAppGridAdapter.getCount() >= 5) {
android.widget.Toast.makeText(AppListActivity.this, "快捷栏最多只能添加5个应用", android.widget.Toast.LENGTH_SHORT).show();
return true;
}
appInfo.setHotseat(1);
appInfo.setOutside(1);
appInfo.setHotseatPosition(mAppGridAdapter.getCount());
mViewModel.updateAppInfo(appInfo);
}
return true;
}
return true;
}
});
}
@Override
protected void initData() {
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
.observe(this, event -> {
mViewModel.getDbAppList();
mViewModel.getHotseatApp();
});
mViewModel.mHotseatAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mViewDataBinding.gvApp.setNumColumns(appInfos.size());
mAppGridAdapter.setAppInfos(appInfos);
}
});
mViewModel.getHotseatApp();
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
@@ -84,7 +156,7 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
@Override
public void onChanged(Integer integer) {
if (integer > 0) {
mViewModel.getHotseatApp();
}
mViewModel.getDbAppList();
}

View File

@@ -33,6 +33,41 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
mAppRepository = new AppRepository(context);
}
public MutableLiveData<List<AppInfo>> mHotseatAppData = new MutableLiveData<>();
public void getHotseatApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getHotseatApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("getHotseatApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getHotseatApp", "onNext: " + appInfos);
mHotseatAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getHotseatApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getHotseatApp", "onComplete: ");
}
});
}
public MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
public void getDbAppList() {

View File

@@ -77,15 +77,7 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
mContactInfoAdapter.setItemMoveCallback(new ContactInfoAdapter.ItemMoveCallback() {
@Override
public void onItemMove(int fromPosition, int toPosition) {
Logger.e(TAG, "onItemMove: ");
ContactInfo fromContactInfo = mContactInfos.get(fromPosition);
int fromContactPosition = fromContactInfo.getPosition();
ContactInfo toContactInfo = mContactInfos.get(toPosition);
int toContactPosition = toContactInfo.getPosition();
fromContactInfo.setPosition(toContactPosition);
mViewModel.updateItemPosition(fromContactInfo);
toContactInfo.setPosition(fromContactPosition);
mViewModel.updateItemPosition(toContactInfo);
Logger.e(TAG, "onItemMove: from " + fromPosition + " to " + toPosition);
}
@Override
@@ -93,6 +85,17 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
Logger.e(TAG, "onItemRemoved: ");
}
@Override
public void onItemMoveFinished() {
Logger.e(TAG, "onItemMoveFinished: ");
if (mContactInfos != null) {
for (int i = 0; i < mContactInfos.size(); i++) {
mContactInfos.get(i).setPosition(i + 1);
}
mViewModel.updateContacts(mContactInfos);
}
}
});
mContactInfoAdapter.setOnClickListener(new ContactInfoAdapter.ClickListener() {
@Override

View File

@@ -131,6 +131,39 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
});
}
public void updateContacts(List<ContactInfo> contactInfos) {
Logger.e(TAG, "updateContacts: " + contactInfos.size());
Observable.create(new ObservableOnSubscribe<Boolean>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Boolean> emitter) throws Throwable {
mRepository.update(contactInfos);
emitter.onNext(true);
emitter.onComplete();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Boolean aBoolean) {
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("updateContacts", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("updateContacts", "onComplete: ");
}
});
}
public MutableLiveData<Boolean> mDeleteLiveData = new MutableLiveData<>();
public void deleteContact(long id) {

View File

@@ -79,6 +79,11 @@ public class ContactTestActivity extends BaseMvvmActivity<ContactTestViewModel,
Logger.e(TAG, "mOnlineContactInfoAdapter onItemRemoved: ");
}
@Override
public void onItemMoveFinished() {
}
});
mOnlineContactInfoAdapter.setOnClickListener(new ContactInfoAdapter.ClickListener() {
@Override
@@ -126,6 +131,11 @@ public class ContactTestActivity extends BaseMvvmActivity<ContactTestViewModel,
Logger.e(TAG, "mLocalContactInfoAdapter onItemRemoved: ");
}
@Override
public void onItemMoveFinished() {
}
});
mLocalContactInfoAdapter.setOnClickListener(new ContactInfoAdapter.ClickListener() {
@Override

View File

@@ -188,12 +188,6 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
mAppGridAdapter = new AppGridAdapter(this, LoaderManager.getInstance(this));
mViewDataBinding.gvApp.setAdapter(mAppGridAdapter);
mAppGridAdapter.setShortcutCallback(new AppGridAdapter.ShortcutCallback() {
@Override
public void setAppInside(AppInfo appInfo) {
// 处理回调
}
});
}
@Override
@@ -266,6 +260,8 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
//修补autozie fragment item大小不一致
Logger.e(TAG, "onResume: ");
mViewModel.getOutsideApp();
mViewModel.getHotseatApp();
if (wallpaperScrollHelper != null) {
wallpaperScrollHelper.onResume();
}

View File

@@ -1,8 +1,14 @@
package com.ttstd.dialer.adapter;
import android.content.ClipData;
import android.content.ComponentName;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -21,7 +27,6 @@ import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.R;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.fragment.dialog.shortcut.ShortcutDialogFagment;
import com.ttstd.dialer.utils.ApkUtils;
import com.ttstd.dialer.utils.Logger;
import com.ttstd.iconloader.IconCacheManager;
@@ -51,16 +56,6 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
notifyDataSetChanged();
}
public interface ShortcutCallback {
void setAppInside(AppInfo appInfo);
}
private ShortcutCallback mShortcutCallback;
public void setShortcutCallback(ShortcutCallback shortcutCallback) {
mShortcutCallback = shortcutCallback;
}
@Override
public int getCount() {
return mAppInfos == null ? 0 : mAppInfos.size();
@@ -109,23 +104,54 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
shortcutDialogFagment.setTitile("温馨提示");
shortcutDialogFagment.setTips("是否把这个应用移到“更多应用”里?");
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
@Override
public void onPositiveClick() {
if (mShortcutCallback != null)
mShortcutCallback.setAppInside(appInfo);
shortcutDialogFagment.dismiss();
if (!v.isAttachedToWindow()) {
Logger.e(TAG, "View is not attached to window");
return false;
}
// 添加触感反馈
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
ClipData data = ClipData.newPlainText("appInfo", appInfo.getPackageName());
View.DragShadowBuilder shadowBuilder = new LauncherDragShadowBuilder(v);
// 使用 post 确保在当前事件处理完成后再开始拖拽,提高稳定性并减少崩溃
v.post(() -> {
boolean started = false;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
started = v.startDragAndDrop(data, shadowBuilder, appInfo, View.DRAG_FLAG_OPAQUE);
} else {
started = v.startDrag(data, shadowBuilder, appInfo, 0);
}
} catch (Exception e) {
Logger.e(TAG, "startDragAndDrop error: " + e.getMessage());
}
@Override
public void onNegativeClick() {
shortcutDialogFagment.dismiss();
Logger.e(TAG, "startDragAndDrop result: " + started);
if (started) {
v.setAlpha(0.5f);
}
});
shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
return true;
}
});
holder.root.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
if (event.getLocalState() == appInfo) {
v.setAlpha(0.5f);
return true;
}
return false;
case DragEvent.ACTION_DRAG_ENDED:
v.setAlpha(1.0f);
return true;
}
return false;
}
});
@@ -133,6 +159,34 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
return convertView;
}
private static class LauncherDragShadowBuilder extends View.DragShadowBuilder {
public LauncherDragShadowBuilder(View view) {
super(view);
}
@Override
public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
float scale = 1.2f;
int width = (int) (getView().getWidth() * scale);
int height = (int) (getView().getHeight() * scale);
outShadowSize.set(width, height);
outShadowTouchPoint.set(width / 2, height / 2);
}
@Override
public void onDrawShadow(Canvas canvas) {
float scale = 1.2f;
canvas.save();
canvas.scale(scale, scale);
// 确保在绘制阴影时视图是不透明的
float originalAlpha = getView().getAlpha();
getView().setAlpha(1.0f);
getView().draw(canvas);
getView().setAlpha(originalAlpha);
canvas.restore();
}
}
@NonNull
@NotNull
@Override

View File

@@ -42,6 +42,8 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
void onItemMove(int fromPosition, int toPosition);
void onItemRemoved(int position);
void onItemMoveFinished();
}
public interface ClickListener {
@@ -145,6 +147,12 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
notifyItemRemoved(position);
}
public void onItemMoveFinished() {
if (mItemMoveCallback != null) {
mItemMoveCallback.onItemMoveFinished();
}
}
public class ContactInfoHolder extends RecyclerView.ViewHolder {
ConstraintLayout root, clOperation;
NiceImageView nv_avatar;

View File

@@ -1,8 +1,14 @@
package com.ttstd.dialer.adapter;
import android.content.ClipData;
import android.content.ComponentName;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -21,7 +27,6 @@ import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.R;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.fragment.dialog.shortcut.ShortcutDialogFagment;
import com.ttstd.dialer.utils.ApkUtils;
import com.ttstd.dialer.utils.Logger;
import com.ttstd.iconloader.IconCacheManager;
@@ -55,16 +60,6 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
notifyDataSetChanged();
}
public interface ShortcutCallback {
void setAppOutside(AppInfo appInfo);
}
private ShortcutCallback mShortcutCallback;
public void setShortcutCallback(ShortcutCallback shortcutCallback) {
mShortcutCallback = shortcutCallback;
}
@NonNull
@NotNull
@Override
@@ -73,9 +68,11 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
return new AppHolder(LayoutInflater.from(mContext).inflate(R.layout.item_more_app, parent, false));
}
@Override
public void onBindViewHolder(@NonNull @NotNull AppHolder holder, int position) {
AppInfo appInfo = mAppInfos.get(position);
holder.root.setAlpha(1.0f);
Drawable drawable = mIconCacheManager.getIcon(appInfo.getComponentName().flattenToShortString());
if (drawable != null) {
holder.iv_icon.setImageDrawable(drawable);
@@ -92,29 +89,89 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
shortcutDialogFagment.setTitile("温馨提示");
shortcutDialogFagment.setTips("是否将应用放在桌面显示");
if (!v.isAttachedToWindow()) {
Logger.e(TAG, "View is not attached to window");
return false;
}
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
@Override
public void onPositiveClick() {
if (mShortcutCallback != null)
mShortcutCallback.setAppOutside(appInfo);
shortcutDialogFagment.dismiss();
// 添加触感反馈
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
ClipData data = ClipData.newPlainText("appInfo", appInfo.getPackageName());
View.DragShadowBuilder shadowBuilder = new LauncherDragShadowBuilder(v);
// 使用 post 确保在当前事件处理完成后再开始拖拽,提高稳定性并减少崩溃
v.post(() -> {
boolean started = false;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 修复:移除 DRAG_FLAG_GLOBAL 以解决某些设备上的 IllegalStateException
// 增加 DRAG_FLAG_OPAQUE 提升性能
started = v.startDragAndDrop(data, shadowBuilder, appInfo, View.DRAG_FLAG_OPAQUE);
} else {
started = v.startDrag(data, shadowBuilder, appInfo, 0);
}
} catch (Exception e) {
Logger.e(TAG, "startDragAndDrop error: " + e.getMessage());
}
@Override
public void onNegativeClick() {
shortcutDialogFagment.dismiss();
Logger.e(TAG, "startDragAndDrop result: " + started);
if (started) {
v.setAlpha(0.5f);
}
});
shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
return true;
}
});
holder.root.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
if (event.getLocalState() == appInfo) {
v.setAlpha(0.5f);
return true;
}
return false;
case DragEvent.ACTION_DRAG_ENDED:
v.setAlpha(1.0f);
return true;
}
return false;
}
});
}
private static class LauncherDragShadowBuilder extends View.DragShadowBuilder {
public LauncherDragShadowBuilder(View view) {
super(view);
}
@Override
public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
float scale = 1.2f;
int width = (int) (getView().getWidth() * scale);
int height = (int) (getView().getHeight() * scale);
outShadowSize.set(width, height);
outShadowTouchPoint.set(width / 2, height / 2);
}
@Override
public void onDrawShadow(Canvas canvas) {
float scale = 1.2f;
canvas.save();
canvas.scale(scale, scale);
// 确保在绘制阴影时视图是不透明的
float originalAlpha = getView().getAlpha();
getView().setAlpha(1.0f);
getView().draw(canvas);
getView().setAlpha(originalAlpha);
canvas.restore();
}
}
@Override
public int getItemCount() {
return mAppInfos == null ? 0 : mAppInfos.size();

View File

@@ -24,6 +24,9 @@ public interface ContactDao {
@Update
int update(ContactInfo contactInfo);
@Update
void update(List<ContactInfo> contactInfos);
@Delete
int delete(ContactInfo contactInfo);

View File

@@ -46,6 +46,10 @@ public class ContactRepository {
return mContactDao.update(contactInfo);
}
public void update(List<ContactInfo> contactInfos) {
mContactDao.update(contactInfos);
}
// 删除联系人
public int delete(ContactInfo contactInfo) {
return mContactDao.delete(contactInfo);

View File

@@ -56,14 +56,14 @@ public class AppManager {
private static final int TOTAL_STEPS_NORMAL = 3; // 正常流程总步骤
private static final int TOTAL_STEPS_FIRST = 2; // 首次初始化总步骤
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
@SuppressLint("StaticFieldLeak")
private static volatile AppManager INSTANCE;
private Context mContext;
private PackageManager mPackageManager;
Context mContext;
PackageManager mPackageManager;
private AppRepository mAppRepository;
AppRepository mAppRepository;
private Set<ProgressCallback> mProgressCallbacks = new CopyOnWriteArraySet<>();
@@ -142,46 +142,216 @@ public class AppManager {
// 通知进度回调
private void notifyProgress(String step, int current, int total) {
mProgressCallbacks.forEach(callback -> callback.onProgress(step, current, total));
for (ProgressCallback callback : mProgressCallbacks) {
callback.onProgress(step, current, total);
}
}
// 通知完成回调
private void notifyCompleted(boolean success, String message) {
mProgressCallbacks.forEach(callback -> callback.onCompleted(success, message));
for (ProgressCallback callback : mProgressCallbacks) {
callback.onCompleted(success, message);
}
}
// 异步执行应用列表处理流程
public void executeAppListProcessing() {
CompletableFuture
.supplyAsync(this::getAllDesktopSortApps, ASYNC_EXECUTOR)
.thenComposeAsync(desktopApps -> {
// 根据应用列表是否为空,选择不同处理流程
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
V26Helper.executeAppListProcessing(this);
} else {
executeAppListProcessingLegacy();
}
}
private void executeAppListProcessingLegacy() {
ASYNC_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
try {
List<AppInfo> desktopApps = getAllDesktopSortApps();
if (desktopApps.isEmpty()) {
// 首次初始化流程:首次数据加载 -> 更新位置
return processFirstTimeApps()
.thenComposeAsync(unused -> {
notifyProgress(STEP_UPDATE_POSITION, 2, TOTAL_STEPS_FIRST);
return updatePosition();
}, ASYNC_EXECUTOR);
processFirstTimeAppsSync();
notifyProgress(STEP_UPDATE_POSITION, 2, TOTAL_STEPS_FIRST);
updatePositionSync();
} else {
// 正常流程:移除未安装 -> 添加新应用 -> 更新位置
return processUninstalledApps(desktopApps)
.thenComposeAsync(unused -> {
notifyProgress(STEP_ADD_NEW_APPS, 2, TOTAL_STEPS_NORMAL);
return processNewApps();
}, ASYNC_EXECUTOR)
.thenComposeAsync(unused -> {
notifyProgress(STEP_UPDATE_POSITION, 3, TOTAL_STEPS_NORMAL);
return updatePosition();
}, ASYNC_EXECUTOR);
processUninstalledAppsSync(desktopApps);
notifyProgress(STEP_ADD_NEW_APPS, 2, TOTAL_STEPS_NORMAL);
processNewAppsSync();
notifyProgress(STEP_UPDATE_POSITION, 3, TOTAL_STEPS_NORMAL);
updatePositionSync();
}
}, ASYNC_EXECUTOR)
.thenRun(() -> notifyCompleted(true, "应用列表处理完成"))
.exceptionally(throwable -> {
Logger.e(TAG, "异步处理失败", throwable);
notifyCompleted(false, "处理失败: " + throwable.getMessage());
return null;
});
notifyCompleted(true, "应用列表处理完成");
} catch (Exception e) {
Logger.e(TAG, "异步处理失败", e);
notifyCompleted(false, "处理失败: " + e.getMessage());
}
}
});
}
// 同步版本的处理逻辑,用于低版本适配
private void processFirstTimeAppsSync() {
notifyProgress(STEP_FIRST_INIT, 1, TOTAL_STEPS_FIRST);
Logger.i(TAG, "进入首次应用数据初始化流程(Legacy)");
try {
List<ResolveInfo> allLauncherApps = ApkUtils.getAllLauncherResolveInfo(mContext);
if (allLauncherApps.isEmpty()) {
Logger.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
return;
}
List<AppInfo> allFirstApps = new ArrayList<>();
for (ResolveInfo ri : allLauncherApps) {
if (ri != null) {
AppInfo appInfo = resolveInfoToDesktopApp(ri);
if (appInfo != null) {
allFirstApps.add(appInfo);
}
}
}
Collections.sort(allFirstApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
boolean isSystem1 = ApkUtils.isSystemApp(mContext, o1.getPackageName());
boolean isSystem2 = ApkUtils.isSystemApp(mContext, o2.getPackageName());
if (isSystem1 != isSystem2) {
return isSystem2 ? 1 : -1;
}
return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel());
}
});
int hotseatCounter = 0;
for (int i = 0; i < allFirstApps.size(); i++) {
AppInfo appInfo = allFirstApps.get(i);
if (DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName())) {
appInfo.setOutside(1);
} else {
appInfo.setOutside(0);
}
if (DEFAULT_DIALER_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter++);
} else if (DEFAULT_CONTACT_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter++);
} else if (DEFAULT_MESSAGE_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter++);
} else if (DEFAULT_SETTINGS_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter++);
} else {
appInfo.setHotseat(0);
appInfo.setHotseatPosition(0);
}
appInfo.setPosition(i);
}
for (AppInfo app : allFirstApps) {
try {
mAppRepository.insert(app);
} catch (Exception e) {
Logger.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
}
}
Logger.i(TAG, "首次初始化完成(Legacy),插入默认应用: " + allFirstApps.size() + "");
} catch (Exception e) {
Logger.e(TAG, "首次应用数据初始化失败", e);
throw new RuntimeException("首次初始化失败", e);
}
}
private void processUninstalledAppsSync(List<AppInfo> appInfos) {
notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL);
if (appInfos.isEmpty()) {
Logger.w(TAG, "桌面应用列表为空,跳过删除处理");
return;
}
for (AppInfo app : appInfos) {
if (app != null && !ApkUtils.isInstalled(mContext, app.getPackageName())) {
try {
int id = mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
if (id > 0) {
mAppRepository.deleteById(id);
}
} catch (Exception e) {
Logger.e(TAG, "处理未安装应用失败: " + app.getPackageName(), e);
}
}
}
}
private void processNewAppsSync() {
try {
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
if (resolveInfos.isEmpty()) {
Logger.w(TAG, "未获取到启动器应用列表,跳过新应用处理");
return;
}
List<AppInfo> newApps = new ArrayList<>();
for (ResolveInfo ri : resolveInfos) {
if (ri != null) {
AppInfo app = resolveInfoToDesktopApp(ri);
if (app != null && ApkUtils.isInstalled(mContext, app.getPackageName()) && !isAppExists(app)) {
newApps.add(app);
}
}
}
Collections.sort(newApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel());
}
});
if (!newApps.isEmpty()) {
int totalCount = mAppRepository.getTotalCount();
for (int i = 0; i < newApps.size(); i++) {
AppInfo app = newApps.get(i);
app.setPosition(totalCount + i);
try {
mAppRepository.insert(app);
} catch (Exception e) {
Logger.e(TAG, "插入新应用失败: " + app.getPackageName(), e);
}
}
}
} catch (Exception e) {
Logger.e(TAG, "处理新应用时发生异常", e);
}
}
private void updatePositionSync() {
try {
List<AppInfo> appInfos = getAllDesktopSortApps();
if (appInfos.isEmpty()) return;
Collections.sort(appInfos, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Integer.compare(o1.getPosition(), o2.getPosition());
}
});
for (int i = 0; i < appInfos.size(); i++) {
AppInfo app = appInfos.get(i);
app.setPosition(i);
try {
mAppRepository.update(app);
} catch (Exception e) {
Logger.e(TAG, "更新应用位置失败: " + app.getPackageName(), e);
}
}
} catch (Exception e) {
Logger.e(TAG, "更新应用位置时发生异常", e);
}
}
// 第一步:获取所有桌面应用(空值安全处理)
@@ -195,188 +365,8 @@ public class AppManager {
}
}
// 首次初始化应用数据(针对首次获取为空的场景)
private CompletableFuture<Void> processFirstTimeApps() {
return CompletableFuture.runAsync(() -> {
notifyProgress(STEP_FIRST_INIT, 1, TOTAL_STEPS_FIRST);
Logger.i(TAG, "进入首次应用数据初始化流程");
try {
// 获取所有可启动应用
List<ResolveInfo> allLauncherApps = ApkUtils.getAllLauncherResolveInfo(mContext);
if (allLauncherApps.isEmpty()) {
Logger.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
return;
}
List<AppInfo> allFirstApps = allLauncherApps.stream()
.filter(Objects::nonNull)
.map(this::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.sorted((o1, o2) -> Boolean.compare(ApkUtils.isSystemApp(mContext, o2.getPackageName()), ApkUtils.isSystemApp(mContext, o1.getPackageName())))
.sorted(getAppComparator())
.collect(Collectors.toList());
final int[] hotseatCounter = {0};
IntStream.range(0, allFirstApps.size())
.forEach(new IntConsumer() {
@Override
public void accept(int index) {
AppInfo appInfo = allFirstApps.get(index);
if (DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName())) {
appInfo.setOutside(1);
} else {
appInfo.setOutside(0);
}
if (DEFAULT_DIALER_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else if (DEFAULT_CONTACT_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else if (DEFAULT_MESSAGE_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else if (DEFAULT_SETTINGS_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else {
appInfo.setHotseat(0);
appInfo.setHotseatPosition(0);
}
appInfo.setPosition(index);
}
});
// 批量插入首次数据
allFirstApps.forEach(app -> {
try {
mAppRepository.insert(app);
} catch (Exception e) {
Logger.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
}
});
Logger.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + "");
} catch (Exception e) {
Logger.e(TAG, "首次应用数据初始化失败", e);
throw new RuntimeException("首次初始化失败", e); // 抛出异常触发回调
}
}, ASYNC_EXECUTOR);
}
// 处理未安装的应用(删除操作)
private CompletableFuture<Void> processUninstalledApps(List<AppInfo> appInfos) {
return CompletableFuture.runAsync(() -> {
notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL);
if (appInfos.isEmpty()) {
Logger.w(TAG, "桌面应用列表为空,跳过删除处理");
return;
}
List<Integer> ids = appInfos.stream()
.filter(Objects::nonNull)
.filter(app -> !ApkUtils.isInstalled(mContext, app.getPackageName()))
.map(app -> {
try {
return mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
} catch (Exception e) {
Logger.e(TAG, "获取应用ID失败: " + app.getPackageName(), e);
return -1;
}
})
.filter(id -> id > 0)
.collect(Collectors.toList());
if (!ids.isEmpty()) {
ids.forEach(id -> {
try {
mAppRepository.deleteById(id);
} catch (Exception e) {
Logger.e(TAG, "删除应用失败, ID: " + id, e);
}
});
Logger.i(TAG, "成功删除 " + ids.size() + " 个未安装应用");
}
}, ASYNC_EXECUTOR);
}
// 处理新安装的应用(插入操作)- 非首次场景
private CompletableFuture<Void> processNewApps() {
return CompletableFuture.runAsync(() -> {
try {
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
if (resolveInfos.isEmpty()) {
Logger.w(TAG, "未获取到启动器应用列表,跳过新应用处理");
return;
}
List<AppInfo> newApps = resolveInfos.stream()
.filter(Objects::nonNull)
.map(this::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.filter(app -> ApkUtils.isInstalled(mContext, app.getPackageName()))
.filter(app -> !isAppExists(app)) // 过滤已存在的应用
.sorted(getAppComparator())
.collect(Collectors.toList());
if (!newApps.isEmpty()) {
newApps.forEach(app -> app.setPosition(mAppRepository.getTotalCount()));
newApps.forEach(app -> {
try {
mAppRepository.insert(app);
} catch (Exception e) {
Logger.e(TAG, "插入新应用失败: " + app.getPackageName(), e);
}
});
Logger.i(TAG, "成功插入 " + newApps.size() + " 个新应用");
} else {
Logger.i(TAG, "没有需要插入的新应用");
}
} catch (Exception e) {
Logger.e(TAG, "处理新应用时发生异常", e);
throw new RuntimeException("新应用处理失败", e);
}
}, ASYNC_EXECUTOR);
}
// 更新应用位置
private CompletableFuture<Void> updatePosition() {
return CompletableFuture.runAsync(() -> {
try {
List<AppInfo> appInfos = getAllDesktopSortApps();
if (appInfos.isEmpty()) {
Logger.w(TAG, "无应用数据,跳过位置更新");
return;
}
List<AppInfo> sortedApps = appInfos.stream()
.filter(Objects::nonNull)
.sorted(Comparator.comparingInt(AppInfo::getPosition))
.collect(Collectors.toList());
IntStream.range(0, sortedApps.size())
.forEach(index -> {
AppInfo app = sortedApps.get(index);
app.setPosition(index);
try {
mAppRepository.update(app);
} catch (Exception e) {
Logger.e(TAG, "更新应用位置失败: " + app.getPackageName(), e);
}
});
Logger.i(TAG, "成功更新 " + sortedApps.size() + " 个应用的位置");
} catch (Exception e) {
Logger.e(TAG, "更新应用位置时发生异常", e);
throw new RuntimeException("位置更新失败", e);
}
}, ASYNC_EXECUTOR);
}
// 工具方法ResolveInfo转换为DesktopSortApp
private AppInfo resolveInfoToDesktopApp(ResolveInfo resolveInfo) {
AppInfo resolveInfoToDesktopApp(ResolveInfo resolveInfo) {
ComponentName component = new ComponentName(
resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name
@@ -385,7 +375,7 @@ public class AppManager {
}
// 工具方法:检查应用是否已存在
private boolean isAppExists(AppInfo app) {
boolean isAppExists(AppInfo app) {
try {
return mAppRepository.checkAppInfoExists(app.getPackageName(), app.getClassName()) > 0;
} catch (Exception e) {
@@ -395,8 +385,13 @@ public class AppManager {
}
// 工具方法:获取应用排序器(统一排序逻辑)
private Comparator<AppInfo> getAppComparator() {
return Comparator.comparing(AppInfo::getLabel, Collator.getInstance(Locale.CHINESE));
Comparator<AppInfo> getAppComparator() {
return new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel());
}
};
}
public void updateApp(String packageName) {
@@ -408,27 +403,23 @@ public class AppManager {
List<ResolveInfo> resolveInfos = ApkUtils.getResolveInfoByPackageName(mContext, packageName);
if (!resolveInfos.isEmpty()) {
resolveInfos.stream()
.map(this::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.forEach(app -> {
try {
int existingId = mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
for (ResolveInfo ri : resolveInfos) {
AppInfo app = resolveInfoToDesktopApp(ri);
if (app != null) {
try {
int existingId = mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
if (existingId > 0) {
// app.setId(existingId);
// mAppRepository.update(app);
// Logger.i(TAG, "更新应用信息: " + app.getPackageName());
} else {
app.setOutside(1);
app.setPosition(mAppRepository.getTotalCount());
mAppRepository.insert(app);
Logger.i(TAG, "新增应用信息: " + app.getPackageName());
}
} catch (Exception e) {
Logger.e(TAG, "处理应用失败: " + app.getPackageName(), e);
if (existingId <= 0) {
app.setOutside(1);
app.setPosition(mAppRepository.getTotalCount());
mAppRepository.insert(app);
Logger.i(TAG, "新增应用信息: " + app.getPackageName());
}
});
} catch (Exception e) {
Logger.e(TAG, "处理应用失败: " + app.getPackageName(), e);
}
}
}
} else {
Logger.w(TAG, "应用已安装但无可启动的 Launcher Activity");
}
@@ -438,21 +429,25 @@ public class AppManager {
try {
List<AppInfo> allApps = mAppRepository.getAllApp();
if (allApps != null) {
List<Integer> idsToDelete = allApps.stream()
.filter(app -> packageName.equals(app.getPackageName()))
.map(AppInfo::getId)
.filter(id -> id > 0)
.collect(Collectors.toList());
List<Integer> idsToDelete = new ArrayList<>();
for (AppInfo app : allApps) {
if (packageName.equals(app.getPackageName())) {
int id = app.getId();
if (id > 0) {
idsToDelete.add(id);
}
}
}
if (!idsToDelete.isEmpty()) {
idsToDelete.forEach(id -> {
for (Integer id : idsToDelete) {
try {
mAppRepository.deleteById(id);
Logger.i(TAG, "删除应用数据库记录, ID: " + id);
} catch (Exception e) {
Logger.e(TAG, "删除应用记录失败, ID: " + id, e);
}
});
}
Logger.i(TAG, "成功删除 " + idsToDelete.size() + " 条记录");
} else {
Logger.i(TAG, "数据库中未找到该应用的记录");
@@ -469,34 +464,58 @@ public class AppManager {
// 重构getAllApp方法复用现有处理逻辑
public void refreshAllApps() {
CompletableFuture.runAsync(() -> {
List<AppInfo> allApps = getAllDesktopSortApps();
if (allApps.isEmpty()) {
processFirstTimeApps().join();
} else {
processUninstalledApps(allApps).join();
processNewApps().join();
}
updatePosition().join();
}, ASYNC_EXECUTOR);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
V26Helper.refreshAllApps(this);
} else {
ASYNC_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
List<AppInfo> allApps = getAllDesktopSortApps();
if (allApps.isEmpty()) {
processFirstTimeAppsSync();
} else {
processUninstalledAppsSync(allApps);
processNewAppsSync();
}
updatePositionSync();
}
});
}
}
public String getDefaultAppList() {
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(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(
ApkUtils.isSystemApp(mContext, b.getPackageName()),
ApkUtils.isSystemApp(mContext, a.getPackageName())))
.collect(Collectors.toCollection(LinkedList::new));
List<AppInfo> defaultApps = new LinkedList<>();
for (ResolveInfo ri : resolveInfos) {
if (DEFAULT_APP_PACKAGES.contains(ri.activityInfo.packageName)) {
ComponentName component = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
defaultApps.add(new AppInfo(mContext, component));
}
}
IntStream.range(0, defaultApps.size())
.forEach(i -> defaultApps.get(i).setPosition(i));
Collections.sort(defaultApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel());
}
});
Collections.sort(defaultApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo a, AppInfo b) {
boolean isSystemA = ApkUtils.isSystemApp(mContext, a.getPackageName());
boolean isSystemB = ApkUtils.isSystemApp(mContext, b.getPackageName());
if (isSystemA != isSystemB) {
return isSystemB ? 1 : -1;
}
return 0;
}
});
for (int i = 0; i < defaultApps.size(); i++) {
defaultApps.get(i).setPosition(i);
}
return GsonUtils.toJSONString(defaultApps);
}
@@ -506,19 +525,12 @@ public class AppManager {
}};
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) {
List<ApkInstalledInfo> apkInstalledInfos = new ArrayList<>();
for (PackageInfo packageInfo : installedApps) {
String packageName = packageInfo.packageName;
if (!ApkUtils.isSystemApp(mContext, packageName) || WHITE_SYSTEM_PACKAGES.contains(packageName)) {
ApkInstalledInfo apkInstalledInfo = new ApkInstalledInfo();
apkInstalledInfo.setPackageName(packageInfo.packageName);
apkInstalledInfo.setAppName(packageInfo.applicationInfo.loadLabel(mPackageManager).toString());
@@ -531,33 +543,260 @@ public class AppManager {
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());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
StorageStatsHelper.fillStats(mContext, packageInfo, apkInstalledInfo);
}
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)));
File publicSourceFile = new File(packageInfo.applicationInfo.publicSourceDir);
if (publicSourceFile.exists()) {
apkInstalledInfo.setMd5(HashUtils.getFileMd5(publicSourceFile));
}
apkInstalledInfo.setSystemApp(ApkUtils.isSystemApp(mContext, packageInfo.packageName));
return apkInstalledInfo;
apkInstalledInfos.add(apkInstalledInfo);
}
}).collect(Collectors.toList());
}
return apkInstalledInfos;
}
}
private static class StorageStatsHelper {
@SuppressLint("NewApi")
static void fillStats(Context context, PackageInfo packageInfo, ApkInstalledInfo apkInstalledInfo) {
try {
StorageStatsManager ssm = (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
if (ssm != null) {
StorageStats stats = ssm.queryStatsForPackage(
packageInfo.applicationInfo.storageUuid,
packageInfo.packageName,
android.os.Process.myUserHandle()
);
apkInstalledInfo.setApkSize(stats.getAppBytes());
apkInstalledInfo.setDataSize(stats.getDataBytes());
apkInstalledInfo.setCacheSize(stats.getCacheBytes());
}
} catch (Exception e) {
Log.e(TAG, "getApkInstallInfos stats error: " + e.getMessage());
}
}
}
private static class V26Helper {
@SuppressLint("NewApi")
static void executeAppListProcessing(final AppManager manager) {
CompletableFuture.supplyAsync(new java.util.function.Supplier<List<AppInfo>>() {
@Override
public List<AppInfo> get() {
return manager.getAllDesktopSortApps();
}
}, ASYNC_EXECUTOR).thenComposeAsync(new java.util.function.Function<List<AppInfo>, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final List<AppInfo> desktopApps) {
if (desktopApps.isEmpty()) {
return processFirstTimeApps(manager).thenComposeAsync(new java.util.function.Function<Void, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(Void unused) {
manager.notifyProgress(STEP_UPDATE_POSITION, 2, TOTAL_STEPS_FIRST);
return updatePosition(manager);
}
}, ASYNC_EXECUTOR);
} else {
return processUninstalledApps(manager, desktopApps).thenComposeAsync(new java.util.function.Function<Void, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(Void unused) {
manager.notifyProgress(STEP_ADD_NEW_APPS, 2, TOTAL_STEPS_NORMAL);
return processNewApps(manager);
}
}, ASYNC_EXECUTOR).thenComposeAsync(new java.util.function.Function<Void, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(Void unused) {
manager.notifyProgress(STEP_UPDATE_POSITION, 3, TOTAL_STEPS_NORMAL);
return updatePosition(manager);
}
}, ASYNC_EXECUTOR);
}
}
}, ASYNC_EXECUTOR).thenRun(new Runnable() {
@Override
public void run() {
manager.notifyCompleted(true, "应用列表处理完成");
}
}).exceptionally(new java.util.function.Function<Throwable, Void>() {
@Override
public Void apply(Throwable throwable) {
Logger.e(TAG, "异步处理失败", throwable);
manager.notifyCompleted(false, "处理失败: " + throwable.getMessage());
return null;
}
});
}
@SuppressLint("NewApi")
static CompletableFuture<Void> processFirstTimeApps(final AppManager manager) {
return CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
manager.notifyProgress(STEP_FIRST_INIT, 1, TOTAL_STEPS_FIRST);
Logger.i(TAG, "进入首次应用数据初始化流程");
try {
List<ResolveInfo> allLauncherApps = ApkUtils.getAllLauncherResolveInfo(manager.mContext);
if (allLauncherApps.isEmpty()) {
Logger.w(TAG, "未获取到任何可启动应用,无法完成首次初始化");
return;
}
List<AppInfo> allFirstApps = allLauncherApps.stream()
.filter(Objects::nonNull)
.map(manager::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.sorted((o1, o2) -> Boolean.compare(ApkUtils.isSystemApp(manager.mContext, o2.getPackageName()), ApkUtils.isSystemApp(manager.mContext, o1.getPackageName())))
.sorted(manager.getAppComparator())
.collect(Collectors.toList());
final int[] hotseatCounter = {0};
IntStream.range(0, allFirstApps.size()).forEach(index -> {
AppInfo appInfo = allFirstApps.get(index);
if (DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName())) {
appInfo.setOutside(1);
} else {
appInfo.setOutside(0);
}
if (DEFAULT_DIALER_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else if (DEFAULT_CONTACT_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else if (DEFAULT_MESSAGE_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else if (DEFAULT_SETTINGS_PACKAGE.contains(appInfo.getPackageName())) {
appInfo.setHotseat(1);
appInfo.setHotseatPosition(hotseatCounter[0]++);
} else {
appInfo.setHotseat(0);
appInfo.setHotseatPosition(0);
}
appInfo.setPosition(index);
});
allFirstApps.forEach(app -> {
try {
manager.mAppRepository.insert(app);
} catch (Exception e) {
Logger.e(TAG, "首次初始化插入应用失败: " + app.getPackageName(), e);
}
});
Logger.i(TAG, "首次初始化完成,插入默认应用: " + allFirstApps.size() + "");
} catch (Exception e) {
Logger.e(TAG, "首次应用数据初始化失败", e);
throw new RuntimeException("首次初始化失败", e);
}
}
}, ASYNC_EXECUTOR);
}
@SuppressLint("NewApi")
static CompletableFuture<Void> processUninstalledApps(final AppManager manager, final List<AppInfo> appInfos) {
return CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
manager.notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL);
if (appInfos.isEmpty()) return;
List<Integer> ids = appInfos.stream()
.filter(Objects::nonNull)
.filter(app -> !ApkUtils.isInstalled(manager.mContext, app.getPackageName()))
.map(app -> {
try {
return manager.mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
} catch (Exception e) {
return -1;
}
})
.filter(id -> id > 0)
.collect(Collectors.toList());
if (!ids.isEmpty()) {
ids.forEach(id -> {
try {
manager.mAppRepository.deleteById(id);
} catch (Exception e) {}
});
}
}
}, ASYNC_EXECUTOR);
}
@SuppressLint("NewApi")
static CompletableFuture<Void> processNewApps(final AppManager manager) {
return CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(manager.mContext);
if (resolveInfos.isEmpty()) return;
List<AppInfo> newApps = resolveInfos.stream()
.filter(Objects::nonNull)
.map(manager::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.filter(app -> ApkUtils.isInstalled(manager.mContext, app.getPackageName()))
.filter(manager::isAppExists)
.sorted(manager.getAppComparator())
.collect(Collectors.toList());
if (!newApps.isEmpty()) {
int count = manager.mAppRepository.getTotalCount();
for (int i = 0; i < newApps.size(); i++) {
AppInfo app = newApps.get(i);
app.setPosition(count + i);
manager.mAppRepository.insert(app);
}
}
} catch (Exception e) {}
}
}, ASYNC_EXECUTOR);
}
@SuppressLint("NewApi")
static CompletableFuture<Void> updatePosition(final AppManager manager) {
return CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
List<AppInfo> appInfos = manager.getAllDesktopSortApps();
if (appInfos.isEmpty()) return;
List<AppInfo> sortedApps = appInfos.stream()
.filter(Objects::nonNull)
.sorted(Comparator.comparingInt(AppInfo::getPosition))
.collect(Collectors.toList());
for (int i = 0; i < sortedApps.size(); i++) {
AppInfo app = sortedApps.get(i);
app.setPosition(i);
manager.mAppRepository.update(app);
}
} catch (Exception e) {}
}
}, ASYNC_EXECUTOR);
}
@SuppressLint("NewApi")
static void refreshAllApps(final AppManager manager) {
CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
List<AppInfo> allApps = manager.getAllDesktopSortApps();
if (allApps.isEmpty()) {
processFirstTimeApps(manager).join();
} else {
processUninstalledApps(manager, allApps).join();
processNewApps(manager).join();
}
updatePosition(manager).join();
}
}, ASYNC_EXECUTOR);
}
}
}

View File

@@ -51,4 +51,10 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
public boolean isItemViewSwipeEnabled() {
return false;
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
mAdapter.onItemMoveFinished();
}
}

View File

@@ -48,13 +48,31 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_hotseat"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_100"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginHorizontal="@dimen/dp_8"
android:background="@drawable/main_hotseat_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bar">
<GridView
android:id="@+id/gv_app"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="auto_fit"
android:orientation="horizontal" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bar" />
app:layout_constraintTop_toBottomOf="@+id/cl_hotseat" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -38,6 +38,8 @@
android:id="@+id/cl_hotseat"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_100"
android:layout_marginHorizontal="@dimen/dp_8"
android:background="@drawable/main_hotseat_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
@@ -46,8 +48,6 @@
android:id="@+id/gv_app"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="@dimen/dp_8"
android:background="@drawable/main_hotseat_background"
android:numColumns="auto_fit"
android:orientation="horizontal" />

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip