增加app在内外显示,支持长按显示隐藏,优化命名,主页app显示还有问题

This commit is contained in:
2025-11-03 08:56:21 +08:00
parent 552ec8dbe9
commit 8dc284c2f3
38 changed files with 1265 additions and 290 deletions

View File

@@ -8,9 +8,9 @@ import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.R;
import com.ttstd.dialer.adapter.MoreAppAdapter;
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
import com.ttstd.dialer.db.app.DesktopSortApp;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.databinding.ActivityAppListBinding;
import com.ttstd.dialer.db.app.AppInfo;
import java.util.List;
@@ -48,6 +48,13 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
@Override
protected void initView() {
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);
@@ -55,15 +62,24 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
@Override
protected void initData() {
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<DesktopSortApp>>() {
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<DesktopSortApp> desktopSortApps) {
mMoreAppAdapter.setDesktopSortApps(desktopSortApps);
public void onChanged(List<AppInfo> appInfos) {
mMoreAppAdapter.setAppInfos(appInfos);
}
});
// mViewModel.getLauncherAppList();
mViewModel.getDbAppList();
mViewModel.mAppUpdateData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
if (integer > 0) {
}
mViewModel.getDbAppList();
}
});
}

View File

@@ -11,8 +11,8 @@ import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
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.db.app.DesktopSortApp;
import com.ttstd.dialer.utils.ApkUtils;
import java.text.Collator;
@@ -41,27 +41,27 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
mAppRepository = new AppRepository(context);
}
public MutableLiveData<List<DesktopSortApp>> mDesktopSortAppData = new MutableLiveData<>();
public MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
public void getDbAppList() {
Observable.fromCallable(new Callable<List<DesktopSortApp>>() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<DesktopSortApp> call() throws Exception {
return mAppRepository.getAllApp();
public List<AppInfo> call() throws Exception {
return mAppRepository.getInsideApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<DesktopSortApp>>() {
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getDbAppList", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<DesktopSortApp> desktopSortApps) {
Log.e("getDbAppList", "onNext: " + desktopSortApps);
mDesktopSortAppData.setValue(desktopSortApps);
public void onNext(@NonNull List<AppInfo> appInfos) {
Log.e("getDbAppList", "onNext: " + appInfos);
mDesktopSortAppData.setValue(appInfos);
}
@Override
@@ -74,80 +74,41 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
Log.e("getDbAppList", "onComplete: ");
}
});
}
public void getLauncherAppList() {
Observable.fromCallable(new Callable<List<ResolveInfo>>() {
public MutableLiveData<Integer> mAppUpdateData = new MutableLiveData<>();
public void updateAppInfo(AppInfo appInfo){
Observable.fromCallable(new Callable<Integer>() {
@Override
public List<ResolveInfo> call() throws Exception {
return ApkUtils.getAllLauncherResolveInfo(getSafeContext());
public Integer call() throws Exception {
return mAppRepository.update(appInfo);
}
})
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.map(new Function<List<ResolveInfo>, List<ComponentName>>() {
@Override
public List<ComponentName> apply(List<ResolveInfo> resolveInfos) throws Throwable {
List<ComponentName> componentNames = resolveInfos.stream().map(new java.util.function.Function<ResolveInfo, ComponentName>() {
@Override
public ComponentName apply(ResolveInfo resolveInfo) {
String packageName = resolveInfo.activityInfo.packageName;
String className = resolveInfo.activityInfo.name;
ComponentName componentName = new ComponentName(packageName, className);
return componentName;
}
}).collect(Collectors.toList());
return componentNames;
}
})
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.map(new Function<List<ComponentName>, List<DesktopSortApp>>() {
@Override
public List<DesktopSortApp> apply(List<ComponentName> componentNames) throws Throwable {
List<DesktopSortApp> desktopSortApps = componentNames.stream().map(new java.util.function.Function<ComponentName, DesktopSortApp>() {
@Override
public DesktopSortApp apply(ComponentName componentName) {
DesktopSortApp desktopSortApp = new DesktopSortApp(getSafeContext(), componentName);
return desktopSortApp;
}
}).sorted(new Comparator<DesktopSortApp>() {
@Override
public int compare(DesktopSortApp o1, DesktopSortApp o2) {
return Collator.getInstance(Locale.CHINESE).compare(o1.getLabel(), o2.getLabel());
}
}).collect(Collectors.toList());
return desktopSortApps;
}
})
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<DesktopSortApp>>() {
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("updateAppInfo", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<DesktopSortApp> desktopSortApps) {
Log.e(TAG, "getLauncherAppList" + "onNext: " + desktopSortApps);
mDesktopSortAppData.setValue(desktopSortApps);
public void onNext(@NonNull Integer row) {
Log.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("updateAppInfo", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Log.e("updateAppInfo", "onComplete: ");
}
});
}
}

View File

@@ -9,7 +9,7 @@ import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityContactAddBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.db.contact.ContactRepository;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
@@ -38,9 +38,9 @@ public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding
@Override
public void subscribe(@NonNull ObservableEmitter<Long> emitter) throws Throwable {
int count = mRepository.getTotalCount() + 1;
Contact contact = new Contact(name, phone, count);
Log.e(TAG, "saveContact: " + contact);
emitter.onNext(mRepository.insert(contact));
ContactInfo contactInfo = new ContactInfo(name, phone, count);
Log.e(TAG, "saveContact: " + contactInfo);
emitter.onNext(mRepository.insert(contactInfo));
emitter.onComplete();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))

View File

@@ -1,14 +1,11 @@
package com.ttstd.dialer.activity.contact.list;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -18,7 +15,7 @@ import com.ttstd.dialer.activity.contact.add.ContactAddActivity;
import com.ttstd.dialer.adapter.ContactInfoAdapter;
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
import com.ttstd.dialer.databinding.ActivityContactListBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.fragment.dialog.call.CallFragment;
import com.ttstd.dialer.view.ItemTouchHelperCallback;
@@ -31,7 +28,7 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
private ContactInfoAdapter mContactInfoAdapter;
private List<Contact> mContacts;
private List<ContactInfo> mContactInfos;
@Override
public boolean setNightMode() {
@@ -63,14 +60,14 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
@Override
public void onItemMove(int fromPosition, int toPosition) {
Log.e(TAG, "onItemMove: ");
Contact fromContact = mContacts.get(fromPosition);
int fromContactPosition = fromContact.getPosition();
Contact toContact = mContacts.get(toPosition);
int toContactPosition = toContact.getPosition();
fromContact.setPosition(toContactPosition);
mViewModel.updateItemPosition(fromContact);
toContact.setPosition(fromContactPosition);
mViewModel.updateItemPosition(toContact);
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);
}
@Override
@@ -81,8 +78,8 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
});
mContactInfoAdapter.setOnClickListener(new ContactInfoAdapter.OnClickListener() {
@Override
public void onClick(Contact contact) {
new CallFragment(contact).show(getSupportFragmentManager(), "CallFragment");
public void onClick(ContactInfo contactInfo) {
new CallFragment(contactInfo).show(getSupportFragmentManager(), "CallFragment");
}
});
// 设置ItemTouchHelper实现拖动和滑动删除
@@ -98,12 +95,12 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
@Override
protected void initData() {
mViewModel.mContactListData.observe(this, new Observer<List<Contact>>() {
mViewModel.mContactListData.observe(this, new Observer<List<ContactInfo>>() {
@Override
public void onChanged(List<Contact> contacts) {
Log.e(TAG, "mContactListData: " + contacts);
mContacts = contacts;
mContactInfoAdapter.setContacts(mContacts);
public void onChanged(List<ContactInfo> contactInfos) {
Log.e(TAG, "mContactListData: " + contactInfos);
mContactInfos = contactInfos;
mContactInfoAdapter.setContactInfos(mContactInfos);
}
});
}

View File

@@ -9,7 +9,7 @@ import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityContactListBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.db.contact.ContactRepository;
import java.util.List;
@@ -33,29 +33,29 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
mRepository = new ContactRepository(context);
}
public MutableLiveData<List<Contact>> mContactListData = new MutableLiveData<>();
public MutableLiveData<List<ContactInfo>> mContactListData = new MutableLiveData<>();
public void getAllContacts() {
Observable.create(new ObservableOnSubscribe<List<Contact>>() {
Observable.create(new ObservableOnSubscribe<List<ContactInfo>>() {
@Override
public void subscribe(@NonNull ObservableEmitter<List<Contact>> emitter) throws Throwable {
List<Contact> contacts = mRepository.getAllContacts();
emitter.onNext(contacts);
public void subscribe(@NonNull ObservableEmitter<List<ContactInfo>> emitter) throws Throwable {
List<ContactInfo> contactInfos = mRepository.getAllContacts();
emitter.onNext(contactInfos);
emitter.onComplete();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Contact>>() {
.subscribe(new Observer<List<ContactInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getAllContacts", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<Contact> contacts) {
public void onNext(@NonNull List<ContactInfo> contactInfos) {
Log.e("getAllContacts", "onNext: ");
mContactListData.setValue(contacts);
mContactListData.setValue(contactInfos);
// List<Contact> sorted = contacts.stream().sorted(new Comparator<Contact>() {
// @Override
@@ -78,12 +78,12 @@ public class ContactListViewModel extends BaseViewModel<ActivityContactListBindi
});
}
public void updateItemPosition(Contact contact) {
Log.e(TAG, "updateItemPosition: " + contact);
public void updateItemPosition(ContactInfo contactInfo) {
Log.e(TAG, "updateItemPosition: " + contactInfo);
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
int id = mRepository.update(contact);
int id = mRepository.update(contactInfo);
emitter.onNext(id);
emitter.onComplete();
}

View File

@@ -4,14 +4,19 @@ import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.loader.app.LoaderManager;
import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.R;
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.databinding.ActivityMainBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.fragment.app.AppFragment;
import com.ttstd.dialer.fragment.contact.ContactFragment;
import com.ttstd.dialer.fragment.home.HomeFragment;
import com.ttstd.dialer.view.ApkPagerAdapter;
import com.ttstd.dialer.view.BaseFragmentPagerAdapter;
import com.ttstd.dialer.view.ScaleCircleNavigator;
@@ -25,7 +30,8 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
protected MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private FragmentManager mFragmentManager = getSupportFragmentManager();
private BaseFragmentPagerAdapter mBaseFragmentPagerAdapter;
// private BaseFragmentPagerAdapter mBaseFragmentPagerAdapter;
private ApkPagerAdapter mApkPagerAdapter;
private List<Fragment> mFragments = new ArrayList<>();
private HomeFragment mHomeFragment;
@@ -34,8 +40,12 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
private int mCurrentIndex = 0;
private int mFragmentSize = 0;
private int APK_PER_FRAGMENT = 9;
private ScaleCircleNavigator mScaleCircleNavigator;
private List<AppInfo> mAppInfos;
@Override
public boolean setNightMode() {
@@ -62,18 +72,21 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
@Override
protected void initView() {
// mBaseFragmentPagerAdapter = new BaseFragmentPagerAdapter(mFragmentManager, mFragments);
mApkPagerAdapter = new ApkPagerAdapter(mFragmentManager);
mScaleCircleNavigator = new ScaleCircleNavigator(this);
boolean contactHome = mMMKV.decodeBool(CommonConfig.CONTACT_HOME_PAGE, false);
if (!contactHome) {
mCurrentIndex += 1;
}
if (mContactFragment == null) {
mContactFragment = new ContactFragment();
mFragments.add(mContactFragment);
mFragmentSize += 1;
}
boolean contactHome = mMMKV.decodeBool(CommonConfig.CONTACT_HOME_PAGE, false);
if (!contactHome) {
mCurrentIndex += 1;
}
if (mHomeFragment == null) {
mHomeFragment = new HomeFragment();
mFragments.add(mHomeFragment);
@@ -91,28 +104,64 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
mViewDataBinding.viewPager.setCurrentItem(index);
}
});
mBaseFragmentPagerAdapter = new BaseFragmentPagerAdapter(mFragmentManager, mFragments);
mViewDataBinding.viewPager.setAdapter(mBaseFragmentPagerAdapter);
mViewDataBinding.viewPager.setAdapter(mApkPagerAdapter);
mViewDataBinding.viewPager.setOffscreenPageLimit(10);
mViewDataBinding.viewPager.setCurrentItem(mCurrentIndex);
mViewDataBinding.magicIndicator.setNavigator(mScaleCircleNavigator);
ViewPagerHelper.bind(mViewDataBinding.magicIndicator, mViewDataBinding.viewPager);
Log.e(TAG, "initView: mCurrentIndex = " + mCurrentIndex);
Log.e(TAG, "initView: mFragmentSize = " + mFragmentSize);
}
@Override
protected void initData() {
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mAppInfos = appInfos;
Log.e(TAG, "onChanged: mAppInfos size = " + mAppInfos.size());
setAppList();
}
});
mViewModel.getOutsideApp();
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume: ");
setAppList();
mViewModel.getOutsideApp();
}
private void setAppList() {
Log.e(TAG, "setAppList: mFragments size = " + mFragments.size());
mFragments = mFragments.subList(0, mFragmentSize);
Log.e(TAG, "setAppList: subList mFragments size = " + mFragments.size());
int fragmentCount = (int) Math.ceil((double) mAppInfos.size() / APK_PER_FRAGMENT);
for (int i = 0; i < fragmentCount; i++) {
int start = i * APK_PER_FRAGMENT;
int end = Math.min(start + APK_PER_FRAGMENT, mAppInfos.size());
List<AppInfo> subList = new ArrayList<>(mAppInfos.subList(start, end));
Log.e(TAG, "setAppList: subList size = " + subList.size());
// AppFragment fragment = AppFragment.newInstance(subList);
AppFragment fragment = new AppFragment(subList);
fragment.setUpdateCallback(new AppFragment.UpdateCallback() {
@Override
public void onUpdate() {
mViewModel.getOutsideApp();
}
});
mFragments.add(fragment);
Log.e(TAG, "setAppList: add mFragments size = " + mFragments.size());
}
mScaleCircleNavigator.setCircleCount(mFragments.size());
mScaleCircleNavigator.notifyDataSetChanged();
mApkPagerAdapter.setFragments(mFragments);
mApkPagerAdapter.notifyDataSetChanged();
}

View File

@@ -1,12 +1,28 @@
package com.ttstd.dialer.activity.main;
import android.content.Context;
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityMainBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.db.app.AppRepository;
import java.util.List;
import java.util.concurrent.Callable;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEvent> {
private static final String TAG = "MainViewModel";
@@ -18,4 +34,38 @@ public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEv
mAppRepository = new AppRepository(context);
}
public MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
public void getOutsideApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getOutsideApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getOutsideApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Log.e("getOutsideApp", "onNext: " + appInfos);
mDesktopSortAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("getOutsideApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Log.e("getOutsideApp", "onComplete: ");
}
});
}
}

View File

@@ -0,0 +1,152 @@
package com.ttstd.dialer.adapter;
import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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.iconloader.IconCacheManager;
import com.ttstd.iconloader.IconLoader;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class AppAdapter extends RecyclerView.Adapter<AppAdapter.AppHolder> implements LoaderManager.LoaderCallbacks<Drawable> {
private static final String TAG = "AppAdapter";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private FragmentActivity mContext;
private LoaderManager mLoaderManager;
private IconCacheManager mIconCacheManager = IconCacheManager.getInstance();
public AppAdapter(LoaderManager loaderManager) {
mLoaderManager = loaderManager;
}
private List<AppInfo> mAppInfos;
public void setAppInfos(List<AppInfo> appInfos) {
mAppInfos = appInfos;
notifyDataSetChanged();
}
public interface ShortcutCallback {
void setAppInside(AppInfo appInfo);
}
private ShortcutCallback mShortcutCallback;
public void setShortcutCallback(ShortcutCallback shortcutCallback) {
mShortcutCallback = shortcutCallback;
}
@NonNull
@NotNull
@Override
public AppHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
mContext = (FragmentActivity) parent.getContext();
return new AppHolder(LayoutInflater.from(mContext).inflate(R.layout.item_app, parent, false));
}
@Override
public void onBindViewHolder(@NonNull @NotNull AppHolder holder, int position) {
AppInfo appInfo = mAppInfos.get(position);
Drawable drawable = mIconCacheManager.getIcon(appInfo.getComponentName().flattenToShortString());
if (drawable != null) {
holder.iv_icon.setImageDrawable(drawable);
} else {
mLoaderManager.restartLoader(position, null, this).forceLoad();
}
holder.tv_app_name.setText(appInfo.getLabel());
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ApkUtils.openApp(mContext, appInfo.getComponentName());
}
});
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
shortcutDialogFagment.setTitil("温馨提示");
shortcutDialogFagment.setTips("是否将应用放入更多应用");
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
@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;
}
});
}
@Override
public int getItemCount() {
return mAppInfos == null ? 0 : mAppInfos.size();
}
@NonNull
@NotNull
@Override
public Loader<Drawable> onCreateLoader(int id, @Nullable @org.jetbrains.annotations.Nullable Bundle args) {
AppInfo appInfo = mAppInfos.get(id);
ComponentName componentName = appInfo.getComponentName(); // 从数据项中获取 ComponentName
return new IconLoader(mContext, componentName, mIconCacheManager);
}
@Override
public void onLoadFinished(@NonNull @NotNull Loader<Drawable> loader, Drawable data) {
int position = loader.getId();
notifyItemChanged(position);
}
@Override
public void onLoaderReset(@NonNull @NotNull Loader<Drawable> loader) {
Log.e(TAG, "onLoaderReset: ");
}
public class AppHolder extends RecyclerView.ViewHolder {
ConstraintLayout root;
NiceImageView iv_icon;
TextView tv_app_name;
public AppHolder(@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

@@ -1,9 +1,5 @@
package com.ttstd.dialer.adapter;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -12,13 +8,12 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;
import com.shehuan.niv.NiceImageView;
import com.ttstd.dialer.R;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import java.util.Collections;
import java.util.List;
@@ -28,10 +23,10 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
private FragmentActivity mContext;
private List<Contact> mContacts;
private List<ContactInfo> mContactInfos;
public void setContacts(List<Contact> contacts) {
mContacts = contacts;
public void setContactInfos(List<ContactInfo> contactInfos) {
mContactInfos = contactInfos;
notifyDataSetChanged();
}
@@ -48,7 +43,7 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
}
public interface OnClickListener {
void onClick(Contact contact);
void onClick(ContactInfo contactInfo);
}
private OnClickListener mOnClickListener;
@@ -66,17 +61,17 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
@Override
public void onBindViewHolder(@NonNull ContactInfoHolder holder, int position) {
Contact contact = mContacts.get(position);
String name = contact.getName();
ContactInfo contactInfo = mContactInfos.get(position);
String name = contactInfo.getName();
holder.tv_name.setText(name);
String phone = contact.getPhoneNumber();
String phone = contactInfo.getPhoneNumber();
holder.tv_phone.setText(phone);
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick: " + contact);
Log.e(TAG, "onClick: " + contactInfo);
if (mOnClickListener != null) {
mOnClickListener.onClick(contact);
mOnClickListener.onClick(contactInfo);
}
}
});
@@ -84,7 +79,7 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
@Override
public int getItemCount() {
return mContacts == null ? 0 : mContacts.size();
return mContactInfos == null ? 0 : mContactInfos.size();
}
@@ -98,11 +93,11 @@ public class ContactInfoAdapter extends RecyclerView.Adapter<ContactInfoAdapter.
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mContacts, i, i + 1);
Collections.swap(mContactInfos, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mContacts, i, i - 1);
Collections.swap(mContactInfos, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);

View File

@@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.shehuan.niv.NiceImageView;
import com.ttstd.dialer.R;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import java.util.Collections;
import java.util.List;
@@ -23,10 +23,10 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
private FragmentActivity mContext;
private List<Contact> mContacts;
private List<ContactInfo> mContactInfos;
public void setContacts(List<Contact> contacts) {
mContacts = contacts;
public void setContactInfos(List<ContactInfo> contactInfos) {
mContactInfos = contactInfos;
notifyDataSetChanged();
}
@@ -43,7 +43,7 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
}
public interface OnClickListener {
void onClick(Contact contact);
void onClick(ContactInfo contactInfo);
}
private OnClickListener mOnClickListener;
@@ -61,17 +61,17 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
@Override
public void onBindViewHolder(@NonNull ContactInfoHolder holder, int position) {
Contact contact = mContacts.get(position);
String name = contact.getName();
ContactInfo contactInfo = mContactInfos.get(position);
String name = contactInfo.getName();
holder.tv_name.setText(name);
String phone = contact.getPhoneNumber();
String phone = contactInfo.getPhoneNumber();
holder.tv_phone.setText(phone);
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick: " + contact);
Log.e(TAG, "onClick: " + contactInfo);
if (mOnClickListener != null) {
mOnClickListener.onClick(contact);
mOnClickListener.onClick(contactInfo);
}
}
});
@@ -79,7 +79,7 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
@Override
public int getItemCount() {
return mContacts == null ? 0 : mContacts.size();
return mContactInfos == null ? 0 : mContactInfos.size();
}
@@ -93,11 +93,11 @@ public class HomeContactAdapter extends RecyclerView.Adapter<HomeContactAdapter.
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mContacts, i, i + 1);
Collections.swap(mContactInfos, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mContacts, i, i - 1);
Collections.swap(mContactInfos, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);

View File

@@ -7,7 +7,6 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -18,10 +17,12 @@ 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.db.app.DesktopSortApp;
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.iconloader.IconCacheManager;
import com.ttstd.iconloader.IconLoader;
@@ -43,53 +44,87 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
mLoaderManager = loaderManager;
}
public void setSelectApps(List<DesktopSortApp> selectApps) {
public void setSelectApps(List<AppInfo> selectApps) {
}
private List<DesktopSortApp> mDesktopSortApps;
private List<AppInfo> mAppInfos;
public void setDesktopSortApps(List<DesktopSortApp> desktopSortApps) {
mDesktopSortApps = desktopSortApps;
public void setAppInfos(List<AppInfo> appInfos) {
mAppInfos = appInfos;
notifyDataSetChanged();
}
public interface ShortcutCallback {
void setAppOutside(AppInfo appInfo);
}
private ShortcutCallback mShortcutCallback;
public void setShortcutCallback(ShortcutCallback shortcutCallback) {
mShortcutCallback = shortcutCallback;
}
@NonNull
@NotNull
@Override
public AppHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
mContext = (FragmentActivity) parent.getContext();
return new AppHolder(LayoutInflater.from(mContext).inflate(R.layout.item_app, parent, false));
return new AppHolder(LayoutInflater.from(mContext).inflate(R.layout.item_more_app, parent, false));
}
@Override
public void onBindViewHolder(@NonNull @NotNull AppHolder holder, int position) {
DesktopSortApp desktopSortApp = mDesktopSortApps.get(position);
Drawable drawable = mIconCacheManager.getIcon(desktopSortApp.getComponentName().flattenToShortString());
AppInfo appInfo = mAppInfos.get(position);
Drawable drawable = mIconCacheManager.getIcon(appInfo.getComponentName().flattenToShortString());
if (drawable != null) {
holder.iv_icon.setImageDrawable(drawable);
} else {
mLoaderManager.restartLoader(position, null, this).forceLoad();
}
holder.tv_app_name.setText(desktopSortApp.getLabel());
holder.tv_app_name.setText(appInfo.getLabel());
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ApkUtils.openApp(mContext, desktopSortApp.getComponentName());
ApkUtils.openApp(mContext, appInfo.getComponentName());
}
});
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo);
shortcutDialogFagment.setTitil("温馨提示");
shortcutDialogFagment.setTips("是否将应用放在桌面显示");
shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() {
@Override
public void onPositiveClick() {
if (mShortcutCallback != null)
mShortcutCallback.setAppOutside(appInfo);
shortcutDialogFagment.dismiss();
}
@Override
public void onNegativeClick() {
shortcutDialogFagment.dismiss();
}
});
shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment");
return false;
}
});
}
@Override
public int getItemCount() {
return mDesktopSortApps == null ? 0 : mDesktopSortApps.size();
return mAppInfos == null ? 0 : mAppInfos.size();
}
@NonNull
@NotNull
@Override
public Loader<Drawable> onCreateLoader(int id, @Nullable @org.jetbrains.annotations.Nullable Bundle args) {
DesktopSortApp appInfo = mDesktopSortApps.get(id);
AppInfo appInfo = mAppInfos.get(id);
ComponentName componentName = appInfo.getComponentName(); // 从数据项中获取 ComponentName
return new IconLoader(mContext, componentName, mIconCacheManager);
}
@@ -108,7 +143,7 @@ public class MoreAppAdapter extends RecyclerView.Adapter<MoreAppAdapter.AppHolde
public class AppHolder extends RecyclerView.ViewHolder {
ConstraintLayout root;
ImageView iv_icon;
NiceImageView iv_icon;
TextView tv_app_name;
public AppHolder(@NonNull @NotNull View itemView) {

View File

@@ -19,7 +19,7 @@ public interface AppDao {
Integer getCountById();
@Query("SELECT * FROM app_list WHERE package_name = :packageName AND class_name = :className")
DesktopSortApp getAppInfo(String packageName, String className);
AppInfo getAppInfo(String packageName, String className);
@Query("SELECT id FROM app_list WHERE package_name = :packageName AND class_name = :className")
Integer getIdByPackageAndClass(String packageName, String className);
@@ -29,16 +29,16 @@ public interface AppDao {
Integer checkAppInfoExists(String pkgName, String clsName);
@Insert
long insert(DesktopSortApp desktopSortApp);
long insert(AppInfo appInfo);
@Insert
long[] insert(List<DesktopSortApp> desktopSortApps);
long[] insert(List<AppInfo> appInfos);
@Update
Integer update(DesktopSortApp desktopSortApp);
Integer update(AppInfo appInfo);
@Delete
Integer delete(DesktopSortApp desktopSortApp);
Integer delete(AppInfo appInfo);
@Query("DELETE FROM app_list WHERE id = :id")
Integer deleteById(Integer id);
@@ -47,11 +47,17 @@ public interface AppDao {
Integer deleteAll();
@Query("SELECT * FROM app_list ORDER BY position ASC")
List<DesktopSortApp> getAllApp();
List<AppInfo> getAllApp();
@Query("SELECT * FROM app_list WHERE outside = 1 ORDER BY position ASC")
List<AppInfo> getOutsideApp();
@Query("SELECT * FROM app_list WHERE outside = 0 ORDER BY position ASC")
List<AppInfo> getInsideApp();
@Query("SELECT * FROM app_list WHERE id = :id")
DesktopSortApp getAppById(Integer id);
AppInfo getAppById(Integer id);
@Query("SELECT * FROM app_list WHERE label LIKE :searchQuery OR package_name LIKE :searchQuery")
List<DesktopSortApp> searchApp(String searchQuery);
List<AppInfo> searchApp(String searchQuery);
}

View File

@@ -8,7 +8,7 @@ import androidx.room.RoomDatabase;
import java.io.File;
@Database(entities = {DesktopSortApp.class}, version = 2, exportSchema = false)
@Database(entities = {AppInfo.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract AppDao appDao();

View File

@@ -2,7 +2,10 @@ package com.ttstd.dialer.db.app;
import android.content.ComponentName;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
@@ -10,6 +13,8 @@ import androidx.room.Index;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import com.google.gson.Gson;
import com.google.gson.JsonParser;
import com.ttstd.dialer.utils.ApkUtils;
import java.io.Serializable;
@@ -17,7 +22,7 @@ import java.util.Objects;
@Entity(tableName = "app_list", indices = {@Index(value = "component_name", unique = true)})
@TypeConverters({ComponentNameConverter.class})
public class DesktopSortApp implements Serializable {
public class AppInfo implements Serializable , Parcelable {
private static final long serialVersionUID = 9113517079637096245L;
@PrimaryKey(autoGenerate = true)
@@ -32,26 +37,53 @@ public class DesktopSortApp implements Serializable {
private String mClassName;
@ColumnInfo(name = "position")
private int mPosition;
@ColumnInfo(name = "outside")
private int outside;
public AppInfo() {
public DesktopSortApp() {
}
public DesktopSortApp(Context context, ComponentName componentName) {
public AppInfo(Context context, ComponentName componentName) {
this.componentName = componentName;
mPackageName = componentName.getPackageName();
mClassName = componentName.getClassName();
mPosition = 0;
mLabel = ApkUtils.getAppName(context, componentName);
this.mLabel = ApkUtils.getAppName(context, componentName);
this.mPackageName = componentName.getPackageName();
this.mClassName = componentName.getClassName();
this.mPosition = 0;
this.outside = 0;
}
public DesktopSortApp(Context context, ComponentName componentName, int pos) {
public AppInfo(Context context, ComponentName componentName, int pos) {
this.componentName = componentName;
mPackageName = componentName.getPackageName();
mClassName = componentName.getClassName();
mPosition = pos;
mLabel = ApkUtils.getAppName(context, componentName);
this.mLabel = ApkUtils.getAppName(context, componentName);
this.mPackageName = componentName.getPackageName();
this.mClassName = componentName.getClassName();
this.mPosition = pos;
this.outside = 0;
}
protected AppInfo(Parcel in) {
id = in.readInt();
componentName = in.readParcelable(ComponentName.class.getClassLoader());
mLabel = in.readString();
mPackageName = in.readString();
mClassName = in.readString();
mPosition = in.readInt();
outside = in.readInt();
}
public static final Creator<AppInfo> CREATOR = new Creator<AppInfo>() {
@Override
public AppInfo createFromParcel(Parcel in) {
return new AppInfo(in);
}
@Override
public AppInfo[] newArray(int size) {
return new AppInfo[size];
}
};
public int getId() {
return id;
}
@@ -100,10 +132,18 @@ public class DesktopSortApp implements Serializable {
this.mPosition = position;
}
public int getOutside() {
return outside;
}
public void setOutside(int outside) {
this.outside = outside;
}
@Override
public boolean equals(@Nullable @org.jetbrains.annotations.Nullable Object obj) {
if (obj instanceof DesktopSortApp) {
return Objects.equals(componentName, ((DesktopSortApp) obj).componentName);
if (obj instanceof AppInfo) {
return Objects.equals(componentName, ((AppInfo) obj).componentName);
} else if (obj instanceof ComponentName) {
return Objects.equals(componentName, obj);
} else {
@@ -115,4 +155,27 @@ public class DesktopSortApp implements Serializable {
public int hashCode() {
return componentName.hashCode();
}
@NonNull
@Override
public String toString() {
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeParcelable(componentName, flags);
dest.writeString(mLabel);
dest.writeString(mPackageName);
dest.writeString(mClassName);
dest.writeInt(mPosition);
dest.writeInt(outside);
}
}

View File

@@ -20,7 +20,7 @@ public class AppRepository {
return mAppDao.getCountById();
}
public DesktopSortApp getAppInfo(String packageName, String className) {
public AppInfo getAppInfo(String packageName, String className) {
return mAppDao.getAppInfo(packageName, className);
}
@@ -33,37 +33,47 @@ public class AppRepository {
}
// 获取所有APP
public List<DesktopSortApp> getAllApp() {
public List<AppInfo> getAllApp() {
return mAppDao.getAllApp();
}
// 获取在外面显示的APP
public List<AppInfo> getOutsideApp() {
return mAppDao.getOutsideApp();
}
// 获取在里面显示的APP
public List<AppInfo> getInsideApp() {
return mAppDao.getInsideApp();
}
// 根据ID获取APP
public DesktopSortApp getAppById(int id) {
public AppInfo getAppById(int id) {
return mAppDao.getAppById(id);
}
// 搜索APP
public List<DesktopSortApp> searchApp(String query) {
public List<AppInfo> searchApp(String query) {
return mAppDao.searchApp("%" + query + "%");
}
// 添加APP
public long insert(DesktopSortApp desktopSortApp) {
return mAppDao.insert(desktopSortApp);
public long insert(AppInfo appInfo) {
return mAppDao.insert(appInfo);
}
public long[] insert(List<DesktopSortApp> desktopSortApps) {
return mAppDao.insert(desktopSortApps);
public long[] insert(List<AppInfo> appInfos) {
return mAppDao.insert(appInfos);
}
// 更新APP
public int update(DesktopSortApp desktopSortApp) {
return mAppDao.update(desktopSortApp);
public int update(AppInfo appInfo) {
return mAppDao.update(appInfo);
}
// 删除APP
public int delete(DesktopSortApp desktopSortApp) {
return mAppDao.delete(desktopSortApp);
public int delete(AppInfo appInfo) {
return mAppDao.delete(appInfo);
}
// 根据ID删除APP

View File

@@ -19,13 +19,13 @@ public interface ContactDao {
int getCountById();
@Insert
long insert(Contact contact);
long insert(ContactInfo contactInfo);
@Update
int update(Contact contact);
int update(ContactInfo contactInfo);
@Delete
int delete(Contact contact);
int delete(ContactInfo contactInfo);
@Query("DELETE FROM contacts WHERE id = :id")
int deleteById(int id);
@@ -34,12 +34,12 @@ public interface ContactDao {
int deleteAll();
@Query("SELECT * FROM contacts ORDER BY position ASC")
List<Contact> getAllContacts();
List<ContactInfo> getAllContacts();
@Query("SELECT * FROM contacts WHERE id = :id")
Contact getContactById(int id);
ContactInfo getContactById(int id);
@Query("SELECT * FROM contacts WHERE name LIKE :searchQuery OR phoneNumber LIKE :searchQuery")
List<Contact> searchContacts(String searchQuery);
List<ContactInfo> searchContacts(String searchQuery);
}

View File

@@ -8,7 +8,7 @@ import androidx.room.RoomDatabase;
import java.io.File;
@Database(entities = {Contact.class}, version = 1, exportSchema = false)
@Database(entities = {ContactInfo.class}, version = 1, exportSchema = false)
public abstract class ContactDatabase extends RoomDatabase {
public abstract ContactDao contactDao();

View File

@@ -12,7 +12,7 @@ import java.io.Serializable;
import java.util.Objects;
@Entity(tableName = "contacts")
public class Contact implements Serializable {
public class ContactInfo implements Serializable {
@PrimaryKey(autoGenerate = true)
private int id;
@@ -22,7 +22,7 @@ public class Contact implements Serializable {
private String wxid;
private int position;
public Contact(String name, String phoneNumber, int position) {
public ContactInfo(String name, String phoneNumber, int position) {
this.name = name;
this.phoneNumber = phoneNumber;
this.position = position;
@@ -78,9 +78,9 @@ public class Contact implements Serializable {
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof Contact) {
return Objects.equals(((Contact) obj).phoneNumber, phoneNumber)
|| ((Contact) obj).id == id;
if (obj instanceof ContactInfo) {
return Objects.equals(((ContactInfo) obj).phoneNumber, phoneNumber)
|| ((ContactInfo) obj).id == id;
}
return false;
}

View File

@@ -22,33 +22,33 @@ public class ContactRepository {
}
// 获取所有联系人
public List<Contact> getAllContacts() {
public List<ContactInfo> getAllContacts() {
return mContactDao.getAllContacts();
}
// 根据ID获取联系人
public Contact getContactById(int id) {
public ContactInfo getContactById(int id) {
return mContactDao.getContactById(id);
}
// 搜索联系人
public List<Contact> searchContacts(String query) {
public List<ContactInfo> searchContacts(String query) {
return mContactDao.searchContacts("%" + query + "%");
}
// 添加联系人
public long insert(Contact contact) {
return mContactDao.insert(contact);
public long insert(ContactInfo contactInfo) {
return mContactDao.insert(contactInfo);
}
// 更新联系人
public int update(Contact contact) {
return mContactDao.update(contact);
public int update(ContactInfo contactInfo) {
return mContactDao.update(contactInfo);
}
// 删除联系人
public int delete(Contact contact) {
return mContactDao.delete(contact);
public int delete(ContactInfo contactInfo) {
return mContactDao.delete(contactInfo);
}
// 根据ID删除联系人

View File

@@ -14,28 +14,28 @@ public class ContactViewModel extends AndroidViewModel {
mRepository = new ContactRepository(application);
}
public List<Contact> getAllContacts() {
public List<ContactInfo> getAllContacts() {
return mRepository.getAllContacts();
}
public Contact getContactById(int id) {
public ContactInfo getContactById(int id) {
return mRepository.getContactById(id);
}
public List<Contact> searchContacts(String query) {
public List<ContactInfo> searchContacts(String query) {
return mRepository.searchContacts(query);
}
public void insert(Contact contact) {
mRepository.insert(contact);
public void insert(ContactInfo contactInfo) {
mRepository.insert(contactInfo);
}
public void update(Contact contact) {
mRepository.update(contact);
public void update(ContactInfo contactInfo) {
mRepository.update(contactInfo);
}
public void delete(Contact contact) {
mRepository.delete(contact);
public void delete(ContactInfo contactInfo) {
mRepository.delete(contactInfo);
}
public void deleteById(int id) {

View File

@@ -2,14 +2,56 @@ package com.ttstd.dialer.fragment.app;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import androidx.lifecycle.Observer;
import androidx.loader.app.LoaderManager;
import androidx.recyclerview.widget.GridLayoutManager;
import com.ttstd.dialer.R;
import com.ttstd.dialer.adapter.AppAdapter;
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
import com.ttstd.dialer.databinding.FragmentAppBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.view.EqualHeightDecoration;
import java.io.Serializable;
import java.util.List;
public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBinding> {
private static final String TAG = "AppFragment";
private static final String ARG_APP_LIST = "app_list";
private Activity mContext;
private AppAdapter mAppAdapter;
private List<AppInfo> mAppInfos;
// public static AppFragment newInstance(List<AppInfo> apkList) {
// AppFragment appFragment = new AppFragment();
// Log.e(TAG, "newInstance: " + appFragment);
// Bundle args = new Bundle();
// args.putSerializable(ARG_APP_LIST, (Serializable) apkList);
// appFragment.setArguments(args);
// return appFragment;
// }
public AppFragment(List<AppInfo> appInfos) {
mAppInfos = appInfos;
}
public interface UpdateCallback {
void onUpdate();
}
private UpdateCallback mUpdateCallback;
public void setUpdateCallback(UpdateCallback updateCallback) {
mUpdateCallback = updateCallback;
}
@Override
protected int getLayoutId() {
return R.layout.fragment_app;
@@ -26,20 +68,68 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
@Override
protected void initView(Bundle bundle) {
Log.e(TAG, "initView: ");
mAppAdapter = new AppAdapter(LoaderManager.getInstance(this));
mAppAdapter.setShortcutCallback(new AppAdapter.ShortcutCallback() {
@Override
public void setAppInside(AppInfo appInfo) {
appInfo.setOutside(0);
mViewModel.updateAppInfo(appInfo);
}
});
mViewDataBinding.recyclerView.setLayoutManager(new GridLayoutManager(mContext, 3));
mViewDataBinding.recyclerView.addItemDecoration(new EqualHeightDecoration(3));
mViewDataBinding.recyclerView.setAdapter(mAppAdapter);
}
@Override
protected void initData(Bundle savedInstanceState) {
Log.e(TAG, "initData: ");
Log.e(TAG, "initData: " + mViewModel.mAppUpdateData);
mViewModel.mAppUpdateData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
if (integer > 0) {
}
if (mUpdateCallback != null) {
mUpdateCallback.onUpdate();
}
}
});
// if (getArguments() != null) {
// mAppInfos = (List<AppInfo>) getArguments().getSerializable(ARG_APP_LIST);
// }
if (mAppInfos != null) {
Log.e(TAG, "initData: mAppInfos size = " + mAppInfos.size());
mAppAdapter.setAppInfos(mAppInfos);
}
}
@Override
public void fetchData() {
Log.e(TAG, "fetchData: ");
}
public class BtnClick{
@Override
public void onResume() {
super.onResume();
Log.e(TAG, "onResume: ");
if (mAppInfos != null) {
mAppAdapter.setAppInfos(mAppInfos);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.e(TAG, "onDestroyView: ");
mViewModel.resetLiveData();
}
public class BtnClick {
}
}

View File

@@ -1,10 +1,74 @@
package com.ttstd.dialer.fragment.app;
import android.content.Context;
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentAppBinding;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.db.app.AppRepository;
import java.util.concurrent.Callable;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AppViewModel extends BaseViewModel<FragmentAppBinding, FragmentEvent> {
private static final String TAG = "AppViewModel";
private AppRepository mAppRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mAppRepository = new AppRepository(context);
}
public MutableLiveData<Integer> mAppUpdateData = new MutableLiveData<>();
public void resetLiveData() {
mAppUpdateData = new MutableLiveData<>();
}
public void updateAppInfo(AppInfo appInfo) {
Observable.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return mAppRepository.update(appInfo);
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("updateAppInfo", "onSubscribe: ");
}
@Override
public void onNext(@NonNull Integer row) {
Log.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("updateAppInfo", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Log.e("updateAppInfo", "onComplete: ");
}
});
}
}

View File

@@ -1,14 +1,9 @@
package com.ttstd.dialer.fragment.contact;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.GridLayoutManager;
@@ -16,9 +11,8 @@ import com.ttstd.dialer.R;
import com.ttstd.dialer.adapter.HomeContactAdapter;
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
import com.ttstd.dialer.databinding.FragmentContactBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.fragment.dialog.call.CallFragment;
import com.ttstd.dialer.fragment.dialog.permission.PermissionDialogFragment;
import com.ttstd.dialer.view.EqualHeightDecoration;
import java.util.List;
@@ -28,7 +22,7 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
private Activity mContext;
private HomeContactAdapter mHomeContactAdapter;
private List<Contact> mContacts;
private List<ContactInfo> mContactInfos;
private static final int REQUEST_CODE_CALL = 7897;
@@ -52,8 +46,8 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
mHomeContactAdapter = new HomeContactAdapter();
mHomeContactAdapter.setOnClickListener(new HomeContactAdapter.OnClickListener() {
@Override
public void onClick(Contact contact) {
new CallFragment(contact).show(getChildFragmentManager(), "CallFragment");
public void onClick(ContactInfo contactInfo) {
new CallFragment(contactInfo).show(getChildFragmentManager(), "CallFragment");
}
});
GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, 2);
@@ -64,12 +58,12 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
@Override
protected void initData(Bundle savedInstanceState) {
mViewModel.mContactListData.observe(this, new Observer<List<Contact>>() {
mViewModel.mContactListData.observe(this, new Observer<List<ContactInfo>>() {
@Override
public void onChanged(List<Contact> contacts) {
Log.e(TAG, "mContactListData: " + contacts);
mContacts = contacts;
mHomeContactAdapter.setContacts(mContacts);
public void onChanged(List<ContactInfo> contactInfos) {
Log.e(TAG, "mContactListData: " + contactInfos);
mContactInfos = contactInfos;
mHomeContactAdapter.setContactInfos(mContactInfos);
}
});
}

View File

@@ -6,11 +6,10 @@ import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentContactBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.db.contact.ContactRepository;
import java.util.List;
@@ -33,29 +32,29 @@ public class ContactViewModel extends BaseViewModel<FragmentContactBinding, Frag
mRepository = new ContactRepository(context);
}
public MutableLiveData<List<Contact>> mContactListData = new MutableLiveData<>();
public MutableLiveData<List<ContactInfo>> mContactListData = new MutableLiveData<>();
public void getAllContacts() {
Observable.create(new ObservableOnSubscribe<List<Contact>>() {
Observable.create(new ObservableOnSubscribe<List<ContactInfo>>() {
@Override
public void subscribe(@NonNull ObservableEmitter<List<Contact>> emitter) throws Throwable {
List<Contact> contacts = mRepository.getAllContacts();
emitter.onNext(contacts);
public void subscribe(@NonNull ObservableEmitter<List<ContactInfo>> emitter) throws Throwable {
List<ContactInfo> contactInfos = mRepository.getAllContacts();
emitter.onNext(contactInfos);
emitter.onComplete();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Contact>>() {
.subscribe(new Observer<List<ContactInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getAllContacts", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<Contact> contacts) {
public void onNext(@NonNull List<ContactInfo> contactInfos) {
Log.e("getAllContacts", "onNext: ");
mContactListData.setValue(contacts);
mContactListData.setValue(contactInfos);
// List<Contact> sorted = contacts.stream().sorted(new Comparator<Contact>() {
// @Override

View File

@@ -21,10 +21,9 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.ttstd.dialer.R;
import com.ttstd.dialer.activity.contact.list.ContactListActivity;
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmDialogFragment;
import com.ttstd.dialer.databinding.FragmentCallBinding;
import com.ttstd.dialer.db.contact.Contact;
import com.ttstd.dialer.db.contact.ContactInfo;
public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, FragmentCallBinding> {
private static final String TAG = "CallFragment";
@@ -32,10 +31,10 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, Fragment
private static final int REQUEST_CODE_CALL = 7897;
private Activity mContext;
private Contact mContact;
private ContactInfo mContactInfo;
public CallFragment(Contact contact) {
mContact = contact;
public CallFragment(ContactInfo contactInfo) {
mContactInfo = contactInfo;
}
@Override
@@ -112,7 +111,7 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, Fragment
public void call() {
try {
String phone = mContact.getPhoneNumber();
String phone = mContactInfo.getPhoneNumber();
Intent dialIntent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + phone);
dialIntent.setData(data);

View File

@@ -0,0 +1,183 @@
package com.ttstd.dialer.fragment.dialog.shortcut;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
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.FragmentDialogShortcutBinding;
import com.ttstd.dialer.db.app.AppInfo;
public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewModel, FragmentDialogShortcutBinding> {
private static final String TAG = "ShortcutDialogFagment";
private Activity mContext;
private PackageManager mPackageManager;
private AppInfo mAppInfo;
private String mTitil;
private String mTips;
private String mNegativeText;
private String mPositiveText;
public ShortcutDialogFagment(AppInfo appInfo) {
mAppInfo = appInfo;
}
public void setTitil(String titil) {
mTitil = titil;
}
public void setTips(String tips) {
mTips = tips;
}
public void setNegativeText(String negativeText) {
mNegativeText = negativeText;
}
public void setPositiveText(String positiveText) {
mPositiveText = positiveText;
}
public interface OnClickListener {
void onPositiveClick();
void onNegativeClick();
}
private OnClickListener mOnClickListener;
public void setOnClickListener(OnClickListener onClickListener) {
mOnClickListener = onClickListener;
}
@Override
protected int getLayoutId() {
return R.layout.fragment_dialog_shortcut;
}
@Override
protected void initDataBinding() {
mContext = getActivity();
mPackageManager = mContext.getPackageManager();
mViewModel.setContext(mContext);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@Override
protected void initView(Bundle bundle) {
}
@Override
protected void initData(Bundle savedInstanceState) {
if (TextUtils.isEmpty(mTitil)) {
mViewDataBinding.tvTitle.setText("提示");
} else {
mViewDataBinding.tvTitle.setText(mTitil);
}
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) {
Log.e(TAG, "show: " + e.getMessage());
}
}
@Override
public void fetchData() {
}
public class BtnClick {
public void onPositive(View view) {
if (mOnClickListener != null) {
mOnClickListener.onPositiveClick();
}
}
public void onNegative(View view) {
if (mOnClickListener != null) {
mOnClickListener.onNegativeClick();
}
}
}
}

View File

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

View File

@@ -9,8 +9,8 @@ import android.util.Log;
import com.tencent.mmkv.MMKV;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.db.app.AppRepository;
import com.ttstd.dialer.db.app.DesktopSortApp;
import com.ttstd.dialer.gson.GsonUtils;
import com.ttstd.dialer.utils.ApkUtils;
@@ -27,6 +27,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.IntConsumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -73,6 +74,8 @@ public class AppManager {
this.add("com.android.settings");
this.add("com.android.dialer");
this.add("com.android.camera2");
this.add("com.android.messaging");
this.add("com.android.contacts");
this.add("com.tencent.mm");
this.add("com.jiangjia.gif");
this.add("com.ss.android.ugc.aweme");
@@ -142,9 +145,9 @@ public class AppManager {
}
// 第一步:获取所有桌面应用(空值安全处理)
private List<DesktopSortApp> getAllDesktopSortApps() {
private List<AppInfo> getAllDesktopSortApps() {
try {
List<DesktopSortApp> result = mAppRepository.getAllApp();
List<AppInfo> result = mAppRepository.getAllApp();
return result != null ? result : Collections.emptyList();
} catch (Exception e) {
Log.e(TAG, "获取桌面应用列表失败", e);
@@ -166,16 +169,27 @@ public class AppManager {
return;
}
List<DesktopSortApp> allFirstApps = allLauncherApps.stream()
List<AppInfo> allFirstApps = allLauncherApps.stream()
.filter(Objects::nonNull)
.map(this::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
.sorted((o1, o2) -> Boolean.compare(ApkUtils.isSystemApp(mContext, o1.getPackageName()), ApkUtils.isSystemApp(mContext, o2.getPackageName())))
.sorted((o1, o2) -> Boolean.compare(ApkUtils.isSystemApp(mContext, o2.getPackageName()), ApkUtils.isSystemApp(mContext, o1.getPackageName())))
.sorted(getAppComparator())
.collect(Collectors.toList());
IntStream.range(0, allFirstApps.size())
.forEach(index -> allFirstApps.get(index).setPosition(index));
.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);
}
appInfo.setPosition(index);
}
});
// 批量插入首次数据
allFirstApps.forEach(app -> {
@@ -195,15 +209,15 @@ public class AppManager {
}
// 处理未安装的应用(删除操作)
private CompletableFuture<Void> processUninstalledApps(List<DesktopSortApp> desktopSortApps) {
private CompletableFuture<Void> processUninstalledApps(List<AppInfo> appInfos) {
return CompletableFuture.runAsync(() -> {
notifyProgress(STEP_REMOVE_UNINSTALLED, 1, TOTAL_STEPS_NORMAL);
if (desktopSortApps.isEmpty()) {
if (appInfos.isEmpty()) {
Log.w(TAG, "桌面应用列表为空,跳过删除处理");
return;
}
List<Integer> ids = desktopSortApps.stream()
List<Integer> ids = appInfos.stream()
.filter(Objects::nonNull)
.filter(app -> !ApkUtils.isInstalled(mContext, app.getPackageName()))
.map(app -> {
@@ -240,7 +254,7 @@ public class AppManager {
return;
}
List<DesktopSortApp> newApps = resolveInfos.stream()
List<AppInfo> newApps = resolveInfos.stream()
.filter(Objects::nonNull)
.map(this::resolveInfoToDesktopApp)
.filter(Objects::nonNull)
@@ -273,20 +287,20 @@ public class AppManager {
private CompletableFuture<Void> updatePosition() {
return CompletableFuture.runAsync(() -> {
try {
List<DesktopSortApp> desktopSortApps = getAllDesktopSortApps();
if (desktopSortApps.isEmpty()) {
List<AppInfo> appInfos = getAllDesktopSortApps();
if (appInfos.isEmpty()) {
Log.w(TAG, "无应用数据,跳过位置更新");
return;
}
List<DesktopSortApp> sortedApps = desktopSortApps.stream()
List<AppInfo> sortedApps = appInfos.stream()
.filter(Objects::nonNull)
.sorted(Comparator.comparingInt(DesktopSortApp::getPosition))
.sorted(Comparator.comparingInt(AppInfo::getPosition))
.collect(Collectors.toList());
IntStream.range(0, sortedApps.size())
.forEach(index -> {
DesktopSortApp app = sortedApps.get(index);
AppInfo app = sortedApps.get(index);
app.setPosition(index);
try {
mAppRepository.update(app);
@@ -303,16 +317,16 @@ public class AppManager {
}
// 工具方法ResolveInfo转换为DesktopSortApp
private DesktopSortApp resolveInfoToDesktopApp(ResolveInfo resolveInfo) {
private AppInfo resolveInfoToDesktopApp(ResolveInfo resolveInfo) {
ComponentName component = new ComponentName(
resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name
);
return new DesktopSortApp(mContext, component);
return new AppInfo(mContext, component);
}
// 工具方法:检查应用是否已存在
private boolean isAppExists(DesktopSortApp app) {
private boolean isAppExists(AppInfo app) {
try {
return mAppRepository.checkAppInfoExists(app.getPackageName(), app.getClassName()) > 0;
} catch (Exception e) {
@@ -322,8 +336,8 @@ public class AppManager {
}
// 工具方法:获取应用排序器(统一排序逻辑)
private Comparator<DesktopSortApp> getAppComparator() {
return Comparator.comparing(DesktopSortApp::getLabel, Collator.getInstance(Locale.CHINESE));
private Comparator<AppInfo> getAppComparator() {
return Comparator.comparing(AppInfo::getLabel, Collator.getInstance(Locale.CHINESE));
}
public void updateApp(String packageName) {
@@ -347,7 +361,7 @@ public class AppManager {
// 重构getAllApp方法复用现有处理逻辑
public void refreshAllApps() {
CompletableFuture.runAsync(() -> {
List<DesktopSortApp> allApps = getAllDesktopSortApps();
List<AppInfo> allApps = getAllDesktopSortApps();
if (allApps.isEmpty()) {
processFirstTimeApps().join();
} else {
@@ -362,12 +376,12 @@ public class AppManager {
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> resolveInfos = ApkUtils.getAllLauncherResolveInfo(mContext);
List<DesktopSortApp> defaultApps = resolveInfos.stream()
List<AppInfo> defaultApps = resolveInfos.stream()
.filter(ri -> DEFAULT_APP_PACKAGES.contains(ri.activityInfo.packageName))
.sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE)
.compare(o1.activityInfo.loadLabel(pm), o2.activityInfo.loadLabel(pm)))
.map(ri -> new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name))
.map(component -> new DesktopSortApp(mContext, component))
.map(component -> new AppInfo(mContext, component))
.sorted((a, b) -> Boolean.compare(
ApkUtils.isSystemApp(mContext, b.getPackageName()),
ApkUtils.isSystemApp(mContext, a.getPackageName())))

View File

@@ -0,0 +1,36 @@
package com.ttstd.dialer.view;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.List;
public class ApkPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public ApkPagerAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
public void setFragments(List<Fragment> fragments) {
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments == null ? 0 : fragments.size();
}
// 解决数据更新不刷新的问题
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 内部颜色 -->
<solid android:color="@color/lightGray" />
<!-- 圆角的幅度 -->
<corners android:radius="32dp" />
<padding
android:bottom="4dp"
android:left="20dp"
android:right="20dp"
android:top="4dp" />
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 内部颜色 -->
<solid android:color="#5591F3" />
<!-- 圆角的幅度 -->
<corners android:radius="32dp" />
<padding
android:bottom="4dp"
android:left="20dp"
android:right="20dp"
android:top="4dp" />
</shape>

View File

@@ -18,7 +18,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="300dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_permissions"
android:background="@drawable/default_dialog_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@@ -15,7 +15,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".fragment.dialog.shortcut.ShortcutDialogFagment">
<data>
<variable
name="click"
type="com.ttstd.dialer.fragment.dialog.shortcut.ShortcutDialogFagment.BtnClick" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="300dp"
android:layout_height="240dp"
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="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
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:gravity="center_vertical"
android:textColor="@color/black"
android:textSize="18sp"
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="8dp"
android:gravity="center_vertical"
android:textColor="@color/black"
android:textSize="16sp"
android:visibility="visible"
tools:text="消息" />
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/linearLayout2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
<com.shehuan.niv.NiceImageView
android:id="@+id/iv_icon"
android:layout_width="40dp"
android:layout_height="40dp"
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="16dp"
android:layout_height="16dp"
android:layout_marginStart="26dp"
android:layout_marginTop="26dp"
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="20dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
android:gravity="center"
android:lineSpacingExtra="3dp"
android:maxLines="1"
android:textColor="#676767"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_icon"
tools:text="app" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/tv_negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/default_negative_background"
android:gravity="center"
android:onClick="@{click::onNegative}"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="取消" />
<TextView
android:id="@+id/tv_positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/default_positive_background"
android:gravity="center"
android:onClick="@{click::onPositive}"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="确定" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -13,7 +13,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
<com.shehuan.niv.NiceImageView
android:id="@+id/iv_icon"
android:layout_width="60dp"
android:layout_height="60dp"

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="120dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/root"
android:layout_width="match_parent"
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">
<com.shehuan.niv.NiceImageView
android:id="@+id/iv_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginTop="8dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_app_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"
android:text="appname"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -129,19 +129,31 @@ public class IconCacheManager {
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (icon instanceof AdaptiveIconDrawable) {
// 分别获取背景和前景Drawable
Drawable[] layers = new Drawable[2];
layers[0] = ((AdaptiveIconDrawable) icon).getBackground();
layers[1] = ((AdaptiveIconDrawable) icon).getForeground();
LayerDrawable layerDrawable = new LayerDrawable(layers);
// Drawable[] layers = new Drawable[2];
// layers[0] = ((AdaptiveIconDrawable) icon).getBackground();
// layers[1] = ((AdaptiveIconDrawable) icon).getForeground();
// LayerDrawable layerDrawable = new LayerDrawable(layers);
//
// int width = icon.getIntrinsicWidth();
// int height = icon.getIntrinsicHeight();
//
// Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Canvas canvas = new Canvas(bitmap);
//
// layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
// layerDrawable.draw(canvas);
int width = icon.getIntrinsicWidth();
int height = icon.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 创建一个大小与原始Drawable相同的位图
Bitmap bitmap = Bitmap.createBitmap(
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888
);
Canvas canvas = new Canvas(bitmap);
// 关键步骤设置Drawable的绘制边界
icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
icon.draw(canvas);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, editor.newOutputStream(0));
editor.commit();
}