feat: 优化Base类自动设置DataBinding,app拖动数据更加合理

This commit is contained in:
TongTongStudio
2026-06-14 20:22:16 +08:00
parent a59370cf32
commit 5b456a5bf0
49 changed files with 1173 additions and 559 deletions

View File

@@ -33,9 +33,6 @@ public class AlarmAddActivity extends BaseMvvmActivity<AlarmAddViewModel, Activi
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -31,9 +31,6 @@ public class AlarmEditActivity extends BaseMvvmActivity<AlarmEditViewModel, Acti
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
mAlarmInfo = (AlarmInfo) getIntent().getSerializableExtra("AlarmInfo");

View File

@@ -48,9 +48,6 @@ public class AlarmListActivity extends BaseMvvmActivity<AlarmListViewModel, Acti
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -17,7 +17,6 @@ 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;
@@ -47,9 +46,6 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@@ -57,10 +53,44 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
@Override
protected void initView() {
mAppGridAdapter = new AppGridAdapter(this, LoaderManager.getInstance(this));
mAppGridAdapter.setShortcutCallback(new AppGridAdapter.ShortcutCallback() {
@Override
public void setAppInside(AppInfo appInfo) {
appInfo.setOutside(0);
appInfo.setHotseat(0);
mViewModel.updateAppInfo(appInfo);
}
});
mViewDataBinding.gvApp.setLayoutManager(new GridLayoutManager(this, 5) {
@Override
public boolean canScrollVertically() {
return false;
}
@Override
public boolean canScrollHorizontally() {
return false;
}
});
mViewDataBinding.gvApp.setItemAnimator(null);
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);
}
@Override
public void setAppHotSeat(AppInfo appInfo) {
appInfo.setHotseat(1);
appInfo.setOutside(1);
mViewModel.updateAppInfo(appInfo);
}
});
mViewDataBinding.recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
mViewDataBinding.recyclerView.setAdapter(mMoreAppAdapter);
@@ -82,7 +112,6 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
if (appInfo != null && appInfo.getHotseat() == 1) {
appInfo.setHotseat(0);
appInfo.setOutside(0);
appInfo.setPosition(mMoreAppAdapter.getItemCount());
mViewModel.updateAppInfo(appInfo);
}
return true;
@@ -108,13 +137,12 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
AppInfo appInfo = (AppInfo) event.getLocalState();
if (appInfo != null && appInfo.getHotseat() == 0) {
// 检查数量限制
if (mAppGridAdapter.getCount() >= 5) {
if (mAppGridAdapter.getItemCount() >= 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;
@@ -126,7 +154,6 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
}
@Override
protected void initData() {
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
@@ -137,7 +164,12 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
mViewModel.mHotseatAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mViewDataBinding.gvApp.setNumColumns(appInfos.size());
if (appInfos != null && !appInfos.isEmpty()) {
GridLayoutManager layoutManager = (GridLayoutManager) mViewDataBinding.gvApp.getLayoutManager();
if (layoutManager != null) {
layoutManager.setSpanCount(Math.max(1, appInfos.size()));
}
}
mAppGridAdapter.setAppInfos(appInfos);
}
});
@@ -159,8 +191,11 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
mViewModel.getHotseatApp();
}
mViewModel.getDbAppList();
mViewModel.getAllApp();
}
});
mViewModel.getAllApp();
}

View File

@@ -10,6 +10,7 @@ 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.manager.AppManager;
import com.ttstd.dialer.utils.Logger;
import java.util.List;
@@ -33,6 +34,41 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
mAppRepository = new AppRepository(context);
}
public MutableLiveData<List<AppInfo>> mAllAppData = new MutableLiveData<>();
public void getAllApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getAllApp();
}
}).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("getAllApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getAllApp", "onNext: " + appInfos);
mAllAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getAllApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getAllApp", "onComplete: ");
}
});
}
public MutableLiveData<List<AppInfo>> mHotseatAppData = new MutableLiveData<>();
public void getHotseatApp() {
@@ -105,10 +141,29 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
public MutableLiveData<Integer> mAppUpdateData = new MutableLiveData<>();
public void updateAppInfo(AppInfo appInfo){
public void updateAppInfo(AppInfo appInfo) {
Observable.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
if (appInfo.getOutside() == 0) {
appInfo.setHotseat(0);
appInfo.setPosition(mAppRepository.getMaxPosition() + 1);
appInfo.setOutsidePosition(0);
appInfo.setHotseatPosition(0);
} else if (appInfo.getOutside() == 1) {
if (appInfo.getHotseat() == 0) {
appInfo.setPosition(0);
appInfo.setOutsidePosition(mAppRepository.getMaxOutsidePosition() + 1);
appInfo.setHotseatPosition(0);
} else if (appInfo.getHotseat() == 1) {
appInfo.setPosition(0);
appInfo.setOutsidePosition(0);
if (appInfo.getHotseatPosition() <= 0) {
appInfo.setHotseatPosition(mAppRepository.getMaxHotseatPosition() + 1);
}
}
}
return mAppRepository.update(appInfo);
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
@@ -124,6 +179,7 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
public void onNext(@NonNull Integer row) {
Logger.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
AppManager.getInstance().reorderAppPositions();
}
@Override

View File

@@ -67,9 +67,6 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -34,9 +34,6 @@ public class ContactEditActivity extends BaseMvvmActivity<ContactEditViewModel,
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -58,9 +58,6 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -51,9 +51,6 @@ public class ContactTestActivity extends BaseMvvmActivity<ContactTestViewModel,
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.loader.app.LoaderManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.viewpager.widget.ViewPager;
import com.tencent.mmkv.MMKV;
@@ -88,9 +89,6 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@@ -151,23 +149,19 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
}
});
mApkPagerAdapter.setFragments(mFragments);
mViewDataBinding.viewPager.setAdapter(mApkPagerAdapter);
mViewDataBinding.viewPager.setOffscreenPageLimit(10);
mViewDataBinding.viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Logger.v(TAG, "onPageScrolled: position = " + position);
if (mCurrentIndex == -1 && position == 0) {
mViewDataBinding.viewPager.setCurrentItem(mDefaultIndex, true);
mCurrentIndex = mDefaultIndex;
} else {
mCurrentIndex = position;
}
}
@Override
public void onPageSelected(int position) {
Logger.v(TAG, "onPageSelected: position = " + position);
mCurrentIndex = position;
}
@Override
@@ -177,7 +171,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
});
mViewDataBinding.magicIndicator.setNavigator(mScaleCircleNavigator);
ViewPagerHelper.bind(mViewDataBinding.magicIndicator, mViewDataBinding.viewPager);
mViewDataBinding.viewPager.setCurrentItem(mDefaultIndex, true);
mViewDataBinding.viewPager.setCurrentItem(mDefaultIndex, false);
Logger.e(TAG, "initView: mCurrentIndex = " + mCurrentIndex);
Logger.e(TAG, "initView: mDefaultIndex = " + mDefaultIndex);
@@ -186,7 +180,19 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
Intent intent = new Intent(this, MainService.class);
startService(intent);
mAppGridAdapter = new AppGridAdapter(this, LoaderManager.getInstance(this));
mAppGridAdapter = new AppGridAdapter(this, LoaderManager.getInstance(this),false);
mViewDataBinding.gvApp.setLayoutManager(new GridLayoutManager(this, 5) {
@Override
public boolean canScrollVertically() {
return false;
}
@Override
public boolean canScrollHorizontally() {
return false;
}
});
mViewDataBinding.gvApp.setItemAnimator(null);
mViewDataBinding.gvApp.setAdapter(mAppGridAdapter);
}
@@ -225,7 +231,12 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
mViewModel.mHotseatAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mViewDataBinding.gvApp.setNumColumns(appInfos.size());
if (appInfos != null && !appInfos.isEmpty()) {
GridLayoutManager layoutManager = (GridLayoutManager) mViewDataBinding.gvApp.getLayoutManager();
if (layoutManager != null) {
layoutManager.setSpanCount(Math.max(1, appInfos.size()));
}
}
mAppGridAdapter.setAppInfos(appInfos);
}
});
@@ -355,7 +366,8 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
mApkPagerAdapter.setFragments(mFragments);
mViewDataBinding.viewPager.setPageCount(mFragments.size());
mViewDataBinding.viewPager.setCurrentItem(mCurrentIndex, true);
int currentIndex = mCurrentIndex == -1 ? mDefaultIndex : mCurrentIndex;
mViewDataBinding.viewPager.setCurrentItem(currentIndex, false);
}
public class BtnClick {

View File

@@ -28,9 +28,6 @@ public class SettingsCallActivity extends BaseMvvmActivity<SettingsCallViewModel
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -35,9 +35,6 @@ public class SettingsActivity extends BaseMvvmActivity<SettingsViewModel, Activi
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -51,9 +51,6 @@ public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewMod
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -22,9 +22,6 @@ public class TemplateActivity extends BaseMvvmActivity<TemplateViewModel, Activi
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -48,9 +48,6 @@ public class WeatherMainActivity extends BaseMvvmActivity<WeatherMainViewModel,
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -22,7 +22,7 @@ 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.MoveAppDialogFagment;
import com.ttstd.dialer.fragment.dialog.move.MoveAppDialogFagment;
import com.ttstd.dialer.utils.ApkUtils;
import com.ttstd.dialer.utils.Logger;
import com.ttstd.iconloader.IconCacheManager;

View File

@@ -12,7 +12,6 @@ import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -21,12 +20,14 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.RecyclerView;
import com.shehuan.niv.NiceImageView;
import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.R;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.fragment.dialog.shortcut.ShortcutDialogFagment;
import com.ttstd.dialer.utils.ApkUtils;
import com.ttstd.dialer.utils.Logger;
import com.ttstd.iconloader.IconCacheManager;
@@ -36,7 +37,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderCallbacks<Drawable> {
public class AppGridAdapter extends RecyclerView.Adapter<AppGridAdapter.ViewHolder> implements LoaderManager.LoaderCallbacks<Drawable> {
private static final String TAG = "AppGridAdapter";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
@@ -45,46 +46,49 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
private LoaderManager mLoaderManager;
private IconCacheManager mIconCacheManager = IconCacheManager.getInstance();
private List<AppInfo> mAppInfos;
private boolean mLongClickEnabled = true;
public interface ShortcutCallback {
void setAppInside(AppInfo appInfo);
}
private ShortcutCallback mShortcutCallback;
public void setShortcutCallback(ShortcutCallback shortcutCallback) {
mShortcutCallback = shortcutCallback;
}
public void setLongClickEnabled(boolean longClickEnabled) {
this.mLongClickEnabled = longClickEnabled;
notifyDataSetChanged();
}
public AppGridAdapter(FragmentActivity context, LoaderManager loaderManager) {
mContext = context;
mLoaderManager = loaderManager;
}
public AppGridAdapter(FragmentActivity context, LoaderManager loaderManager, boolean longClickEnabled) {
mContext = context;
mLoaderManager = loaderManager;
mLongClickEnabled = longClickEnabled;
}
public void setAppInfos(List<AppInfo> appInfos) {
mAppInfos = appInfos;
notifyDataSetChanged();
}
@NonNull
@NotNull
@Override
public int getCount() {
return mAppInfos == null ? 0 : mAppInfos.size();
public ViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_grid_app, parent, false);
return new ViewHolder(view);
}
@Override
public Object getItem(int position) {
return mAppInfos == null ? null : mAppInfos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_grid_app, parent, false);
holder = new ViewHolder();
holder.root = convertView.findViewById(R.id.root);
holder.iv_icon = convertView.findViewById(R.id.iv_icon);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
public void onBindViewHolder(@NonNull @NotNull ViewHolder holder, int position) {
AppInfo appInfo = mAppInfos.get(position);
Drawable drawable = mIconCacheManager.getIcon(appInfo.getComponentName().flattenToShortString());
if (drawable != null) {
@@ -101,9 +105,33 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
}
});
if (mLongClickEnabled) {
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.S_V2
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU
) {
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
shortcutDialogFagment.setTitile("温馨提示");
shortcutDialogFagment.setTips("是否把这个应用移到“更多应用”里?");
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
@Override
public void onPositiveClick() {
if (mShortcutCallback != null)
mShortcutCallback.setAppInside(appInfo);
shortcutDialogFagment.dismiss();
}
@Override
public void onNegativeClick() {
shortcutDialogFagment.dismiss();
}
});
shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
return false;
} else {
if (!v.isAttachedToWindow()) {
Logger.e(TAG, "View is not attached to window");
return false;
@@ -136,7 +164,11 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
return true;
}
}
});
} else {
holder.root.setOnLongClickListener(null);
}
holder.root.setOnDragListener(new View.OnDragListener() {
@Override
@@ -155,8 +187,11 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
return false;
}
});
}
return convertView;
@Override
public int getItemCount() {
return mAppInfos == null ? 0 : mAppInfos.size();
}
private static class LauncherDragShadowBuilder extends View.DragShadowBuilder {
@@ -178,7 +213,6 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
float scale = 1.2f;
canvas.save();
canvas.scale(scale, scale);
// 确保在绘制阴影时视图是不透明的
float originalAlpha = getView().getAlpha();
getView().setAlpha(1.0f);
getView().draw(canvas);
@@ -199,7 +233,7 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
@Override
public void onLoadFinished(@NonNull @NotNull Loader<Drawable> loader, Drawable data) {
int position = loader.getId();
notifyDataSetChanged();
notifyItemChanged(position);
}
@Override
@@ -207,9 +241,16 @@ public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderC
Logger.e(TAG, "onLoaderReset: ");
}
static class ViewHolder {
public static class ViewHolder extends RecyclerView.ViewHolder {
ConstraintLayout root;
NiceImageView iv_icon;
TextView tv_app_name;
public ViewHolder(@NonNull @NotNull View itemView) {
super(itemView);
root = itemView.findViewById(R.id.root);
iv_icon = itemView.findViewById(R.id.iv_icon);
tv_app_name = itemView.findViewById(R.id.tv_app_name);
}
}
}

View File

@@ -27,6 +27,8 @@ 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.dock.DockAppDialogFagment;
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;
@@ -44,11 +46,33 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
private FragmentActivity mContext;
private LoaderManager mLoaderManager;
private IconCacheManager mIconCacheManager = IconCacheManager.getInstance();
private boolean mLongClickEnabled = true;
public interface ShortcutCallback {
void setAppOutside(AppInfo appInfo);
void setAppHotSeat(AppInfo appInfo);
}
private ShortcutCallback mShortcutCallback;
public void setShortcutCallback(ShortcutCallback shortcutCallback) {
mShortcutCallback = shortcutCallback;
}
public void setLongClickEnabled(boolean longClickEnabled) {
this.mLongClickEnabled = longClickEnabled;
notifyDataSetChanged();
}
public MoreAppAdapter(LoaderManager loaderManager) {
mLoaderManager = loaderManager;
}
public MoreAppAdapter(LoaderManager loaderManager, boolean longClickEnabled) {
mLoaderManager = loaderManager;
mLongClickEnabled = longClickEnabled;
}
public void setSelectApps(List<AppInfo> selectApps) {
}
@@ -86,9 +110,42 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
ApkUtils.openApp(mContext, appInfo.getComponentName());
}
});
if (mLongClickEnabled) {
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.S_V2
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU
) {
DockAppDialogFagment dockAppDialogFagment = new DockAppDialogFagment(appInfo);
dockAppDialogFagment.setTitile("温馨提示");
dockAppDialogFagment.setTips("选择应用移动位置");
dockAppDialogFagment.setOnClickListener(new DockAppDialogFagment.OnClickListener() {
@Override
public void onDesktopClick() {
if (mShortcutCallback != null)
mShortcutCallback.setAppOutside(appInfo);
dockAppDialogFagment.dismiss();
}
@Override
public void onDockClick() {
if (mShortcutCallback != null)
mShortcutCallback.setAppHotSeat(appInfo);
dockAppDialogFagment.dismiss();
}
@Override
public void onNegativeClick() {
dockAppDialogFagment.dismiss();
}
});
dockAppDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
return false;
} else {
if (!v.isAttachedToWindow()) {
Logger.e(TAG, "View is not attached to window");
return false;
@@ -120,10 +177,13 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
v.setAlpha(0.5f);
}
});
return true;
}
}
});
} else {
holder.root.setOnLongClickListener(null);
}
holder.root.setOnDragListener(new View.OnDragListener() {
@Override

View File

@@ -1,35 +0,0 @@
package com.ttstd.dialer.base.mvp;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.ttstd.dialer.base.BaseTransparentActivity;
@Deprecated
public abstract class BaseMvpActivity extends BaseTransparentActivity {
public BaseMvpActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initView();
initData();
}
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -1,8 +0,0 @@
package com.ttstd.dialer.base.mvp;
@Deprecated
public interface BasePresenter<V extends BaseView> {
void attachView(V view);
void detachView();
}

View File

@@ -1,6 +0,0 @@
package com.ttstd.dialer.base.mvp;
@Deprecated
public interface BaseView {
}

View File

@@ -34,6 +34,12 @@ public abstract class BaseMvvmActivity<VM extends ViewModel, VDB extends ViewDat
Logger.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract);
if (!isAbstract) {//不是一个抽象类
mViewModel = new ViewModelProvider(this).get(vmClass);
if (mViewModel instanceof BaseViewModel) {
BaseViewModel<?, ?> baseVm = (BaseViewModel<?, ?>) mViewModel;
baseVm.setContext(this);
baseVm.setVDBinding(mViewDataBinding);
baseVm.setLifecycle(getLifecycleSubject());
}
}
initDataBinding();
initView();

View File

@@ -1,14 +1,11 @@
package com.ttstd.dialer.base.mvvm.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
@@ -19,35 +16,26 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.dialer.base.BaseDialogFragment;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.utils.Logger;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* 优化生命周期管理,去掉冗余的 Bundle 定义
* @param <VM> ViewModel 类型
* @param <VDB> ViewDataBinding 类型
*/
public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseDialogFragment {
protected String mTag = this.getClass().getSimpleName();
/**
* 是否顯示了
*/
protected boolean mIsVisible;
/**
* 是否準備好了-Created
*/
protected boolean mHasPrepare;
protected final String mTag = this.getClass().getSimpleName();
protected boolean mIsVisible;
protected boolean mHasPrepare;
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
protected Bundle bundle;
protected Bundle savedInstanceState;
// protected Context context;
/**
* 上下文
*/
private WeakReference<Context> ctx;
public Context getCtx() {
@@ -60,72 +48,75 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
ctx = new WeakReference<>(context);
}
/**
* onCreate、onResume里不能调用
*
* @return
*/
public boolean isAttached() {
boolean flag = getCtx() != null && isAdded();
Logger.e(" >> isAttached >>", "flag = " + flag);
return flag;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ViewModel 初始化移至 onCreate
try {
Class<VM> vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
if (mViewModel instanceof BaseViewModel) {
BaseViewModel<?, ?> baseVm = (BaseViewModel<?, ?>) mViewModel;
baseVm.setContext(getContext());
baseVm.setLifecycle(getLifecycleSubject());
}
} catch (Exception e) {
Logger.e(mTag, "ViewModel initialization failed: " + e.getMessage());
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
try {
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
} catch (Exception e) {
Logger.e(mTag, "Failed to create ViewModel: " + e.getMessage());
throw new RuntimeException("Failed to create ViewModel", e);
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(mViewDataBinding);
}
return mViewDataBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewDataBinding.setLifecycleOwner(getViewLifecycleOwner());
initDataBinding();
initView(bundle = getArguments());
initData(this.savedInstanceState = savedInstanceState);
initView(getArguments());
initData(savedInstanceState);
if (mIsVisible) {
mHasPrepare = true;
if (mIsVisible || getUserVisibleHint()) {
mIsVisible = true;
onEnter();
}
mHasPrepare = true;
}
@Override
public void onDestroyView() {
super.onDestroyView();
mHasPrepare = false;
// 及时解绑 Binding防止内存泄漏
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(null);
}
mViewDataBinding = null;
mViewModel = null;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mIsVisible == getUserVisibleHint())
return;
mIsVisible = getUserVisibleHint();
if (mIsVisible == isVisibleToUser) return;
mIsVisible = isVisibleToUser;
if (mIsVisible) {
if (!mHasPrepare)
return;
if (mHasPrepare) {
onEnter();
}
} else {
if (mHasPrepare) {
onExit();
}
}
}
@Override
public void fetchData() {
@@ -145,36 +136,13 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
super.onConfigurationChanged(newConfig);
}
protected void hideInputMethod(Activity activity) {
if (activity == null) return;
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
protected void hideInputMethod(Activity activity, EditText editText) {
if (activity == null || editText == null) return;
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
protected void showInputMethod(EditText editText) {
if (editText == null) return;
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
}
protected void onEnter() {
}
protected void onExit() {
}
public boolean isAttached() {
return getCtx() != null && isAdded();
}
}

View File

@@ -1,14 +1,11 @@
package com.ttstd.dialer.base.mvvm.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
@@ -19,42 +16,32 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.dialer.base.BaseFragment;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.utils.Logger;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* @author: lml
* @date: 2021/12/15
* 优化生命周期管理,去掉冗余的 Bundle 定义
* @param <VM> ViewModel 类型
* @param <VDB> ViewDataBinding 类型
*/
public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseFragment {
protected String mTag = this.getClass().getSimpleName();
protected final String mTag = this.getClass().getSimpleName();
/**
* 是否顯示了
* 是否可见(用于懒加载逻辑)
*/
protected boolean mIsVisible;
/**
* 是否準備好了-Created
* View 是否准备完毕
*/
protected boolean mHasPrepare;
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
//
// protected Toolbar toolbar;
// protected View statusBarView;
//
protected Bundle bundle;//来自getArguments()
protected Bundle savedInstanceState;
// protected Context context;
/**
* 上下文
*/
private WeakReference<Context> ctx;
public Context getCtx() {
@@ -64,104 +51,85 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// this.context = context;
ctx = new WeakReference<>(context);
}
/**
* onCreate、onResume里不能调用
*
* @return
*/
public boolean isAttached() {
boolean flag = getCtx() != null && isAdded();
Logger.e(" >> isAttached >>", "flag = " + flag);
return flag;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ViewModel 初始化
try {
Class<VM> vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
if (mViewModel instanceof BaseViewModel) {
BaseViewModel<?, ?> baseVm = (BaseViewModel<?, ?>) mViewModel;
baseVm.setContext(getContext());
baseVm.setLifecycle(getLifecycleSubject());
}
} catch (Exception e) {
Logger.e(mTag, "ViewModel initialization failed: " + e.getMessage());
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//ViewDataBinding
// ViewDataBinding 初始化
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
//
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(mViewDataBinding);
}
return mViewDataBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewDataBinding.setLifecycleOwner(getViewLifecycleOwner());
// if (initStatusBarToolBar()) {
// toolbar = getToolbar();
// }
//注册eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.register(this);
//
// fitsLayoutOverlap();
initDataBinding();
initView(bundle = getArguments());
//
initData(this.savedInstanceState = savedInstanceState);
//
if (mIsVisible) {
// 去掉重复的 bundle 和 savedInstanceState 定义,直接从参数/方法获取
initView(getArguments());
initData(savedInstanceState);
mHasPrepare = true;
// 检查初始可见性
if (mIsVisible || getUserVisibleHint()) {
mIsVisible = true;
onEnter();
}
mHasPrepare = true;
//
// LiveDataBus.get().with(ConstantUtils.DATA_BUS_LOADING_FRAGMENT, Boolean.class).observe(getActivity(), bool -> {
// L.e(" >> LiveDataBus >> DATA_BUS_LOADING_FRAGMENT: %s", bool);
// if(bool) {
// showLoading(R.string.str_please_wait);
// } else {
// hideLoading();
// }
// });
}
@Override
public void onDestroyView() {
super.onDestroyView();
mHasPrepare = false;
// 及时解绑 Binding防止内存泄漏
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(null);
}
mViewDataBinding = null;
//移除eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.unregister(this);
//
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mIsVisible == getUserVisibleHint())
return;
mIsVisible = getUserVisibleHint();
if (mIsVisible == isVisibleToUser) return;
mIsVisible = isVisibleToUser;
if (mIsVisible) {
if (!mHasPrepare)
return;
if (mHasPrepare) {
onEnter();
}
} else {
if (mHasPrepare) {
onExit();
}
}
}
@LayoutRes
protected abstract int getLayoutId();
// protected abstract Toolbar getToolbar();
// protected View getStatusView() {
// return null;
// }
protected abstract void initDataBinding();
protected abstract void initView(Bundle bundle);
@@ -171,105 +139,15 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// fitsLayoutOverlap();
}
// protected boolean isImmersionBarEnabled() {
// return true;
// }
// protected boolean initStatusBarToolBar() {
// return true;
// }
// private void fitsLayoutOverlap() {
// if (!isImmersionBarEnabled()) return;
// if (statusBarView != null) {
// ImmersionBar.setStatusBarView(getActivity(), statusBarView);
// }
// if (toolbar != null) {
// ImmersionBar.setTitleBar(getActivity(), toolbar);
// }
// }
protected void hideInputMethod(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
protected void hideInputMethod(Activity activity, EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
protected void showInputMethod(EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
}
// private CustomDialog mWaitDialog;
//
// public void showLoading(@StringRes int contentID) {
// showLoading(contentID, R.color.white);
// }
//
// public void showLoading(@StringRes int contentID, @ColorRes int color) {
// hideLoading();
// DialogX.init(getActivity());
// if (color == R.color.white) {
// mWaitDialog = DialogXUtil.getInstance().showLoading(getActivity(), getString(contentID), getResources().getColor(color));
// } else {
// mWaitDialog = DialogXUtil.getInstance().showLoading_black(getActivity(), getString(contentID), getResources().getColor(color));
// }
// }
//
// public void updateLoadingTip(@StringRes int messageID, int percent) {
// try {
// 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 + "%"));
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public boolean isShowLoading() {
// return mWaitDialog != null && mWaitDiaLogger.isShow();
// }
//
// public void hideLoading() {
// try {
// boolean isShow = isShowLoading();
// L.d(" >> hideLoading :: isShow: %s", isShow);
// if (isShow)
// mWaitDiaLogger.dismiss();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
/**
* 進入界面
*/
protected void onEnter() {
}
/**
* 離開界面
*/
protected void onExit() {
}
public boolean isAttached() {
return getCtx() != null && isAdded();
}
}

View File

@@ -37,6 +37,9 @@ public interface AppDao {
@Update
int update(AppInfo appInfo);
@Update
int update(List<AppInfo> appInfos);
@Delete
int delete(AppInfo appInfo);
@@ -49,10 +52,10 @@ public interface AppDao {
@Query("SELECT * FROM app_list ORDER BY position ASC")
List<AppInfo> getAllApp();
@Query("SELECT * FROM app_list WHERE outside = 1 ORDER BY position ASC")
@Query("SELECT * FROM app_list WHERE outside = 1 ORDER BY outside_position ASC")
List<AppInfo> getOutsideApp();
@Query("SELECT * FROM app_list WHERE outside = 1 AND hotseat = 0 ORDER BY position ASC")
@Query("SELECT * FROM app_list WHERE outside = 1 AND hotseat = 0 ORDER BY outside_position ASC")
List<AppInfo> getOutsideAppWithoutHotseat();
@Query("SELECT * FROM app_list WHERE outside = 0 ORDER BY position ASC")
@@ -69,4 +72,13 @@ public interface AppDao {
@Query("UPDATE app_list SET hotseat = :isHotseat WHERE id = :id")
int updateHotseatStatus(int id, int isHotseat);
@Query("SELECT MAX(position) FROM app_list WHERE outside = 0")
int getMaxPosition();
@Query("SELECT MAX(outside_position) FROM app_list WHERE outside = 1")
int getMaxOutsidePosition();
@Query("SELECT MAX(hotseat_position) FROM app_list WHERE hotseat = 1")
int getMaxHotseatPosition();
}

View File

@@ -8,7 +8,7 @@ import androidx.room.RoomDatabase;
import java.io.File;
@Database(entities = {AppInfo.class}, version = 1, exportSchema = false)
@Database(entities = {AppInfo.class}, version = 2, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract AppDao appDao();
@@ -21,7 +21,7 @@ public abstract class AppDatabase extends RoomDatabase {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, context.getExternalFilesDir("db") + File.separator + "app" + File.separator + "app_db")
// .allowMainThreadQueries() // 为了简化示例,允许主线程查询
.fallbackToDestructiveMigration()
.build();
}
}

View File

@@ -20,7 +20,11 @@ import com.ttstd.dialer.utils.ApkUtils;
import java.io.Serializable;
import java.util.Objects;
@Entity(tableName = "app_list", indices = {@Index(value = "component_name", unique = true)})
@Entity(tableName = "app_list", indices = {
@Index(value = "component_name", unique = true),
@Index(value = {"outside", "outside_position"}),
@Index(value = {"hotseat", "hotseat_position"})
})
@TypeConverters({ComponentNameConverter.class})
public class AppInfo implements Serializable , Parcelable {
private static final long serialVersionUID = 9113517079637096245L;
@@ -43,6 +47,8 @@ public class AppInfo implements Serializable , Parcelable {
private int hotseat;
@ColumnInfo(name = "hotseat_position")
private int hotseatPosition;
@ColumnInfo(name = "outside_position")
private int outsidePosition;
public AppInfo() {
@@ -57,6 +63,7 @@ public class AppInfo implements Serializable , Parcelable {
this.outside = 0;
this.hotseat = 0;
this.hotseatPosition = 0;
this.outsidePosition = 0;
}
public AppInfo(Context context, ComponentName componentName, int pos) {
@@ -68,6 +75,7 @@ public class AppInfo implements Serializable , Parcelable {
this.outside = 0;
this.hotseat = 0;
this.hotseatPosition = 0;
this.outsidePosition = 0;
}
protected AppInfo(Parcel in) {
@@ -78,7 +86,9 @@ public class AppInfo implements Serializable , Parcelable {
mClassName = in.readString();
mPosition = in.readInt();
outside = in.readInt();
hotseat = in.readInt();
hotseatPosition = in.readInt();
outsidePosition = in.readInt();
}
public static final Creator<AppInfo> CREATOR = new Creator<AppInfo>() {
@@ -165,6 +175,14 @@ public class AppInfo implements Serializable , Parcelable {
this.hotseatPosition = hotseatPosition;
}
public int getOutsidePosition() {
return outsidePosition;
}
public void setOutsidePosition(int outsidePosition) {
this.outsidePosition = outsidePosition;
}
@Override
public boolean equals(@Nullable @org.jetbrains.annotations.Nullable Object obj) {
if (obj instanceof AppInfo) {
@@ -204,5 +222,6 @@ public class AppInfo implements Serializable , Parcelable {
dest.writeInt(outside);
dest.writeInt(hotseat);
dest.writeInt(hotseatPosition);
dest.writeInt(outsidePosition);
}
}

View File

@@ -81,6 +81,10 @@ public class AppRepository {
return mAppDao.update(appInfo);
}
public int update(List<AppInfo> appInfos) {
return mAppDao.update(appInfos);
}
// 删除APP
public int delete(AppInfo appInfo) {
return mAppDao.delete(appInfo);
@@ -95,4 +99,16 @@ public class AppRepository {
public int deleteAll() {
return mAppDao.deleteAll();
}
public int getMaxPosition() {
return mAppDao.getMaxPosition();
}
public int getMaxOutsidePosition() {
return mAppDao.getMaxOutsidePosition();
}
public int getMaxHotseatPosition() {
return mAppDao.getMaxHotseatPosition();
}
}

View File

@@ -66,9 +66,6 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -41,6 +41,16 @@ public class AppViewModel extends BaseViewModel<FragmentAppBinding, FragmentEven
Observable.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 根据状态自动设置位置为最大值
if (appInfo.getOutside() == 0) {
appInfo.setPosition(mAppRepository.getMaxPosition() + 1);
} else if (appInfo.getOutside() == 1) {
appInfo.setOutsidePosition(mAppRepository.getMaxOutsidePosition() + 1);
}
if (appInfo.getHotseat() == 1) {
appInfo.setOutside(1);
appInfo.setHotseatPosition(mAppRepository.getMaxHotseatPosition() + 1);
}
return mAppRepository.update(appInfo);
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))

View File

@@ -37,9 +37,6 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -35,7 +35,6 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, DialogFr
private static final int REQUEST_CODE_CALL = 7897;
private Activity mContext;
private ContactInfo mContactInfo;
public CallFragment() {
@@ -52,10 +51,6 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, DialogFr
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@@ -133,19 +128,19 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, DialogFr
}
private void callWeixin(int type) {
if (AccessibilityServiceHelper.isAccessibilityServiceEnabled(mContext, DialerAccessibilityService.class)) {
if (ApkUtils.isInstalled(mContext, "com.tencent.mm")) {
Intent intent = new Intent(mContext, DialerAccessibilityService.class);
if (AccessibilityServiceHelper.isAccessibilityServiceEnabled(requireContext(), DialerAccessibilityService.class)) {
if (ApkUtils.isInstalled(requireContext(), "com.tencent.mm")) {
Intent intent = new Intent(requireContext(), DialerAccessibilityService.class);
intent.putExtra("ContactInfo", mContactInfo);
intent.putExtra("callType", type);
mContext.startService(intent);
requireContext().startService(intent);
} else {
Toaster.show("微信未安装,请安装后使用");
ApkUtils.openAppStore(mContext, "com.tencent.mm");
ApkUtils.openAppStore(requireContext(), "com.tencent.mm");
}
} else {
Toast.makeText(mContext, "请现在打开无障碍服务中 - 拨号助手无障碍服务", Toast.LENGTH_LONG).show();
AccessibilityServiceHelper.jumpToAccessibilitySettings(mContext);
Toast.makeText(requireContext(), "请现在打开无障碍服务中 - 拨号助手无障碍服务", Toast.LENGTH_LONG).show();
AccessibilityServiceHelper.jumpToAccessibilitySettings(requireContext());
}
}
@@ -162,8 +157,8 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, DialogFr
}
public void callPhone(View view) {
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL);
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL);
} else {
call();
}

View File

@@ -25,7 +25,7 @@ import com.ttstd.dialer.utils.Logger;
public class ContactDeleteDialogFragment extends BaseMvvmDialogFragment<ContactDeleteViewModel, DialogFragmentDeleteContactBinding> {
private static final String TAG = "EditDialogFragment";
private Activity mContext;
private ContactInfo mContactInfo;
public interface ContactOperationListener {
@@ -51,10 +51,6 @@ public class ContactDeleteDialogFragment extends BaseMvvmDialogFragment<ContactD
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@@ -62,7 +58,7 @@ public class ContactDeleteDialogFragment extends BaseMvvmDialogFragment<ContactD
protected void initView(Bundle bundle) {
if (mContactInfo != null) {
mViewDataBinding.setContactInfo(mContactInfo);
GlideUtils.loadImageSafe(mContext, mContactInfo.getAvatar(), mViewDataBinding.contactAvatar, AvatarCacheUtil.getAvatar(mContext, mContactInfo.getName()));
GlideUtils.loadImageSafe(requireContext(), mContactInfo.getAvatar(), mViewDataBinding.contactAvatar, AvatarCacheUtil.getAvatar(requireContext(), mContactInfo.getName()));
} else {
Toaster.show("无联系人信息");
dismiss();

View File

@@ -25,7 +25,7 @@ import com.ttstd.dialer.utils.Logger;
public class EditDialogFragment extends BaseMvvmDialogFragment<EditViewModel, DialogFragmentEditContactBinding> {
private static final String TAG = "EditDialogFragment";
private Activity mContext;
private ContactInfo mContactInfo;
public interface ContactOperationListener {
@@ -51,10 +51,6 @@ public class EditDialogFragment extends BaseMvvmDialogFragment<EditViewModel, Di
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@@ -62,7 +58,7 @@ public class EditDialogFragment extends BaseMvvmDialogFragment<EditViewModel, Di
protected void initView(Bundle bundle) {
if (mContactInfo != null) {
mViewDataBinding.setContactInfo(mContactInfo);
GlideUtils.loadImageSafe(mContext, mContactInfo.getAvatar(), mViewDataBinding.contactAvatar, AvatarCacheUtil.getAvatar(mContext, mContactInfo.getName()));
GlideUtils.loadImageSafe(requireContext(), mContactInfo.getAvatar(), mViewDataBinding.contactAvatar, AvatarCacheUtil.getAvatar(requireContext(), mContactInfo.getName()));
} else {
Toaster.show("无联系人信息");
dismiss();

View File

@@ -0,0 +1,192 @@
package com.ttstd.dialer.fragment.dialog.dock;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.ttstd.dialer.R;
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmDialogFragment;
import com.ttstd.dialer.databinding.FragmentDialogDockAppBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.utils.Logger;
public class DockAppDialogFagment extends BaseMvvmDialogFragment<DockAppViewModel, FragmentDialogDockAppBinding> {
private static final String TAG = "ShortcutDialogFagment";
private PackageManager mPackageManager;
private AppInfo mAppInfo;
private String mTitile;
private String mTips;
private String mNegativeText;
private String mPositiveText;
public DockAppDialogFagment() {
}
public DockAppDialogFagment(AppInfo appInfo) {
mAppInfo = appInfo;
}
public void setTitile(String titile) {
mTitile = titile;
}
public void setTips(String tips) {
mTips = tips;
}
public void setNegativeText(String negativeText) {
mNegativeText = negativeText;
}
public void setPositiveText(String positiveText) {
mPositiveText = positiveText;
}
public interface OnClickListener {
void onDesktopClick();
void onDockClick();
void onNegativeClick();
}
private OnClickListener mOnClickListener;
public void setOnClickListener(OnClickListener onClickListener) {
mOnClickListener = onClickListener;
}
@Override
protected int getLayoutId() {
return R.layout.fragment_dialog_dock_app;
}
@Override
protected void initDataBinding() {
//requireContext():这是官方推荐的做法。
// 由于 initDataBinding 和 initData 都是在 Fragment 的视图生命周期内执行的
// (此时 Fragment 肯定已经 onAttach使用 requireContext() 是绝对安全的,且代码最简洁。
//移除冗余检查:既然 mPackageManager 在初始化时通过 requireContext() 赋值,
// 它在整个视图生命周期内都保证非空,因此 if (mPackageManager != null) 的判断可以完全去掉。
mPackageManager = requireContext().getPackageManager();
mViewDataBinding.setClick(new BtnClick());
}
@Override
protected void initView(Bundle bundle) {
}
@Override
protected void initData(Bundle savedInstanceState) {
if (TextUtils.isEmpty(mTitile)) {
mViewDataBinding.tvTitle.setText("提示");
} else {
mViewDataBinding.tvTitle.setText(mTitile);
}
if (TextUtils.isEmpty(mTips)) {
mViewDataBinding.tvMessage.setText("");
mViewDataBinding.tvMessage.setVisibility(View.GONE);
} else {
mViewDataBinding.tvMessage.setText(mTips);
mViewDataBinding.tvMessage.setVisibility(View.VISIBLE);
}
if (TextUtils.isEmpty(mNegativeText)){
mViewDataBinding.tvNegative.setText("取消");
}else {
mViewDataBinding.tvNegative.setText(mNegativeText);
}
if (TextUtils.isEmpty(mPositiveText)){
mViewDataBinding.tvPositive.setText("确定");
}else {
mViewDataBinding.tvPositive.setText(mPositiveText);
}
if (mAppInfo != null) {
try {
ActivityInfo info = mPackageManager.getActivityInfo(mAppInfo.getComponentName(), 0);
mViewDataBinding.tvAppName.setText(info.loadLabel(mPackageManager));
Drawable rawIcon = info.loadIcon(mPackageManager);
mViewDataBinding.ivIcon.setImageDrawable(rawIcon);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
@Override
public void onStart() {
super.onStart();
if (getDialog() != null) {
Window window = getDialog().getWindow();
if (window == null) return;
WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.CENTER;
window.setAttributes(params);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getDialog().setCancelable(true);
getDialog().setCanceledOnTouchOutside(true);
}
}
@Override
public void show(FragmentManager manager, String tag) {
DialogFragment fragment = (DialogFragment) manager.findFragmentByTag(tag);
if (fragment != null && fragment.isAdded()
&& fragment.getDialog() != null && fragment.getDialog().isShowing()) {
return;
}
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (Exception e) {
Logger.e(TAG, "show: " + e.getMessage());
}
}
@Override
public void fetchData() {
}
public class BtnClick {
public void onPositive(View view) {
if (mOnClickListener != null) {
mOnClickListener.onDesktopClick();
}
}
public void onHotSeat(View view) {
if (mOnClickListener != null) {
mOnClickListener.onDockClick();
}
}
public void onNegative(View view) {
if (mOnClickListener != null) {
mOnClickListener.onNegativeClick();
}
}
}
}

View File

@@ -0,0 +1,10 @@
package com.ttstd.dialer.fragment.dialog.dock;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentDialogDockAppBinding;
import com.ttstd.dialer.databinding.FragmentDialogShortcutBinding;
public class DockAppViewModel extends BaseViewModel <FragmentDialogDockAppBinding, FragmentEvent>{
}

View File

@@ -1,4 +1,4 @@
package com.ttstd.dialer.fragment.dialog.shortcut;
package com.ttstd.dialer.fragment.dialog.move;
import android.app.Activity;
import android.content.pm.ActivityInfo;
@@ -23,10 +23,9 @@ import com.ttstd.dialer.databinding.FragmentDialogMoveAppBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.utils.Logger;
public class MoveAppDialogFagment extends BaseMvvmDialogFragment<ShortcutViewModel, FragmentDialogMoveAppBinding> {
private static final String TAG = "ShortcutDialogFagment";
public class MoveAppDialogFagment extends BaseMvvmDialogFragment<MoveAppViewModel, FragmentDialogMoveAppBinding> {
private static final String TAG = "MoveAppDialogFagment";
private Activity mContext;
private PackageManager mPackageManager;
private AppInfo mAppInfo;
@@ -77,11 +76,7 @@ public class MoveAppDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMod
@Override
protected void initDataBinding() {
mContext = getActivity();
mPackageManager = mContext.getPackageManager();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mPackageManager = requireContext().getPackageManager();
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -0,0 +1,9 @@
package com.ttstd.dialer.fragment.dialog.move;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentDialogMoveAppBinding;
public class MoveAppViewModel extends BaseViewModel <FragmentDialogMoveAppBinding, FragmentEvent>{
}

View File

@@ -1,6 +1,6 @@
package com.ttstd.dialer.fragment.dialog.shortcut;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
@@ -26,7 +26,6 @@ import com.ttstd.dialer.utils.Logger;
public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewModel, FragmentDialogShortcutBinding> {
private static final String TAG = "ShortcutDialogFagment";
private Activity mContext;
private PackageManager mPackageManager;
private AppInfo mAppInfo;
@@ -77,11 +76,12 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
@Override
protected void initDataBinding() {
mContext = getActivity();
mPackageManager = mContext.getPackageManager();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
//requireContext():这是官方推荐的做法。
// 由于 initDataBinding 和 initData 都是在 Fragment 的视图生命周期内执行的
// (此时 Fragment 肯定已经 onAttach使用 requireContext() 是绝对安全的,且代码最简洁。
//移除冗余检查:既然 mPackageManager 在初始化时通过 requireContext() 赋值,
// 它在整个视图生命周期内都保证非空,因此 if (mPackageManager != null) 的判断可以完全去掉。
mPackageManager = requireContext().getPackageManager();
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -102,9 +102,6 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -28,9 +28,6 @@ public class SettingsFragment extends BaseMvvmFragment<SettingsViewModel, Fragme
@Override
protected void initDataBinding() {
mContext = getActivity();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}

View File

@@ -66,6 +66,25 @@ public class AppManager {
AppRepository mAppRepository;
private Set<ProgressCallback> mProgressCallbacks = new CopyOnWriteArraySet<>();
private final Set<String> mExcludedPackages = new CopyOnWriteArraySet<>();
public void addExcludedPackage(String packageName) {
if (packageName != null) {
mExcludedPackages.add(packageName);
refreshAllApps();
}
}
public void removeExcludedPackage(String packageName) {
if (packageName != null) {
mExcludedPackages.remove(packageName);
refreshAllApps();
}
}
private boolean isExcluded(String packageName) {
return mContext.getPackageName().equals(packageName) || mExcludedPackages.contains(packageName);
}
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
@@ -180,6 +199,7 @@ public class AppManager {
notifyProgress(STEP_UPDATE_POSITION, 3, TOTAL_STEPS_NORMAL);
updatePositionSync();
}
reorderAppPositionsSync();
notifyCompleted(true, "应用列表处理完成");
} catch (Exception e) {
Logger.e(TAG, "异步处理失败", e);
@@ -203,7 +223,7 @@ public class AppManager {
List<AppInfo> allFirstApps = new ArrayList<>();
for (ResolveInfo ri : allLauncherApps) {
if (ri != null) {
if (ri != null && !isExcluded(ri.activityInfo.packageName)) {
AppInfo appInfo = resolveInfoToDesktopApp(ri);
if (appInfo != null) {
allFirstApps.add(appInfo);
@@ -223,10 +243,17 @@ public class AppManager {
}
});
int outsideCounter = 0;
for (int i = 0; i < allFirstApps.size(); i++) {
AppInfo appInfo = allFirstApps.get(i);
appInfo.setPosition(i);
appInfo.setOutside(DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName()) ? 1 : 0);
if (DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName())) {
appInfo.setOutside(1);
appInfo.setOutsidePosition(outsideCounter++);
} else {
appInfo.setOutside(0);
appInfo.setOutsidePosition(0);
}
appInfo.setHotseat(0);
appInfo.setHotseatPosition(0);
}
@@ -271,7 +298,7 @@ public class AppManager {
}
for (AppInfo app : appInfos) {
if (app != null && !ApkUtils.isInstalled(mContext, app.getPackageName())) {
if (app != null && (!ApkUtils.isInstalled(mContext, app.getPackageName()) || isExcluded(app.getPackageName()))) {
try {
int id = mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
if (id > 0) {
@@ -294,7 +321,7 @@ public class AppManager {
List<AppInfo> newApps = new ArrayList<>();
for (ResolveInfo ri : resolveInfos) {
if (ri != null) {
if (ri != null && !isExcluded(ri.activityInfo.packageName)) {
AppInfo app = resolveInfoToDesktopApp(ri);
if (app != null && ApkUtils.isInstalled(mContext, app.getPackageName()) && !isAppExists(app)) {
newApps.add(app);
@@ -338,9 +365,15 @@ public class AppManager {
}
});
int outsideCounter = 0;
for (int i = 0; i < appInfos.size(); i++) {
AppInfo app = appInfos.get(i);
app.setPosition(i);
if (app.getOutside() == 1) {
app.setOutsidePosition(outsideCounter++);
} else {
app.setOutsidePosition(0);
}
try {
mAppRepository.update(app);
} catch (Exception e) {
@@ -393,7 +426,7 @@ public class AppManager {
}
public void updateApp(String packageName) {
boolean isInstalled = ApkUtils.isInstalled(mContext, packageName);
boolean isInstalled = ApkUtils.isInstalled(mContext, packageName) && !isExcluded(packageName);
if (isInstalled) {
Logger.i(TAG, "应用已安装: " + packageName + ", 开始更新数据库");
@@ -410,6 +443,7 @@ public class AppManager {
if (existingId <= 0) {
app.setOutside(1);
app.setPosition(mAppRepository.getTotalCount());
app.setOutsidePosition(mAppRepository.getOutsideApp().size());
mAppRepository.insert(app);
Logger.i(TAG, "新增应用信息: " + app.getPackageName());
}
@@ -481,6 +515,92 @@ public class AppManager {
}
}
/**
* 获取数据库所有应用列表并根据条件重新设置排序位置从1开始
* 1. outside == 0 用 position 排序,重设 position 从1开始
* 2. outside == 1 用 outside_position 排序,重设 outside_position 从1开始
* 3. hotseat == 1 用 hotseat_position 排序,重设 hotseat_position 从1开始
*/
public void reorderAppPositions() {
ASYNC_EXECUTOR.execute(this::reorderAppPositionsSync);
}
public void reorderAppPositionsSync() {
try {
List<AppInfo> allApps = mAppRepository.getAllApp();
if (allApps == null || allApps.isEmpty()) return;
// 1. 处理 inside 应用 (outside == 0)
List<AppInfo> insideApps = new ArrayList<>();
for (AppInfo app : allApps) {
if (app.getOutside() == 0) {
insideApps.add(app);
}
}
Collections.sort(insideApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Integer.compare(o1.getPosition(), o2.getPosition());
}
});
for (int i = 0; i < insideApps.size(); i++) {
AppInfo app = insideApps.get(i);
app.setPosition(i + 1);
app.setOutsidePosition(0);
app.setHotseatPosition(0);
}
// 2. 处理 outside 应用 (outside == 1)
List<AppInfo> outsideApps = new ArrayList<>();
for (AppInfo app : allApps) {
if (app.getOutside() == 1 && app.getHotseat() == 0) {
outsideApps.add(app);
}
}
Collections.sort(outsideApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Integer.compare(o1.getOutsidePosition(), o2.getOutsidePosition());
}
});
for (int i = 0; i < outsideApps.size(); i++) {
AppInfo app = outsideApps.get(i);
app.setPosition(0);
app.setOutsidePosition(i + 1);
app.setHotseatPosition(0);
}
// 3. 处理 hotseat 应用 (hotseat == 1)
List<AppInfo> hotseatApps = new ArrayList<>();
for (AppInfo app : allApps) {
if (app.getOutside() == 1 && app.getHotseat() == 1) {
hotseatApps.add(app);
}
}
Collections.sort(hotseatApps, new Comparator<AppInfo>() {
@Override
public int compare(AppInfo o1, AppInfo o2) {
return Integer.compare(o1.getHotseatPosition(), o2.getHotseatPosition());
}
});
for (int i = 0; i < hotseatApps.size(); i++) {
AppInfo app = hotseatApps.get(i);
app.setPosition(0);
app.setOutsidePosition(0);
app.setHotseatPosition(i + 1);
}
// 更新数据库
mAppRepository.update(allApps);
Logger.i(TAG, "重置所有应用排序位置完成");
// 通知 UI 更新
LiveDataBus.get().send(LiveDataAction.ACTION_UPDATE_APPS, TAG);
} catch (Exception e) {
Logger.e(TAG, "重置应用排序位置失败", e);
}
}
public String getDefaultAppList() {
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
@@ -615,6 +735,7 @@ public class AppManager {
}, ASYNC_EXECUTOR).thenRun(new Runnable() {
@Override
public void run() {
manager.reorderAppPositionsSync();
manager.notifyCompleted(true, "应用列表处理完成");
}
}).exceptionally(new java.util.function.Function<Throwable, Void>() {
@@ -643,16 +764,24 @@ public class AppManager {
List<AppInfo> allFirstApps = allLauncherApps.stream()
.filter(Objects::nonNull)
.filter(ri -> !manager.isExcluded(ri.activityInfo.packageName))
.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[] outsideCounter = {0};
IntStream.range(0, allFirstApps.size()).forEach(index -> {
AppInfo appInfo = allFirstApps.get(index);
appInfo.setPosition(index);
appInfo.setOutside(DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName()) ? 1 : 0);
if (DEFAULT_APP_PACKAGES.contains(appInfo.getPackageName())) {
appInfo.setOutside(1);
appInfo.setOutsidePosition(outsideCounter[0]++);
} else {
appInfo.setOutside(0);
appInfo.setOutsidePosition(0);
}
appInfo.setHotseat(0);
appInfo.setHotseatPosition(0);
});
@@ -701,7 +830,7 @@ public class AppManager {
List<Integer> ids = appInfos.stream()
.filter(Objects::nonNull)
.filter(app -> !ApkUtils.isInstalled(manager.mContext, app.getPackageName()))
.filter(app -> !ApkUtils.isInstalled(manager.mContext, app.getPackageName()) || manager.isExcluded(app.getPackageName()))
.map(app -> {
try {
return manager.mAppRepository.getIdByPackageAndClass(app.getPackageName(), app.getClassName());
@@ -716,7 +845,8 @@ public class AppManager {
ids.forEach(id -> {
try {
manager.mAppRepository.deleteById(id);
} catch (Exception e) {}
} catch (Exception e) {
}
});
}
}
@@ -734,10 +864,11 @@ public class AppManager {
List<AppInfo> newApps = resolveInfos.stream()
.filter(Objects::nonNull)
.filter(ri -> !manager.isExcluded(ri.activityInfo.packageName))
.map(manager::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.filter(app -> ApkUtils.isInstalled(manager.mContext, app.getPackageName()))
.filter(manager::isAppExists)
.filter(app -> !manager.isAppExists(app))
.sorted(manager.getAppComparator())
.collect(Collectors.toList());
@@ -749,7 +880,8 @@ public class AppManager {
manager.mAppRepository.insert(app);
}
}
} catch (Exception e) {}
} catch (Exception e) {
}
}
}, ASYNC_EXECUTOR);
}
@@ -768,12 +900,19 @@ public class AppManager {
.sorted(Comparator.comparingInt(AppInfo::getPosition))
.collect(Collectors.toList());
final int[] outsideCounter = {0};
for (int i = 0; i < sortedApps.size(); i++) {
AppInfo app = sortedApps.get(i);
app.setPosition(i);
if (app.getOutside() == 1) {
app.setOutsidePosition(outsideCounter[0]++);
} else {
app.setOutsidePosition(0);
}
manager.mAppRepository.update(app);
}
} catch (Exception e) {}
} catch (Exception e) {
}
}
}, ASYNC_EXECUTOR);
}

View File

@@ -48,6 +48,14 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/cl_hotseat"
app:layout_constraintTop_toBottomOf="@+id/bar" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_hotseat"
android:layout_width="match_parent"
@@ -56,23 +64,17 @@
android:layout_marginHorizontal="@dimen/dp_8"
android:background="@drawable/main_hotseat_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bar">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/recyclerView">
<GridView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gv_app"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="auto_fit"
android:orientation="horizontal" />
tools:listitem="@layout/item_grid_app" />
</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/cl_hotseat" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -44,12 +44,11 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<GridView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gv_app"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="auto_fit"
android:orientation="horizontal" />
tools:listitem="@layout/item_grid_app" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,264 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".fragment.dialog.dock.DockAppDialogFagment">
<data>
<variable
name="click"
type="com.ttstd.dialer.fragment.dialog.dock.DockAppDialogFagment.BtnClick" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="@dimen/dp_300"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/default_dialog_background"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp_24"
android:gravity="center_vertical"
android:textColor="@color/black"
android:textSize="@dimen/sp_18"
android:textStyle="bold"
android:visibility="visible"
tools:text="消息" />
<TextView
android:id="@+id/tv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp_16"
android:gravity="center_vertical"
android:textColor="@color/black"
android:textSize="@dimen/sp_16"
android:visibility="visible"
tools:text="消息" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@+id/linearLayout2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
<com.shehuan.niv.NiceImageView
android:id="@+id/iv_icon"
android:layout_width="@dimen/dp_64"
android:layout_height="@dimen/dp_64"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/iv_app_icon"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
android:layout_marginStart="@dimen/dp_52"
android:layout_marginTop="@dimen/dp_52"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="@+id/iv_icon"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_app_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_20"
android:gravity="center"
android:lineSpacingExtra="@dimen/dp_3"
android:maxLines="1"
android:textColor="#676767"
android:textSize="@dimen/sp_18"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_icon"
tools:text="app" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_24"
android:layout_marginTop="@dimen/dp_20"
android:layout_marginEnd="@dimen/dp_24"
android:layout_marginBottom="@dimen/dp_20"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_44"
android:background="@drawable/tv_edit_background"
android:onClick="@{click::onPositive}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:src="@drawable/ic_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:maxLines="1"
android:singleLine="true"
android:text="放到桌面"
android:textColor="#1E88E5"
android:textSize="@dimen/sp_15"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_44"
android:layout_marginTop="@dimen/dp_12"
android:background="@drawable/tv_edit_background"
android:onClick="@{click::onHotSeat}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView2"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:src="@drawable/ic_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_positive2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:maxLines="1"
android:singleLine="true"
android:text="放到快捷栏"
android:textColor="#1E88E5"
android:textSize="@dimen/sp_15"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_44"
android:layout_marginTop="@dimen/dp_12"
android:background="@drawable/tv_delete_background"
android:onClick="@{click::onNegative}">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_delete"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:src="@drawable/ic_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:maxLines="1"
android:singleLine="true"
android:text="取消"
android:textColor="#D32F2F"
android:textSize="@dimen/sp_15"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/iv_delete"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -2,13 +2,13 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".fragment.dialog.shortcut.MoveAppDialogFagment">
tools:context=".fragment.dialog.move.MoveAppDialogFagment">
<data>
<variable
name="click"
type="com.ttstd.dialer.fragment.dialog.shortcut.MoveAppDialogFagment.BtnClick" />
type="com.ttstd.dialer.fragment.dialog.move.MoveAppDialogFagment.BtnClick" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#2196F3</color>
<color name="colorPrimaryDark">#3F51B5</color>
<color name="colorAccent">#03A9F4</color>
<color name="colorPrimary">#D32F2F</color>
<color name="colorPrimaryDark">#B71C1C</color>
<color name="colorAccent">#FFC107</color>
<color name="indicator_color_normal">#99bd2f25</color>
<color name="indicator_color_selected">#BD2F25</color>
<color name="default_background_color">#ECECEC</color>
<color name="default_background_color">#F5E6CC</color>
<color name="settings_text_color_item">#000000</color>
<color name="setting_enable_color">#000000</color>

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="home_item_text_size">20sp</dimen>
<dimen name="home_item_text_size">24sp</dimen>
<dimen name="default_radius">8dp</dimen>
<dimen name="default_return_icon_size">32dp</dimen>
<dimen name="settings_item_height">80dp</dimen>
<dimen name="settings_text_size">18sp</dimen>
<dimen name="settings_text_explanation_size">15sp</dimen>
<dimen name="settings_item_height">90dp</dimen>
<dimen name="settings_text_size">22sp</dimen>
<dimen name="settings_text_explanation_size">18sp</dimen>
<dimen name="top_height">2dp</dimen>
<dimen name="default_card_corner_radius">16dp</dimen>

View File

@@ -13,14 +13,15 @@
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowBackground">@color/default_background_color</item>
<item name="backgroundColor">@color/default_background_color</item>
</style>
<style name="MaterialTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowBackground">@color/default_background_color</item>
<item name="backgroundColor">@color/default_background_color</item>
</style>