From 7d1813f1dcf3690ad48257226c3b51f3c8e95b8b Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 15 Sep 2023 17:47:45 +0300 Subject: [PATCH 01/18] Wait for search to complete When pressing "enter" wait for the search to finish before launching the most relevant result fix #402 --- .../java/rocks/tbog/tblauncher/Behaviour.java | 35 +++++++++++++++---- .../rocks/tbog/tblauncher/TBApplication.java | 5 +++ .../tblauncher/result/RecycleAdapter.java | 4 +-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java index c5f2b7883..5a13ea301 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java +++ b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java @@ -108,7 +108,6 @@ public class Behaviour implements ISearchActivity { private static final String TAG = Behaviour.class.getSimpleName(); private TBLauncherActivity mTBLauncherActivity = null; private DialogFragment mFragmentDialog = null; - private View mResultLayout; private RecyclerList mResultList; private RecycleAdapter mResultAdapter; @@ -131,6 +130,7 @@ public void run() { mLauncherTime.postDelayed(mUpdateTime, delay); } }; + private boolean mLaunchMostRelevantResult = false; private final TextWatcher mSearchTextWatcher = new TextWatcher() { @NonNull String lastText = ""; @@ -147,9 +147,9 @@ public void afterTextChanged(Editable s) { s.delete(0, spaceEnd); } else { String text = s.toString(); - if (lastText.equals(text)) + if (lastText.equals(text) || mLaunchMostRelevantResult) return; - if (text == null || text.isEmpty()) + if (text.isEmpty()) clearAdapter(); else updateSearchRecords(false, text); @@ -383,11 +383,16 @@ private void initLauncherSearchEditText() { } // launch most relevant result - final int mostRelevantIdx = mResultList.getAdapterFirstItemIdx(); - if (mostRelevantIdx >= 0 && mostRelevantIdx < mResultAdapter.getItemCount()) { - RecyclerView.ViewHolder holder = mResultList.findViewHolderForAdapterPosition(mostRelevantIdx); - mResultAdapter.onClick(mostRelevantIdx, holder != null ? holder.itemView : view); + if (TBApplication.hasSearchTask(getContext())) { + mLaunchMostRelevantResult = true; return true; + } else { + final int mostRelevantIdx = mResultList.getAdapterFirstItemIdx(); + if (mostRelevantIdx >= 0 && mostRelevantIdx < mResultAdapter.getItemCount()) { + RecyclerView.ViewHolder holder = mResultList.findViewHolderForAdapterPosition(mostRelevantIdx); + mResultAdapter.onClick(mostRelevantIdx, holder != null ? holder.itemView : view); + return true; + } } return false; }); @@ -1008,6 +1013,22 @@ public void updateAdapter(@NonNull List results, boolean is mTBLauncherActivity.quickList.adapterUpdated(); mClearButton.setVisibility(View.VISIBLE); mMenuButton.setVisibility(View.INVISIBLE); + + if (mLaunchMostRelevantResult) { + mLaunchMostRelevantResult = false; + + // get any view + View view = mResultList.getLayoutManager() != null ? mResultList.getLayoutManager().getChildAt(0) : null; + final int mostRelevantIdx = mResultList.getAdapterFirstItemIdx(); + // try to get view of the most relevant item from adapter + if (mostRelevantIdx >= 0 && mostRelevantIdx < mResultAdapter.getItemCount()) { + RecyclerView.ViewHolder holder = mResultList.findViewHolderForAdapterPosition(mostRelevantIdx); + if (holder != null) + view = holder.itemView; + } + if (view != null) + mResultAdapter.onClick(mostRelevantIdx, view); + } } @Override diff --git a/app/src/main/java/rocks/tbog/tblauncher/TBApplication.java b/app/src/main/java/rocks/tbog/tblauncher/TBApplication.java index 459f911ab..efe2b3122 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/TBApplication.java +++ b/app/src/main/java/rocks/tbog/tblauncher/TBApplication.java @@ -315,6 +315,11 @@ public static void resetTask(Context context) { } } + public static boolean hasSearchTask(Context context) { + TBApplication app = getApplication(context); + return app.mSearchTask != null; + } + public static IconsHandler iconsHandler(Context ctx) { return getApplication(ctx).iconsHandler(); } diff --git a/app/src/main/java/rocks/tbog/tblauncher/result/RecycleAdapter.java b/app/src/main/java/rocks/tbog/tblauncher/result/RecycleAdapter.java index d74551d70..c10f325a7 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/result/RecycleAdapter.java +++ b/app/src/main/java/rocks/tbog/tblauncher/result/RecycleAdapter.java @@ -126,11 +126,11 @@ public void onClick(int index, View anyView) { onClick(result, anyView); } - public static void onClick(final EntryItem result, View v) { + public static void onClick(final EntryItem result, @NonNull View v) { ResultHelper.launch(v, result); } - public static boolean onLongClick(final EntryItem result, View v) { + public static boolean onLongClick(final EntryItem result, @NonNull View v) { ListPopup menu = result.getPopupMenu(v); // check if menu contains elements and if yes show it From 96f75b648edd9ba00677ad4ceba90ee3547995dc Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 15 Sep 2023 22:30:10 +0300 Subject: [PATCH 02/18] Improve parsing of icon packs Cache resource ids to avoid expensive calls to `packResources.getIdentifier` --- .../tbog/tblauncher/icons/DrawableInfo.java | 2 +- .../tbog/tblauncher/icons/IconPackXML.java | 87 ++++++++++--------- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java b/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java index 36f4c0d00..9b3d5efee 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java @@ -12,7 +12,7 @@ public class DrawableInfo { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof DrawableInfo)) return false; DrawableInfo that = (DrawableInfo) o; return drawableName.equals(that.drawableName); diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java index b47d85276..32e047f79 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java @@ -1,5 +1,6 @@ package rocks.tbog.tblauncher.icons; +import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; @@ -35,6 +36,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Random; +import java.util.stream.Collectors; import rocks.tbog.tblauncher.drawable.DrawableUtils; import rocks.tbog.tblauncher.utils.UserHandleCompat; @@ -68,21 +70,6 @@ public IconPackXML(@NonNull String packageName) { loaded = false; } -// static class AsyncParse extends AsyncTask { -// @Override -// protected IconPack doInBackground(IconPack... iconPacks) { -// IconPack pack = iconPacks[0]; -// pack.parseXML(); -// return pack; -// } -// -// @Override -// protected void onPostExecute(IconPack iconPack) { -// iconPack.loaded = true; -// } -// } - - @Override public synchronized boolean isLoaded() { return loaded; @@ -254,6 +241,7 @@ private BitmapDrawable generateBitmap(@NonNull BitmapDrawable defaultBitmap) { return new BitmapDrawable(packResources, result); } + @SuppressLint("DiscouragedApi") private void parseDrawableXML() { XmlPullParser xpp = null; // search drawable.xml into icons pack apk resource folder @@ -274,10 +262,12 @@ private void parseDrawableXML() { String attrName = xpp.getAttributeName(attrIdx); if (attrName.equals("drawable")) { String drawableName = xpp.getAttributeValue(attrIdx); - int drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); - if (drawableId != 0) { - DrawableInfo drawableInfo = new SimpleDrawable(drawableName, drawableId); - drawableList.add(drawableInfo); + if (!drawableList.contains(new DrawableInfo(drawableName))) { + int drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); + if (drawableId != 0) { + DrawableInfo drawableInfo = new SimpleDrawable(drawableName, drawableId); + drawableList.add(drawableInfo); + } } } } @@ -296,6 +286,7 @@ private void parseDrawableXML() { } + @SuppressLint("DiscouragedApi") @Nullable private XmlPullParser findAppFilterXml() throws XmlPullParserException { // search appfilter.xml in icon pack's apk resource folder for xml files @@ -316,10 +307,16 @@ private XmlPullParser findAppFilterXml() throws XmlPullParserException { return null; } + @SuppressLint("DiscouragedApi") private void parseAppFilterXML() { if (packResources == null) return; + Map calendarDrawablesByPrefix = new ArrayMap<>(0); + Map drawableCache = drawableList.stream() + .filter(d -> (d instanceof SimpleDrawable)) + .collect(Collectors.toMap(DrawableInfo::getDrawableName, d -> ((SimpleDrawable) d).getResourceId())); + try { XmlPullParser xpp = findAppFilterXml(); if (xpp != null) { @@ -381,17 +378,21 @@ private void parseAppFilterXML() { } } - if (drawableName != null) { - drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); + if (drawableName != null && componentName != null) { + Integer cache = drawableCache.get(drawableName); + if (cache != null) + drawableId = cache; + else { + drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); + drawableCache.put(drawableName, drawableId); + } if (drawableId != 0) { DrawableInfo drawableInfo = new SimpleDrawable(drawableName, drawableId); drawableList.add(drawableInfo); - if (componentName != null) { - ArraySet infoSet = drawablesByComponent.get(componentName); - if (infoSet == null) - drawablesByComponent.put(componentName, infoSet = new ArraySet<>(1)); - infoSet.add(drawableInfo); - } + ArraySet infoSet = drawablesByComponent.get(componentName); + if (infoSet == null) + drawablesByComponent.put(componentName, infoSet = new ArraySet<>(1)); + infoSet.add(drawableInfo); } //else { // if (componentName == null) @@ -411,22 +412,26 @@ private void parseAppFilterXML() { } } - if (prefix != null) { - CalendarDrawable drawableInfo = new CalendarDrawable(prefix + "1..31"); - for (int day = 0; day < 31; day += 1) { - drawableName = prefix + (1 + day); - drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); - if (drawableId == 0) - Log.w(TAG, "Calendar drawable `" + drawableName + "` for " + componentName + " not found"); - drawableInfo.setDayDrawable(day, drawableId); + if (prefix != null && componentName != null) { + CalendarDrawable calendarDrawable = calendarDrawablesByPrefix.get(prefix); + if (calendarDrawable == null) { + calendarDrawable = new CalendarDrawable(prefix + "1..31"); + calendarDrawablesByPrefix.put(prefix, calendarDrawable); + + // build calendar drawable + for (int day = 0; day < 31; day += 1) { + drawableName = prefix + (1 + day); + drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); + if (drawableId == 0) + Log.w(TAG, "Calendar drawable `" + drawableName + "` for " + componentName + " not found"); + calendarDrawable.setDayDrawable(day, drawableId); + } } - if (componentName != null) { - ArraySet infoSet = drawablesByComponent.get(componentName); - if (infoSet == null) - drawablesByComponent.put(componentName, infoSet = new ArraySet<>(1)); - infoSet.add(drawableInfo); - } + ArraySet infoSet = drawablesByComponent.get(componentName); + if (infoSet == null) + drawablesByComponent.put(componentName, infoSet = new ArraySet<>(1)); + infoSet.add(calendarDrawable); } break; default: From 8a54c11785deab05ae1382287b1bb16bf44e0f2d Mon Sep 17 00:00:00 2001 From: TBog Date: Sun, 17 Sep 2023 21:32:56 +0300 Subject: [PATCH 03/18] fix icon pack overlay Icon pack icons had the `frontImage` applied to all icons not just system icons --- .../java/rocks/tbog/tblauncher/handler/IconsHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java b/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java index b59f9fc19..802ec8c03 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java +++ b/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java @@ -267,7 +267,7 @@ public IconInfo getIconForPackage(ComponentName componentName, UserHandleCompat drawable = DrawableUtils.applyIconMaskShape(ctx, drawable, shape, true); return icon.setAdaptiveIcon(drawable); } else { - drawable = mIconPack.applyBackgroundAndMask(ctx, drawable, false); + //drawable = mIconPack.applyBackgroundAndMask(ctx, drawable, false); return icon.setFitInside(false).setNonAdaptiveIcon(drawable); } } @@ -294,6 +294,7 @@ public IconInfo getIconForPackage(ComponentName componentName, UserHandleCompat /** * Get or generate icon for an app + * TODO: use `getIconForPackage` instead of `getDrawableIconForPackage` */ @WorkerThread @Nullable @@ -334,7 +335,7 @@ public Drawable getDrawableIconForPackage(ComponentName componentName, UserHandl int shape = mSystemPack.getAdaptiveShape(); return DrawableUtils.applyIconMaskShape(ctx, drawable, shape, true); } else - return mIconPack.applyBackgroundAndMask(ctx, drawable, false); + return drawable; //mIconPack.applyBackgroundAndMask(ctx, drawable, false); } } From f13e1837e0c8800c9fc0df412f0a0cba1bf2795d Mon Sep 17 00:00:00 2001 From: TBog Date: Thu, 21 Sep 2023 23:02:56 +0300 Subject: [PATCH 04/18] remove duplicate code from IconsHandler * getDrawableIconForPackage was doing the same thing as getIconForPackage * clean code from custom icon pages --- .../tblauncher/customicon/ButtonPage.java | 64 ----------------- .../customicon/CustomShapePage.java | 5 +- ...hEntryPage.java => DefaultButtonPage.java} | 21 ++++-- .../customicon/IconSelectDialog.java | 70 +++++++++---------- .../tblauncher/customicon/ShortcutPage.java | 6 +- .../customicon/StaticEntryPage.java | 11 +-- .../tblauncher/customicon/SystemPage.java | 4 +- .../tbog/tblauncher/entry/StaticEntry.java | 2 +- .../tbog/tblauncher/handler/IconsHandler.java | 54 +------------- 9 files changed, 66 insertions(+), 171 deletions(-) delete mode 100644 app/src/main/java/rocks/tbog/tblauncher/customicon/ButtonPage.java rename app/src/main/java/rocks/tbog/tblauncher/customicon/{SearchEntryPage.java => DefaultButtonPage.java} (69%) diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/ButtonPage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/ButtonPage.java deleted file mode 100644 index 02c8b7907..000000000 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/ButtonPage.java +++ /dev/null @@ -1,64 +0,0 @@ -package rocks.tbog.tblauncher.customicon; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.DialogFragment; - -import rocks.tbog.tblauncher.R; -import rocks.tbog.tblauncher.drawable.DrawableUtils; - -public class ButtonPage extends CustomShapePage { - final private int mDefaultIcon; - - ButtonPage(CharSequence name, View view, int defaultIcon) { - super(name, view); - mDefaultIcon = defaultIcon; - mScale = DrawableUtils.getScaleToFit(mShape); - } - - @Override - void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickListener iconClickListener, @Nullable OnItemClickListener iconLongClickListener) { - Context context = dialogFragment.requireContext(); - super.setupView(dialogFragment, iconClickListener, iconLongClickListener); - - final Drawable originalDrawable; - // default icon - { - Drawable drawable = ContextCompat.getDrawable(context, mDefaultIcon); - originalDrawable = drawable; - ShapedIconInfo iconInfo = new DefaultIconInfo(dialogFragment.getString(R.string.default_icon), drawable); - mShapedIconAdapter.addItem(iconInfo); - } - - // customizable default icon - { - Drawable shapedDrawable = DrawableUtils.applyIconMaskShape(context, originalDrawable, mShape, mScale, mBackground); - ShapedIconInfo iconInfo = new NamedIconInfo("", shapedDrawable, originalDrawable); - mShapedIconAdapter.addItem(iconInfo); - } - - // this will call generateTextIcons - mLettersView.setText(""); - } - - static class DefaultIconInfo extends CustomShapePage.DefaultIconInfo { - final String name; - - DefaultIconInfo(@NonNull String name, Drawable icon) { - super(icon); - this.name = name; - textId = R.string.default_icon; - } - - @Nullable - @Override - CharSequence getText() { - return name; - } - } -} diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/CustomShapePage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/CustomShapePage.java index 4ff83be82..7eff5cbba 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/CustomShapePage.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/CustomShapePage.java @@ -44,6 +44,7 @@ import rocks.tbog.tblauncher.drawable.FourCodePointDrawable; import rocks.tbog.tblauncher.drawable.TextDrawable; import rocks.tbog.tblauncher.drawable.TwoCodePointDrawable; +import rocks.tbog.tblauncher.handler.IconsHandler; import rocks.tbog.tblauncher.utils.UIColors; import rocks.tbog.tblauncher.utils.UISizes; import rocks.tbog.tblauncher.utils.Utilities; @@ -408,8 +409,8 @@ protected ShapedIconInfo reshape(Context context, int shape, float scale, int ba static class DefaultIconInfo extends ShapedIconInfo { - DefaultIconInfo(Drawable icon) { - super(icon, icon); + DefaultIconInfo(IconsHandler.IconInfo icon) { + super(icon.getDrawable(), icon.getDrawable()); } @Override diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/SearchEntryPage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/DefaultButtonPage.java similarity index 69% rename from app/src/main/java/rocks/tbog/tblauncher/customicon/SearchEntryPage.java rename to app/src/main/java/rocks/tbog/tblauncher/customicon/DefaultButtonPage.java index 8b861c953..ce75889ae 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/SearchEntryPage.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/DefaultButtonPage.java @@ -6,18 +6,25 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; import androidx.fragment.app.DialogFragment; import rocks.tbog.tblauncher.R; import rocks.tbog.tblauncher.drawable.DrawableUtils; +import rocks.tbog.tblauncher.handler.IconsHandler; -public class SearchEntryPage extends CustomShapePage { - final String mEntryName; +public class DefaultButtonPage extends CustomShapePage { + final private int mDefaultIcon; + @StringRes + final private int mDefaultName; + final private String mEntryName; - SearchEntryPage(CharSequence name, View view, String entryName) { + DefaultButtonPage(CharSequence name, View view, String entryName, int defaultIcon, @StringRes int defaultName) { super(name, view); mEntryName = entryName; + mDefaultIcon = defaultIcon; + mDefaultName = defaultName; mScale = DrawableUtils.getScaleToFit(mShape); } @@ -29,9 +36,9 @@ void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickList final Drawable originalDrawable; // default icon { - Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_search); - originalDrawable = drawable; - ShapedIconInfo iconInfo = new DefaultIconInfo(dialogFragment.getString(R.string.default_static_icon, mEntryName), drawable); + originalDrawable = ContextCompat.getDrawable(context, mDefaultIcon); + IconsHandler.IconInfo iconHandlerIconInfo = new IconsHandler.IconInfo().setNonAdaptiveIcon(originalDrawable); + ShapedIconInfo iconInfo = new DefaultIconInfo(dialogFragment.getString(mDefaultName), iconHandlerIconInfo); mShapedIconAdapter.addItem(iconInfo); } @@ -49,7 +56,7 @@ void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickList static class DefaultIconInfo extends CustomShapePage.DefaultIconInfo { final String name; - DefaultIconInfo(@NonNull String name, Drawable icon) { + DefaultIconInfo(@NonNull String name, IconsHandler.IconInfo icon) { super(icon); this.name = name; textId = R.string.default_icon; diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java index cf8432b90..010206c6b 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java @@ -164,7 +164,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c * Add ViewPager pages for every icon pack * * @param inflater used for inflating the page view - * @return an array of pairs with the icon pack package name and icon pack name + * @return a list of pairs with the icon pack package name and icon pack name */ @NonNull private ArrayList> addIconPacks(LayoutInflater inflater) { @@ -211,7 +211,7 @@ private void addSystemIcons(LayoutInflater inflater) { ComponentName cn = UserHandleCompat.unflattenComponentName(name); UserHandleCompat userHandle = UserHandleCompat.fromComponentName(context, name); - mCustomShapePage = addSystemPage(inflater, mViewPager, cn, userHandle, pageName); + mCustomShapePage = addSystemPage(inflater, cn, userHandle, pageName); } else if (args.containsKey("entryId")) { String entryId = args.getString("entryId", ""); EntryItem entryItem = TBApplication.dataHandler(context).getPojo(entryId); @@ -221,7 +221,7 @@ private void addSystemIcons(LayoutInflater inflater) { } else { StaticEntry staticEntry = (StaticEntry) entryItem; String pageName = context.getString(R.string.tab_static_icons); - mCustomShapePage = addStaticEntryPage(inflater, mViewPager, staticEntry, pageName); + mCustomShapePage = addStaticEntryPage(inflater, staticEntry, pageName); } } else if (args.containsKey("shortcutId")) { String packageName = args.getString("packageName", ""); @@ -238,16 +238,16 @@ private void addSystemIcons(LayoutInflater inflater) { String shortcutId = args.getString("shortcutId", ""); Toast.makeText(Utilities.getActivity(context), context.getString(R.string.entry_not_found, shortcutId), Toast.LENGTH_LONG).show(); } else { - mCustomShapePage = addShortcutPage(inflater, mViewPager, shortcutRecord, shortcutRecord.displayName); + mCustomShapePage = addShortcutPage(inflater, shortcutRecord, shortcutRecord.displayName); } } else if (args.containsKey("searchEntryId")) { String entryName = args.getString("searchName", ""); String pageName = context.getString(R.string.tab_search_icon); - mCustomShapePage = addSearchEntryPage(inflater, mViewPager, entryName, pageName); + mCustomShapePage = addSearchEntryPage(inflater, entryName, pageName); } else if (args.containsKey("buttonId")) { int defaultIcon = args.getInt("defaultIcon"); String pageName = context.getString(R.string.tab_button_icon); - mCustomShapePage = addButtonPage(inflater, mViewPager, defaultIcon, pageName); + mCustomShapePage = addButtonPage(inflater, defaultIcon, pageName); } } @@ -286,51 +286,49 @@ public void setSelectedDrawable(Drawable selected, Drawable preview) { mPreviewLabel.setCompoundDrawables(null, null, icon, null); } - public void addIconPackPage(@NonNull LayoutInflater inflater, ViewGroup container, String packName, String packPackageName) { + private void addIconPackPage(@NonNull LayoutInflater inflater, ViewGroup container, String packName, String packPackageName) { View view = inflater.inflate(R.layout.dialog_icon_select_page, container, false); IconPackPage page = new IconPackPage(packName, packPackageName, view); PageAdapter adapter = (PageAdapter) mViewPager.getAdapter(); - adapter.addPage(page); + if (adapter != null) + adapter.addPage(page); } - public SystemPage addSystemPage(LayoutInflater inflater, ViewPager container, ComponentName cn, UserHandleCompat userHandle, String pageName) { - View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, container, false); - SystemPage page = new SystemPage(pageName, view, cn, userHandle); + private CustomShapePage addCustomShapePage(CustomShapePage page) { PageAdapter adapter = (PageAdapter) mViewPager.getAdapter(); - adapter.addPage(page); + if (adapter != null) + adapter.addPage(page); return page; } - public StaticEntryPage addStaticEntryPage(LayoutInflater inflater, ViewPager container, StaticEntry staticEntry, String pageName) { - View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, container, false); - StaticEntryPage page = new StaticEntryPage(pageName, view, staticEntry); - PageAdapter adapter = (PageAdapter) mViewPager.getAdapter(); - adapter.addPage(page); - return page; + private CustomShapePage addSystemPage(LayoutInflater inflater, ComponentName cn, UserHandleCompat userHandle, String pageName) { + View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, mViewPager, false); + CustomShapePage page = new SystemPage(pageName, view, cn, userHandle); + return addCustomShapePage(page); } - public SearchEntryPage addSearchEntryPage(LayoutInflater inflater, ViewPager container, String entryName, String pageName) { - View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, container, false); - SearchEntryPage page = new SearchEntryPage(pageName, view, entryName); - PageAdapter adapter = (PageAdapter) mViewPager.getAdapter(); - adapter.addPage(page); - return page; + private CustomShapePage addStaticEntryPage(LayoutInflater inflater, StaticEntry staticEntry, String pageName) { + View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, mViewPager, false); + CustomShapePage page = new StaticEntryPage(pageName, view, staticEntry); + return addCustomShapePage(page); } - public ShortcutPage addShortcutPage(LayoutInflater inflater, ViewPager container, ShortcutRecord shortcutRecord, String pageName) { - View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, container, false); - ShortcutPage page = new ShortcutPage(pageName, view, shortcutRecord); - PageAdapter adapter = (PageAdapter) mViewPager.getAdapter(); - adapter.addPage(page); - return page; + private CustomShapePage addSearchEntryPage(LayoutInflater inflater, String entryName, String pageName) { + View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, mViewPager, false); + CustomShapePage page = new DefaultButtonPage(pageName, view, entryName, R.drawable.ic_search, R.string.default_static_icon); + return addCustomShapePage(page); } - public ButtonPage addButtonPage(LayoutInflater inflater, ViewPager container, int defaultIcon, String pageName) { - View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, container, false); - ButtonPage page = new ButtonPage(pageName, view, defaultIcon); - PageAdapter adapter = (PageAdapter) mViewPager.getAdapter(); - adapter.addPage(page); - return page; + private CustomShapePage addShortcutPage(LayoutInflater inflater, ShortcutRecord shortcutRecord, String pageName) { + View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, mViewPager, false); + CustomShapePage page = new ShortcutPage(pageName, view, shortcutRecord); + return addCustomShapePage(page); + } + + private CustomShapePage addButtonPage(LayoutInflater inflater, int defaultIcon, String pageName) { + View view = inflater.inflate(R.layout.dialog_custom_shape_icon_select_page, mViewPager, false); + CustomShapePage page = new DefaultButtonPage(pageName, view, "", defaultIcon, R.string.default_icon); + return addCustomShapePage(page); } public ListPopup getIconPackMenu(IconData iconData) { diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/ShortcutPage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/ShortcutPage.java index c4940d1ed..b795c03ac 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/ShortcutPage.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/ShortcutPage.java @@ -13,6 +13,7 @@ import rocks.tbog.tblauncher.R; import rocks.tbog.tblauncher.TBApplication; import rocks.tbog.tblauncher.db.ShortcutRecord; +import rocks.tbog.tblauncher.handler.IconsHandler; import rocks.tbog.tblauncher.shortcut.ShortcutUtil; import rocks.tbog.tblauncher.drawable.DrawableUtils; @@ -26,7 +27,7 @@ public class ShortcutPage extends CustomShapePage { @Override void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickListener iconClickListener, @Nullable OnItemClickListener iconLongClickListener) { - Context context = dialogFragment.getContext(); + Context context = dialogFragment.requireContext(); super.setupView(dialogFragment, iconClickListener, iconLongClickListener); final Drawable defaultIcon; @@ -35,7 +36,8 @@ void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickList Bitmap bitmap = ShortcutUtil.getInitialIcon(context, mShortcutRecord.dbId); defaultIcon = new BitmapDrawable(dialogFragment.getResources(), bitmap); Drawable drawable = TBApplication.iconsHandler(context).applyShortcutMask(context, bitmap); - ShapedIconInfo iconInfo = new StaticEntryPage.DefaultIconInfo(dialogFragment.getString(R.string.default_static_icon, mShortcutRecord.displayName), drawable); + IconsHandler.IconInfo iconHandlerIconInfo = new IconsHandler.IconInfo().setNonAdaptiveIcon(drawable); + ShapedIconInfo iconInfo = new StaticEntryPage.DefaultIconInfo(dialogFragment.getString(R.string.default_static_icon, mShortcutRecord.displayName), iconHandlerIconInfo); iconInfo.textId = R.string.default_icon; mShapedIconAdapter.addItem(iconInfo); } diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/StaticEntryPage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/StaticEntryPage.java index 1fdf58a32..104b35cd4 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/StaticEntryPage.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/StaticEntryPage.java @@ -11,6 +11,7 @@ import rocks.tbog.tblauncher.R; import rocks.tbog.tblauncher.entry.StaticEntry; import rocks.tbog.tblauncher.drawable.DrawableUtils; +import rocks.tbog.tblauncher.handler.IconsHandler; public class StaticEntryPage extends CustomShapePage { private final StaticEntry mStaticEntry; @@ -23,15 +24,15 @@ public class StaticEntryPage extends CustomShapePage { @Override void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickListener iconClickListener, @Nullable OnItemClickListener iconLongClickListener) { - Context context = dialogFragment.getContext(); + Context context = dialogFragment.requireContext(); super.setupView(dialogFragment, iconClickListener, iconLongClickListener); final Drawable originalDrawable; // default icon { - Drawable drawable = mStaticEntry.getDefaultDrawable(context); - originalDrawable = drawable; - ShapedIconInfo iconInfo = new DefaultIconInfo(dialogFragment.getString(R.string.default_static_icon, mStaticEntry.getName()), drawable); + originalDrawable = mStaticEntry.getDefaultDrawable(context); + IconsHandler.IconInfo iconHandlerIconInfo = new IconsHandler.IconInfo().setNonAdaptiveIcon(originalDrawable); + ShapedIconInfo iconInfo = new DefaultIconInfo(dialogFragment.getString(R.string.default_static_icon, mStaticEntry.getName()), iconHandlerIconInfo); mShapedIconAdapter.addItem(iconInfo); } @@ -49,7 +50,7 @@ void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickList static class DefaultIconInfo extends CustomShapePage.DefaultIconInfo { final String name; - DefaultIconInfo(@NonNull String name, Drawable icon) { + DefaultIconInfo(@NonNull String name, IconsHandler.IconInfo icon) { super(icon); this.name = name; textId = R.string.default_icon; diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/SystemPage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/SystemPage.java index 29dcb292e..07833c363 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/SystemPage.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/SystemPage.java @@ -57,11 +57,11 @@ private void addSystemIcons(Context context, ShapedIconAdapter adapter) { // add default icon { IconsHandler iconsHandler = TBApplication.getApplication(context).iconsHandler(); - Drawable drawable = iconsHandler.getDrawableIconForPackage(componentName, userHandle); + IconsHandler.IconInfo icon = iconsHandler.getIconForPackage(componentName, userHandle); //checkDuplicateDrawable(dSet, drawable); - ShapedIconInfo iconInfo = new DefaultIconInfo(drawable); + ShapedIconInfo iconInfo = new DefaultIconInfo(icon); iconInfo.textId = R.string.default_icon; adapter.addItem(iconInfo); } diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/StaticEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/StaticEntry.java index c61837485..4e8289ae2 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/entry/StaticEntry.java +++ b/app/src/main/java/rocks/tbog/tblauncher/entry/StaticEntry.java @@ -191,7 +191,7 @@ public Drawable getIconDrawable(Context context) { return getDefaultDrawable(context); } - public Drawable getDefaultDrawable(Context context) { + public Drawable getDefaultDrawable(@NonNull Context context) { return AppCompatResources.getDrawable(context, iconResource); } diff --git a/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java b/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java index 802ec8c03..ea6786fc3 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java +++ b/app/src/main/java/rocks/tbog/tblauncher/handler/IconsHandler.java @@ -294,62 +294,12 @@ public IconInfo getIconForPackage(ComponentName componentName, UserHandleCompat /** * Get or generate icon for an app - * TODO: use `getIconForPackage` instead of `getDrawableIconForPackage` */ @WorkerThread @Nullable public Drawable getDrawableIconForPackage(ComponentName componentName, UserHandleCompat userHandle) { - // check the icon pack for a resource - if (mIconPack != null) { - // just checking will make this thread wait for the icon pack to load - if (!mIconPack.isLoaded()) { - String componentString = componentName.toString(); - if (mLoadIconsPackTask == null) { - Log.w(TAG, "icon pack `" + mIconPack.getPackPackageName() + "` not loaded, reload"); - SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(ctx); - loadIconsPack(pref.getString("icons-pack", null)); - return getCachedAppIcon(componentString); - } - - Drawable cachedIcon = getCachedAppIcon(componentString); - if (cachedIcon != null) { - Log.i(TAG, "icon pack `" + mIconPack.getPackPackageName() + "` not loaded, cached icon used"); - return cachedIcon; - } - - Log.w(TAG, "icon pack `" + mIconPack.getPackPackageName() + "` not loaded, wait"); - try { - mLoadIconsPackTask.wait(); - } catch (Exception ignored) { - } - if (!mIconPack.isLoaded()) { - Log.e(TAG, "icon pack `" + mIconPack.getPackPackageName() + "` waiting failed to load"); - return null; - } - } - String componentString = componentName.toString(); - DrawableInfo info = mIconPack.getComponentDrawable(componentString); - Drawable drawable = mIconPack.getDrawable(info); - if (drawable != null) { - if (DrawableUtils.isAdaptiveIconDrawable(drawable) || mForceAdaptive) { - int shape = mSystemPack.getAdaptiveShape(); - return DrawableUtils.applyIconMaskShape(ctx, drawable, shape, true); - } else - return drawable; //mIconPack.applyBackgroundAndMask(ctx, drawable, false); - } - } - - // if icon pack doesn't have the drawable, use system drawable - Drawable systemIcon = mSystemPack.getComponentDrawable(ctx, componentName, userHandle); - if (systemIcon == null) - return null; - - // if the icon pack has a mask, use that instead of the adaptive shape - if (mIconPack != null && mIconPack.hasMask() && !mForceShape) - return mIconPack.applyBackgroundAndMask(ctx, systemIcon, false); - - boolean fitInside = mForceAdaptive || !mForceShape; - return mSystemPack.applyBackgroundAndMask(ctx, systemIcon, fitInside); + IconInfo icon = getIconForPackage(componentName, userHandle); + return icon.getDrawable(); } /** From 58cf5e9afc4b8e1803f3c161e556ac034fed2cf3 Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 00:14:47 +0300 Subject: [PATCH 05/18] lazy load resource IDs --- .../tblauncher/icons/CalendarDrawable.java | 43 +++++++++-- .../tbog/tblauncher/icons/DrawableInfo.java | 35 +++++---- .../tbog/tblauncher/icons/IconPackXML.java | 74 +++++-------------- .../tblauncher/icons/LazyLoadDrawable.java | 39 ++++++++++ .../tbog/tblauncher/icons/SimpleDrawable.java | 18 +++++ 5 files changed, 135 insertions(+), 74 deletions(-) create mode 100644 app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java b/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java index 57a8f615b..374c731ae 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java @@ -1,22 +1,40 @@ package rocks.tbog.tblauncher.icons; +import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.res.ResourcesCompat; + +import java.util.Arrays; +import java.util.Calendar; public class CalendarDrawable extends DrawableInfo { private final int[] drawableForDay; + private final boolean[] drawableIdCached; protected CalendarDrawable(@NonNull String drawableName) { super(drawableName); drawableForDay = new int[31]; + drawableIdCached = new boolean[31]; + Arrays.fill(drawableIdCached, false); } - public void setDayDrawable(int dayOfMonthIdx, @DrawableRes int drawableId) { - drawableForDay[dayOfMonthIdx] = drawableId; - } - + @SuppressLint("DiscouragedApi") @DrawableRes - public int getDayDrawable(int dayOfMonthIdx) { + private int getDayDrawableId(@NonNull IconPackXML iconPack, int dayOfMonthIdx) { + Resources res = iconPack.getResources(); + if (res == null) + return 0; + if (!drawableIdCached[dayOfMonthIdx]) { + String drawableName = getDrawableName() + (1 + dayOfMonthIdx); + drawableForDay[dayOfMonthIdx] = res.getIdentifier(drawableName, "drawable", iconPack.getPackPackageName()); + drawableIdCached[dayOfMonthIdx] = true; + } + return drawableForDay[dayOfMonthIdx]; } @@ -24,4 +42,19 @@ public int getDayDrawable(int dayOfMonthIdx) { public boolean isDynamic() { return true; } + + @Nullable + @Override + public Drawable getDrawable(@NonNull IconPackXML iconPack, @Nullable Resources.Theme theme) { + Resources res = iconPack.getResources(); + if (res == null) + return null; + int dayOfMonthIdx = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1; + int drawableId = getDayDrawableId(iconPack, dayOfMonthIdx); + try { + return ResourcesCompat.getDrawable(res, drawableId, theme); + } catch (Resources.NotFoundException ignored) { + return null; + } + } } diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java b/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java index 9b3d5efee..adbd8fc02 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java @@ -1,13 +1,33 @@ package rocks.tbog.tblauncher.icons; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.Objects; -public class DrawableInfo { +public abstract class DrawableInfo { @NonNull private final String drawableName; + protected DrawableInfo(@NonNull String drawableName) { + this.drawableName = drawableName; + } + + @NonNull + public String getDrawableName() { + return drawableName; + } + + public boolean isDynamic() { + return false; + } + + @Nullable + public abstract Drawable getDrawable(@NonNull IconPackXML iconPack, @Nullable Resources.Theme theme); + @Override public boolean equals(Object o) { if (this == o) @@ -22,17 +42,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(drawableName); } - - protected DrawableInfo(@NonNull String drawableName) { - this.drawableName = drawableName; - } - - @NonNull - public String getDrawableName() { - return drawableName; - } - - public boolean isDynamic() { - return false; - } } diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java index 32e047f79..c25b40b84 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java @@ -15,13 +15,13 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.ArraySet; -import androidx.core.content.res.ResourcesCompat; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -30,7 +30,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -144,20 +143,8 @@ public DrawableInfo getComponentDrawable(String componentName) { @Nullable @Override public Drawable getDrawable(@Nullable DrawableInfo drawableInfo) { - if (drawableInfo instanceof SimpleDrawable) { - SimpleDrawable sd = (SimpleDrawable) drawableInfo; - try { - return ResourcesCompat.getDrawable(packResources, sd.getResourceId(), null); - } catch (Resources.NotFoundException ignored) { - } - } else if (drawableInfo instanceof CalendarDrawable) { - CalendarDrawable cd = (CalendarDrawable) drawableInfo; - // The first day of the month has value 1. - int dayOfMonthIdx = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1; - try { - return ResourcesCompat.getDrawable(packResources, cd.getDayDrawable(dayOfMonthIdx), null); - } catch (Resources.NotFoundException ignored) { - } + if (drawableInfo != null) { + return drawableInfo.getDrawable(this, null); } return null; } @@ -262,12 +249,8 @@ private void parseDrawableXML() { String attrName = xpp.getAttributeName(attrIdx); if (attrName.equals("drawable")) { String drawableName = xpp.getAttributeValue(attrIdx); - if (!drawableList.contains(new DrawableInfo(drawableName))) { - int drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); - if (drawableId != 0) { - DrawableInfo drawableInfo = new SimpleDrawable(drawableName, drawableId); - drawableList.add(drawableInfo); - } + if (!TextUtils.isEmpty(drawableName)) { + drawableList.add(new LazyLoadDrawable(drawableName)); } } } @@ -378,27 +361,13 @@ private void parseAppFilterXML() { } } - if (drawableName != null && componentName != null) { - Integer cache = drawableCache.get(drawableName); - if (cache != null) - drawableId = cache; - else { - drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); - drawableCache.put(drawableName, drawableId); - } - if (drawableId != 0) { - DrawableInfo drawableInfo = new SimpleDrawable(drawableName, drawableId); - drawableList.add(drawableInfo); - ArraySet infoSet = drawablesByComponent.get(componentName); - if (infoSet == null) - drawablesByComponent.put(componentName, infoSet = new ArraySet<>(1)); - infoSet.add(drawableInfo); - } - //else { - // if (componentName == null) - // componentName = "`null`"; - // Log.w(TAG, "Drawable `" + drawableName + "` for " + componentName + " not found"); - //} + if (!TextUtils.isEmpty(drawableName) && !TextUtils.isEmpty(componentName)) { + DrawableInfo drawableInfo = new LazyLoadDrawable(drawableName); + drawableList.add(drawableInfo); + ArraySet infoSet = drawablesByComponent.get(componentName); + if (infoSet == null) + drawablesByComponent.put(componentName, infoSet = new ArraySet<>(1)); + infoSet.add(drawableInfo); } break; case "calendar": @@ -412,20 +381,11 @@ private void parseAppFilterXML() { } } - if (prefix != null && componentName != null) { + if (!TextUtils.isEmpty(prefix) && !TextUtils.isEmpty(componentName)) { CalendarDrawable calendarDrawable = calendarDrawablesByPrefix.get(prefix); if (calendarDrawable == null) { - calendarDrawable = new CalendarDrawable(prefix + "1..31"); + calendarDrawable = new CalendarDrawable(prefix); calendarDrawablesByPrefix.put(prefix, calendarDrawable); - - // build calendar drawable - for (int day = 0; day < 31; day += 1) { - drawableName = prefix + (1 + day); - drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); - if (drawableId == 0) - Log.w(TAG, "Calendar drawable `" + drawableName + "` for " + componentName + " not found"); - calendarDrawable.setDayDrawable(day, drawableId); - } } ArraySet infoSet = drawablesByComponent.get(componentName); @@ -443,7 +403,7 @@ private void parseAppFilterXML() { } } } catch (Exception e) { - Log.e(TAG, "Error parsing appfilter.xml " + e); + Log.e(TAG, "parsing appfilter.xml ", e); } } @@ -454,4 +414,8 @@ public String getPackPackageName() { return iconPackPackageName; } + @Nullable + public Resources getResources() { + return packResources; + } } diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java b/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java new file mode 100644 index 000000000..21afa7d5c --- /dev/null +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java @@ -0,0 +1,39 @@ +package rocks.tbog.tblauncher.icons; + +import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.res.ResourcesCompat; + +public class LazyLoadDrawable extends DrawableInfo { + + @DrawableRes + private int drawableId = 0; + private boolean drawableIdCached = false; + + protected LazyLoadDrawable(@NonNull String drawableName) { + super(drawableName); + } + + @SuppressLint("DiscouragedApi") + @Nullable + @Override + public Drawable getDrawable(@NonNull IconPackXML iconPack, @Nullable Resources.Theme theme) { + Resources res = iconPack.getResources(); + if (res == null) + return null; + if (!drawableIdCached) { + drawableId = res.getIdentifier(getDrawableName(), "drawable", iconPack.getPackPackageName()); + drawableIdCached = true; + } + try { + return ResourcesCompat.getDrawable(res, drawableId, theme); + } catch (Resources.NotFoundException ignored) { + return null; + } + } +} diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java b/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java index a721a6000..b95572d2e 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java @@ -1,7 +1,12 @@ package rocks.tbog.tblauncher.icons; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.res.ResourcesCompat; public class SimpleDrawable extends DrawableInfo { @DrawableRes @@ -16,4 +21,17 @@ public SimpleDrawable(@NonNull String drawableName, @DrawableRes int drawableId) public int getResourceId() { return drawableId; } + + @Nullable + @Override + public Drawable getDrawable(@NonNull IconPackXML iconPack, @Nullable Resources.Theme theme) { + Resources res = iconPack.getResources(); + if (res == null) + return null; + try { + return ResourcesCompat.getDrawable(res, drawableId, theme); + } catch (Resources.NotFoundException ignored) { + return null; + } + } } From c823fe71ed6121feedd5398ccbd80d77850e1ab8 Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 15:26:33 +0300 Subject: [PATCH 06/18] synchronize icon cache access --- .../rocks/tbog/tblauncher/DrawableCache.java | 99 ++++++++++--------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java b/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java index d7ac5b25a..d88215da6 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java +++ b/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java @@ -22,47 +22,76 @@ public void setSize(int maxSize) { } public void setCalendar(String cacheId) { - DrawableInfo info = mCache.get(cacheId); - if (info != null) - info.setToday(); + synchronized (mCache) { + DrawableInfo info = mCache.get(cacheId); + if (info != null) + info.setToday(); + } } @Nullable public Drawable getCachedDrawable(@NonNull String cacheId) { - DrawableInfo info = mCache.get(cacheId); - if (info != null) { - if (info.isToday()) - return info.drawable; - mCache.remove(cacheId); + synchronized (mCache) { + DrawableInfo info = mCache.get(cacheId); + if (info != null) { + if (info.isToday()) + return info.drawable; + mCache.remove(cacheId); + } } return null; } - @Nullable - public DrawableInfo getCachedInfo(@NonNull String cacheId) { - return mCache.get(cacheId); - } +// @Nullable +// public DrawableInfo getCachedInfo(@NonNull String cacheId) { +// return mCache.get(cacheId); +// } public void setCachedInfo(@NonNull String cacheId, @Nullable DrawableInfo cache) { - if (cache == null) - { - mCache.remove(cacheId); - return; + synchronized (mCache) { + if (cache == null) { + mCache.remove(cacheId); + return; + } + if (!mEnabled) + return; + mCache.put(cacheId, cache); } - if (!mEnabled) - return; - mCache.put(cacheId, cache); } public void cacheDrawable(@NonNull String cacheId, @Nullable Drawable drawable) { - if (drawable == null) { - mCache.remove(cacheId); - return; + synchronized (mCache) { + if (drawable == null) { + mCache.remove(cacheId); + return; + } + if (!mEnabled) + return; + DrawableInfo info = new DrawableInfo(drawable); + mCache.put(cacheId, info); + } + } + + public void clearCache() { + synchronized (mCache) { + mCache.evictAll(); + } + } + + public void onPrefChanged(Context ctx, SharedPreferences pref) { + boolean enabled = pref.getBoolean("cache-drawable", true); + if (enabled != mEnabled) { + mEnabled = enabled; + clearCache(); + } + boolean halfSize = pref.getBoolean("cache-half-apps", true); + Collection apps = TBApplication.appsHandler(ctx).getAllApps(); + int size = apps.size(); + size = size < 16 ? 16 : halfSize ? (size / 2) : (size * 115 / 100); + Log.i(TAG, "Cache size: " + size); + synchronized (mCache) { + mCache.resize(size); } - if (!mEnabled) - return; - DrawableInfo info = new DrawableInfo(drawable); - mCache.put(cacheId, info); } public static class DrawableInfo { @@ -87,22 +116,4 @@ public boolean isToday() { return dayOfMonth == Calendar.getInstance().get(Calendar.DAY_OF_MONTH); } } - - public void clearCache() { - mCache.evictAll(); - } - - public void onPrefChanged(Context ctx, SharedPreferences pref) { - boolean enabled = pref.getBoolean("cache-drawable", true); - if (enabled != mEnabled) { - mEnabled = enabled; - clearCache(); - } - boolean halfSize = pref.getBoolean("cache-half-apps", true); - Collection apps = TBApplication.appsHandler(ctx).getAllApps(); - int size = apps.size(); - size = size < 16 ? 16 : halfSize ? (size / 2) : (size * 115 / 100); - Log.i(TAG, "Cache size: " + size); - mCache.resize(size); - } } From fd94ce545f008395d346b6bbc17ce7c72e7014cd Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 15:48:00 +0300 Subject: [PATCH 07/18] improve button icon cache previously we were depending on the cached drawable being null to set the default icon --- .../rocks/tbog/tblauncher/DrawableCache.java | 17 ----------------- .../tbog/tblauncher/entry/ContactEntry.java | 16 +++++++++------- .../tblauncher/result/ResultViewHelper.java | 17 +++++------------ 3 files changed, 14 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java b/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java index d88215da6..782d75332 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java +++ b/app/src/main/java/rocks/tbog/tblauncher/DrawableCache.java @@ -42,23 +42,6 @@ public Drawable getCachedDrawable(@NonNull String cacheId) { return null; } -// @Nullable -// public DrawableInfo getCachedInfo(@NonNull String cacheId) { -// return mCache.get(cacheId); -// } - - public void setCachedInfo(@NonNull String cacheId, @Nullable DrawableInfo cache) { - synchronized (mCache) { - if (cache == null) { - mCache.remove(cacheId); - return; - } - if (!mEnabled) - return; - mCache.put(cacheId, cache); - } - } - public void cacheDrawable(@NonNull String cacheId, @Nullable Drawable drawable) { synchronized (mCache) { if (drawable == null) { diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java index 1bbda182f..1a73dae07 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java +++ b/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java @@ -8,7 +8,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.graphics.PorterDuff; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -275,8 +274,9 @@ private void displayActions(View root, boolean hasPhone) { phoneButton.setVisibility(View.VISIBLE); phoneButton.clearColorFilter(); ResultViewHelper.setButtonIconAsync(phoneButton, BTN_ID_PHONE, ctx -> { - phoneButton.post(() -> phoneButton.setColorFilter(contactActionColor, PorterDuff.Mode.MULTIPLY)); - return ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_phone, null); + Drawable drawable = ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_phone, null); + Utilities.setColorFilterMultiply(drawable, contactActionColor); + return drawable; }); phoneButton.setOnClickListener(v -> { ResultHelper.recordLaunch(this, context); @@ -295,8 +295,9 @@ private void displayActions(View root, boolean hasPhone) { messageButton.setVisibility(View.VISIBLE); messageButton.clearColorFilter(); ResultViewHelper.setButtonIconAsync(messageButton, BTN_ID_MESSAGE, ctx -> { - messageButton.post(() -> messageButton.setColorFilter(contactActionColor, PorterDuff.Mode.MULTIPLY)); - return ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_message, null); + Drawable drawable = ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_message, null); + Utilities.setColorFilterMultiply(drawable, contactActionColor); + return drawable; }); messageButton.setOnClickListener(v -> { ResultHelper.recordLaunch(this, context); @@ -315,8 +316,9 @@ private void displayActions(View root, boolean hasPhone) { openButton.setVisibility(View.VISIBLE); openButton.clearColorFilter(); ResultViewHelper.setButtonIconAsync(openButton, BTN_ID_OPEN, ctx -> { - openButton.post(() -> openButton.setColorFilter(contactActionColor, PorterDuff.Mode.MULTIPLY)); - return ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_send, null); + Drawable drawable = ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_send, null); + Utilities.setColorFilterMultiply(drawable, contactActionColor); + return drawable; }); openButton.setOnClickListener(v -> { ResultHelper.recordLaunch(this, context); diff --git a/app/src/main/java/rocks/tbog/tblauncher/result/ResultViewHelper.java b/app/src/main/java/rocks/tbog/tblauncher/result/ResultViewHelper.java index a5822595f..54d08dd52 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/result/ResultViewHelper.java +++ b/app/src/main/java/rocks/tbog/tblauncher/result/ResultViewHelper.java @@ -27,7 +27,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import rocks.tbog.tblauncher.DrawableCache; import rocks.tbog.tblauncher.R; import rocks.tbog.tblauncher.TBApplication; import rocks.tbog.tblauncher.entry.EntryItem; @@ -132,25 +131,19 @@ public static boolean displayHighlighted(@NonNull ResultRelevance relevance, Ite public static void setButtonIconAsync(@NonNull ImageView iconView, String buttonId, @NonNull Utilities.GetDrawable getDefaultIcon) { Context context = iconView.getContext(); - var cache = TBApplication.drawableCache(context).getCachedInfo(buttonId); + + Drawable cache = TBApplication.drawableCache(context).getCachedDrawable(buttonId); if (cache != null) { Log.d(TAG, "cache found, view=" + Integer.toHexString(iconView.hashCode()) + " button=" + buttonId); - final Drawable drawable; - if (cache.drawable != null) { - // found the icon in cache - drawable = cache.drawable; - } else { - drawable = getDefaultIcon.getDrawable(context); - } - iconView.setImageDrawable(drawable); + // found the icon in cache + iconView.setImageDrawable(cache); return; } Utilities.setIconAsync(iconView, ctx -> { Drawable buttonIcon = TBApplication.iconsHandler(ctx).getButtonIcon(buttonId); if (buttonIcon == null) { - TBApplication.drawableCache(ctx).setCachedInfo(buttonId, new DrawableCache.DrawableInfo(null)); - return getDefaultIcon.getDrawable(ctx); + buttonIcon = getDefaultIcon.getDrawable(ctx); } TBApplication.drawableCache(ctx).cacheDrawable(buttonId, buttonIcon); return buttonIcon; From 578e182c5071c23e033146026d6a3ba5add8b951 Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 23:17:29 +0300 Subject: [PATCH 08/18] remove unused map --- .../java/rocks/tbog/tblauncher/icons/IconPackXML.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java index c25b40b84..0043b6c56 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java @@ -35,7 +35,6 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Random; -import java.util.stream.Collectors; import rocks.tbog.tblauncher.drawable.DrawableUtils; import rocks.tbog.tblauncher.utils.UserHandleCompat; @@ -296,10 +295,6 @@ private void parseAppFilterXML() { return; Map calendarDrawablesByPrefix = new ArrayMap<>(0); - Map drawableCache = drawableList.stream() - .filter(d -> (d instanceof SimpleDrawable)) - .collect(Collectors.toMap(DrawableInfo::getDrawableName, d -> ((SimpleDrawable) d).getResourceId())); - try { XmlPullParser xpp = findAppFilterXml(); if (xpp != null) { @@ -319,10 +314,6 @@ private void parseAppFilterXML() { drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); if (drawableId != 0) backImages.add(new SimpleDrawable(drawableName, drawableId)); -// Bitmap iconback = loadBitmap(drawableName); -// if (iconback != null) { -// backImages.add(iconback); -// } } } break; @@ -333,7 +324,6 @@ private void parseAppFilterXML() { drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); if (drawableId != 0) maskImage = new SimpleDrawable(drawableName, drawableId); - //maskImage = loadBitmap(drawableName); } break; //parse xml tags used as front image of generated icons @@ -343,7 +333,6 @@ private void parseAppFilterXML() { drawableId = packResources.getIdentifier(drawableName, "drawable", iconPackPackageName); if (drawableId != 0) frontImage = new SimpleDrawable(drawableName, drawableId); - //frontImage = loadBitmap(drawableName); } break; //parse xml tags used as scale factor of original bitmap icon From c835fea5f68476c7bf50bde2828ff58a1cee91d9 Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 16:26:41 +0300 Subject: [PATCH 09/18] state is kept in application not in view --- .../java/rocks/tbog/tblauncher/Behaviour.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java index 4e7e940b6..683c59731 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java +++ b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java @@ -677,7 +677,7 @@ private void showDesktop(LauncherState.Desktop mode) { break; case WIDGET: // show widgets - showWidgets(true); + showWidgets(); // hide/show the QuickList TBApplication.quickList(getContext()).updateVisibility(); // enable/disable fullscreen (status and navigation bar) @@ -865,14 +865,13 @@ public void onAnimationEnd(Animator animation) { mSearchEditText.setEnabled(false); } + private void showWidgets() + { + boolean animate = !TBApplication.state().isWidgetScreenVisible(); + showWidgets(animate); + } + private void showWidgets(boolean animate) { - if (TBApplication.state().getWidgetScreenVisibility() != LauncherState.AnimatedVisibility.ANIM_TO_VISIBLE) - mWidgetContainer.animate().cancel(); - if (mWidgetContainer.getVisibility() == View.VISIBLE) { - mWidgetContainer.setAlpha(1f); - hideResultList(false); - return; - } mWidgetContainer.setVisibility(View.VISIBLE); if (animate) { mWidgetContainer.setAlpha(0f); @@ -894,10 +893,11 @@ public void onAnimationEnd(Animator animation) { }) .start(); } else { + mWidgetContainer.animate().cancel(); TBApplication.state().setWidgetScreen(LauncherState.AnimatedVisibility.VISIBLE); mWidgetContainer.setAlpha(1f); } - hideResultList(true); + hideResultList(animate); } public void showKeyboard() { From df7fa0e14ec7f7cf4a0dd48824735ee2af6847ee Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 20:20:01 +0300 Subject: [PATCH 10/18] improve show/hide behavior --- .../java/rocks/tbog/tblauncher/Behaviour.java | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java index 683c59731..0636f9015 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java +++ b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java @@ -42,7 +42,6 @@ import androidx.recyclerview.widget.RecyclerView; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; @@ -623,7 +622,7 @@ public void switchToDesktop(@NonNull LauncherState.Desktop mode) { switch (currentMode) { case SEARCH: resetTask(); - hideSearchBar(true); + hideSearchBar(); break; case WIDGET: hideWidgets(); @@ -786,7 +785,8 @@ private void showSearchBar() { setSearchHint(); UITheme.applySearchBarTextShadow(mSearchEditText); - mSearchBarContainer.animate().cancel(); + if (TBApplication.state().getSearchBarVisibility() != LauncherState.AnimatedVisibility.ANIM_TO_VISIBLE) + mSearchBarContainer.animate().cancel(); mSearchBarContainer.setVisibility(View.VISIBLE); mSearchBarContainer.animate() .setStartDelay(0) @@ -822,41 +822,54 @@ private void hideWidgets() { mWidgetContainer.setVisibility(View.GONE); } + private void hideSearchBar() { + boolean animate = !TBApplication.state().isSearchBarVisible(); + hideSearchBar(animate); + } + private void hideSearchBar(boolean animate) { clearSearchText(); clearAdapter(); - final float translationY; - if (PrefCache.searchBarAtBottom(mPref)) - translationY = mSearchBarContainer.getHeight() * 2f; - else - translationY = mSearchBarContainer.getHeight() * -2f; - - mSearchBarContainer.animate().cancel(); - if (animate) { - mSearchBarContainer.animate() - .alpha(0f) - .translationY(translationY) - .setDuration(UI_ANIMATION_DURATION) - .setInterpolator(new AccelerateInterpolator()) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - TBApplication.state().setSearchBar(LauncherState.AnimatedVisibility.ANIM_TO_HIDDEN); - } - - @Override - public void onAnimationEnd(Animator animation) { - TBApplication.state().setSearchBar(LauncherState.AnimatedVisibility.HIDDEN); - mSearchBarContainer.setVisibility(View.GONE); - } - }) - .start(); + if (mSearchBarContainer.getVisibility() == View.VISIBLE) { + final float translationY; + if (PrefCache.searchBarAtBottom(mPref)) + translationY = mSearchBarContainer.getHeight() * 2f; + else + translationY = mSearchBarContainer.getHeight() * -2f; + + if (TBApplication.state().getSearchBarVisibility() != LauncherState.AnimatedVisibility.ANIM_TO_HIDDEN) + mSearchBarContainer.animate().cancel(); + + if (animate) { + mSearchBarContainer.setTranslationY(0f); + mSearchBarContainer.animate() + .alpha(0f) + .translationY(translationY) + .setDuration(UI_ANIMATION_DURATION) + .setInterpolator(new AccelerateInterpolator()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + TBApplication.state().setSearchBar(LauncherState.AnimatedVisibility.ANIM_TO_HIDDEN); + } + + @Override + public void onAnimationEnd(Animator animation) { + TBApplication.state().setSearchBar(LauncherState.AnimatedVisibility.HIDDEN); + mSearchBarContainer.setVisibility(View.GONE); + } + }) + .start(); + } else { + TBApplication.state().setSearchBar(LauncherState.AnimatedVisibility.HIDDEN); + mSearchBarContainer.setAlpha(0f); + mSearchBarContainer.setTranslationY(translationY); + mSearchBarContainer.setVisibility(View.GONE); + } } else { - TBApplication.state().setSearchBar(LauncherState.AnimatedVisibility.HIDDEN); - mSearchBarContainer.setAlpha(0f); - mSearchBarContainer.setTranslationY(translationY); - mSearchBarContainer.setVisibility(View.GONE); + Log.d(TAG, "mSearchBarContainer not VISIBLE, setting state to HIDDEN"); + TBApplication.state().setResultList(LauncherState.AnimatedVisibility.HIDDEN); } if (PrefCache.linkKeyboardAndSearchBar(mPref)) @@ -865,13 +878,15 @@ public void onAnimationEnd(Animator animation) { mSearchEditText.setEnabled(false); } - private void showWidgets() - { + private void showWidgets() { boolean animate = !TBApplication.state().isWidgetScreenVisible(); showWidgets(animate); } private void showWidgets(boolean animate) { + if (TBApplication.state().getWidgetScreenVisibility() != LauncherState.AnimatedVisibility.ANIM_TO_VISIBLE) + mSearchBarContainer.animate().cancel(); + mWidgetContainer.setVisibility(View.VISIBLE); if (animate) { mWidgetContainer.setAlpha(0f); @@ -1045,7 +1060,7 @@ public void runSearcher(@NonNull String query, @NonNull Class constructor = searcherClass.getConstructor(ISearchActivity.class, String.class); searcher = constructor.newInstance(this, query); - } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { + } catch (ReflectiveOperationException e) { Log.e(TAG, "new ", e); } if (searcher != null) @@ -1087,8 +1102,8 @@ private void showResultList(boolean animate) { Log.d(TAG, "showResultList (anim " + animate + ")"); if (TBApplication.state().getResultListVisibility() != LauncherState.AnimatedVisibility.ANIM_TO_VISIBLE) mResultLayout.animate().cancel(); - if (mResultLayout.getVisibility() == View.VISIBLE) - return; +// if (mResultLayout.getVisibility() == View.VISIBLE) +// return; mResultLayout.setVisibility(View.VISIBLE); Log.d(TAG, "mResultLayout set VISIBLE (anim " + animate + ")"); if (animate) { From 923e94025b3c4ec67a7a7dbdb55010b330c0314c Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 29 Sep 2023 23:10:55 +0300 Subject: [PATCH 11/18] fade in result list only when first showing it --- .../java/rocks/tbog/tblauncher/Behaviour.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java index 0636f9015..2739d13b1 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java +++ b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java @@ -17,6 +17,7 @@ import android.os.Bundle; import android.provider.Settings; import android.text.Editable; +import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.View; @@ -148,7 +149,7 @@ public void afterTextChanged(Editable s) { String text = s.toString(); if (lastText.equals(text)) return; - if (text == null || text.isEmpty()) + if (TextUtils.isEmpty(text)) clearAdapter(); else updateSearchRecords(false, text); @@ -1077,11 +1078,9 @@ public void clearSearchText() { } public void clearSearch() { - if (mSearchEditText == null) - return; - - mSearchEditText.setText(""); + clearSearchText(); clearAdapter(); + updateClearButton(); } public void refreshSearchRecords() { @@ -1102,10 +1101,8 @@ private void showResultList(boolean animate) { Log.d(TAG, "showResultList (anim " + animate + ")"); if (TBApplication.state().getResultListVisibility() != LauncherState.AnimatedVisibility.ANIM_TO_VISIBLE) mResultLayout.animate().cancel(); -// if (mResultLayout.getVisibility() == View.VISIBLE) -// return; + mResultLayout.setVisibility(View.VISIBLE); - Log.d(TAG, "mResultLayout set VISIBLE (anim " + animate + ")"); if (animate) { TBApplication.state().setResultList(LauncherState.AnimatedVisibility.ANIM_TO_VISIBLE); mResultLayout.setAlpha(0f); @@ -1184,7 +1181,8 @@ private void updateSearchRecords(boolean isRefresh, @NonNull Searcher searcher) dismissPopup(); TBApplication.runTask(getContext(), searcher); - showResultList(true); + boolean animate = !TBApplication.state().isResultListVisible(); + showResultList(animate); } public void beforeLaunchOccurred() { From be9681be2c7c48a9f8381b74aa79b640c259cdc3 Mon Sep 17 00:00:00 2001 From: TBog Date: Sat, 30 Sep 2023 23:52:21 +0300 Subject: [PATCH 12/18] fix icon pack crash when lazy loading resources fails --- .../tblauncher/customicon/IconPackPage.java | 24 +++++++++++++++++-- .../tblauncher/customicon/IconViewHolder.java | 9 ++++++- .../tblauncher/icons/CalendarDrawable.java | 10 +++++++- .../tbog/tblauncher/icons/DrawableInfo.java | 4 ++++ .../tblauncher/icons/LazyLoadDrawable.java | 14 +++++++++++ .../tbog/tblauncher/icons/SimpleDrawable.java | 3 ++- 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconPackPage.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconPackPage.java index 63d9240d8..723b4add8 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconPackPage.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconPackPage.java @@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.View; import android.widget.BaseAdapter; import android.widget.GridView; @@ -14,6 +15,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.collection.ArraySet; import androidx.fragment.app.DialogFragment; import java.util.ArrayList; @@ -30,12 +32,14 @@ import rocks.tbog.tblauncher.utils.Utilities; class IconPackPage extends PageAdapter.Page { + private static final String TAG = IconPackPage.class.getSimpleName(); final ArrayList iconDataList = new ArrayList<>(); final String packageName; private ProgressBar mIconLoadingBar; private GridView mGridView; private TextView mSearch; private IconPackXML mIconPack = null; + private final ArraySet mInvalidDrawables = new ArraySet<>(0); IconPackPage(CharSequence name, String packPackageName, View view) { super(name, view); @@ -44,7 +48,7 @@ class IconPackPage extends PageAdapter.Page { @Override void setupView(@NonNull DialogFragment dialogFragment, @Nullable OnItemClickListener iconClickListener, @Nullable OnItemClickListener iconLongClickListener) { - Context context = dialogFragment.getContext(); + Context context = dialogFragment.requireContext(); mIconLoadingBar = pageView.findViewById(R.id.iconLoadingBar); Drawable packIcon = null; @@ -104,16 +108,30 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { void loadData() { super.loadData(); + final ArraySet invalidDrawables = new ArraySet<>(0); // load the new pack final IconPackXML pack = TBApplication.iconPackCache(pageView.getContext()).getIconPack(packageName); Utilities.runAsync((t) -> { Activity activity = Utilities.getActivity(pageView); - if (activity != null) + if (activity != null) { pack.loadDrawables(activity.getPackageManager()); + Collection drawables = pack.getDrawableList(); + for (DrawableInfo info : drawables) { + if (info.getDrawableResId(pack) == 0) { + invalidDrawables.add(info.getDrawableName()); + } + } + } }, (t) -> { Activity activity = Utilities.getActivity(pageView); if (activity != null) { mIconPack = pack; + mInvalidDrawables.clear(); + mInvalidDrawables.addAll(invalidDrawables); + int invalidDrawablesSize = mInvalidDrawables.size(); + if (invalidDrawablesSize > 0) { + Log.w(TAG, "icon pack `" + mIconPack.getPackPackageName() + "` has " + invalidDrawablesSize + " drawable(s) without resource id"); + } refreshList(); } else mIconPack = null; @@ -127,6 +145,8 @@ private void refreshList() { StringNormalizer.Result normalized = StringNormalizer.normalizeWithResult(mSearch.getText(), true); FuzzyScore fuzzyScore = new FuzzyScore(normalized.codePoints); for (DrawableInfo info : drawables) { + if (mInvalidDrawables.contains(info.getDrawableName())) + continue; if (fuzzyScore.match(info.getDrawableName()).match) iconDataList.add(new IconData(mIconPack, info)); } diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconViewHolder.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconViewHolder.java index 6bbc3b879..12877ec9c 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconViewHolder.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconViewHolder.java @@ -1,6 +1,7 @@ package rocks.tbog.tblauncher.customicon; import android.graphics.drawable.Drawable; +import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -31,6 +32,7 @@ protected void setContent(IconData content, int position, @NonNull ViewHolderAda } static class AsyncLoad extends AsyncTask { + private static final String TAG = AsyncLoad.class.getSimpleName(); private final WeakReference holder; protected AsyncLoad(IconViewHolder holder) { @@ -48,11 +50,16 @@ protected void onPreExecute() { @Override protected Drawable doInBackground(IconData iconData) { - return iconData.getIcon(); + Drawable drawable = iconData.getIcon(); + if (drawable == null) + Log.w(TAG, "drawable `" + iconData.drawableInfo.getDrawableName() + "` from icon pack `" + iconData.iconPack.getPackPackageName() + "` doesn't load"); + return drawable; } @Override protected void onPostExecute(Drawable drawable) { + if (drawable == null) + return; IconViewHolder h = holder.get(); if (h == null || h.loader != this) return; diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java b/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java index 374c731ae..c9bc35a66 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/CalendarDrawable.java @@ -23,12 +23,20 @@ protected CalendarDrawable(@NonNull String drawableName) { Arrays.fill(drawableIdCached, false); } + @SuppressLint("DiscouragedApi") + @Override + @DrawableRes + public int getDrawableResId(@NonNull IconPackXML iconPack) { + int dayOfMonthIdx = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1; + return getDayDrawableId(iconPack, dayOfMonthIdx); + } + @SuppressLint("DiscouragedApi") @DrawableRes private int getDayDrawableId(@NonNull IconPackXML iconPack, int dayOfMonthIdx) { Resources res = iconPack.getResources(); if (res == null) - return 0; + return drawableForDay[dayOfMonthIdx]; if (!drawableIdCached[dayOfMonthIdx]) { String drawableName = getDrawableName() + (1 + dayOfMonthIdx); drawableForDay[dayOfMonthIdx] = res.getIdentifier(drawableName, "drawable", iconPack.getPackPackageName()); diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java b/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java index adbd8fc02..273b27e7c 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/DrawableInfo.java @@ -3,6 +3,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -25,6 +26,9 @@ public boolean isDynamic() { return false; } + @DrawableRes + public abstract int getDrawableResId(@NonNull IconPackXML iconPack); + @Nullable public abstract Drawable getDrawable(@NonNull IconPackXML iconPack, @Nullable Resources.Theme theme); diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java b/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java index 21afa7d5c..c2080fb65 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/LazyLoadDrawable.java @@ -19,6 +19,20 @@ protected LazyLoadDrawable(@NonNull String drawableName) { super(drawableName); } + @SuppressLint("DiscouragedApi") + @Override + @DrawableRes + public int getDrawableResId(@NonNull IconPackXML iconPack) { + Resources res = iconPack.getResources(); + if (res == null) + return drawableId; + if (!drawableIdCached) { + drawableId = res.getIdentifier(getDrawableName(), "drawable", iconPack.getPackPackageName()); + drawableIdCached = true; + } + return drawableId; + } + @SuppressLint("DiscouragedApi") @Nullable @Override diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java b/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java index b95572d2e..b00ef0ddf 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/SimpleDrawable.java @@ -17,8 +17,9 @@ public SimpleDrawable(@NonNull String drawableName, @DrawableRes int drawableId) this.drawableId = drawableId; } + @Override @DrawableRes - public int getResourceId() { + public int getDrawableResId(@NonNull IconPackXML iconPack) { return drawableId; } From 3c60e394ef70b0a450b840d90ca466ca9a6f5a85 Mon Sep 17 00:00:00 2001 From: TBog Date: Thu, 29 Feb 2024 23:11:38 +0200 Subject: [PATCH 13/18] fix registerReceiver exception on Android 14 --- .../tbog/tblauncher/dataprovider/AppProvider.java | 6 ++++-- .../tblauncher/dataprovider/SearchProvider.java | 0 .../dataprovider/ShortcutsProvider.java | 4 +++- .../tblauncher/dataprovider/SimpleProvider.java | 0 .../tbog/tblauncher/entry/CalculatorEntry.java | 0 .../rocks/tbog/tblauncher/entry/OpenUrlEntry.java | 0 .../tbog/tblauncher/entry/SearchEngineEntry.java | 0 .../rocks/tbog/tblauncher/entry/SearchEntry.java | 0 .../tbog/tblauncher/handler/DataHandler.java | 15 +++++++++------ gradlew | 0 10 files changed, 16 insertions(+), 9 deletions(-) mode change 100755 => 100644 app/src/main/java/rocks/tbog/tblauncher/dataprovider/SearchProvider.java mode change 100755 => 100644 app/src/main/java/rocks/tbog/tblauncher/dataprovider/SimpleProvider.java mode change 100755 => 100644 app/src/main/java/rocks/tbog/tblauncher/entry/CalculatorEntry.java mode change 100755 => 100644 app/src/main/java/rocks/tbog/tblauncher/entry/OpenUrlEntry.java mode change 100755 => 100644 app/src/main/java/rocks/tbog/tblauncher/entry/SearchEngineEntry.java mode change 100755 => 100644 app/src/main/java/rocks/tbog/tblauncher/entry/SearchEntry.java mode change 100755 => 100644 gradlew diff --git a/app/src/main/java/rocks/tbog/tblauncher/dataprovider/AppProvider.java b/app/src/main/java/rocks/tbog/tblauncher/dataprovider/AppProvider.java index 711d32fd6..d8cb5d997 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/dataprovider/AppProvider.java +++ b/app/src/main/java/rocks/tbog/tblauncher/dataprovider/AppProvider.java @@ -12,6 +12,8 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.annotation.WorkerThread; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import java.util.ArrayList; import java.util.Objects; @@ -128,7 +130,7 @@ public void onCreate() { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); - registerReceiver(mProfileReceiver, filter); + ActivityCompat.registerReceiver(this, mProfileReceiver, filter, ContextCompat.RECEIVER_EXPORTED); } // Get notified when app changes on standard user profile @@ -146,7 +148,7 @@ public void onCreate() { } appChangedFilter.addDataScheme("package"); appChangedFilter.addDataScheme("file"); - this.registerReceiver(mPackageAddedRemovedHandler, appChangedFilter); + ActivityCompat.registerReceiver(this, mPackageAddedRemovedHandler, appChangedFilter, ContextCompat.RECEIVER_EXPORTED); super.onCreate(); } diff --git a/app/src/main/java/rocks/tbog/tblauncher/dataprovider/SearchProvider.java b/app/src/main/java/rocks/tbog/tblauncher/dataprovider/SearchProvider.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/rocks/tbog/tblauncher/dataprovider/ShortcutsProvider.java b/app/src/main/java/rocks/tbog/tblauncher/dataprovider/ShortcutsProvider.java index e6e22bc16..059f9a633 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/dataprovider/ShortcutsProvider.java +++ b/app/src/main/java/rocks/tbog/tblauncher/dataprovider/ShortcutsProvider.java @@ -18,6 +18,8 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.annotation.WorkerThread; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import java.util.List; @@ -169,7 +171,7 @@ public void onCreate() { filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); filter.addAction(ACTION_INSTALL_SHORTCUT); - registerReceiver(mProfileReceiver, filter); + ActivityCompat.registerReceiver(this, mProfileReceiver, filter, ContextCompat.RECEIVER_EXPORTED); } super.onCreate(); diff --git a/app/src/main/java/rocks/tbog/tblauncher/dataprovider/SimpleProvider.java b/app/src/main/java/rocks/tbog/tblauncher/dataprovider/SimpleProvider.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/CalculatorEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/CalculatorEntry.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/OpenUrlEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/OpenUrlEntry.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/SearchEngineEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/SearchEngineEntry.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/SearchEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/SearchEntry.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java b/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java index 7b270083a..922bc0a04 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java +++ b/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java @@ -19,6 +19,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import java.util.ArrayDeque; @@ -121,7 +123,8 @@ public DataHandler(Context ctx) { mTimer.start(); IntentFilter intentFilter = new IntentFilter(TBLauncherActivity.LOAD_OVER); - ctx.registerReceiver(this, intentFilter); + + ActivityCompat.registerReceiver(ctx, this, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED); Intent i = new Intent(TBLauncherActivity.START_LOAD); ctx.sendBroadcast(i); @@ -361,7 +364,7 @@ private boolean startService(Context context, Intent intent, String name, int co IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SCREEN_ON); intentFilter.addAction(Intent.ACTION_USER_PRESENT); - context.registerReceiver(new BroadcastReceiver() { + ActivityCompat.registerReceiver(context, new BroadcastReceiver() { @Override public void onReceive(final Context context, Intent intent) { // Is there a lockscreen still visible to the user? @@ -385,7 +388,7 @@ public void run() { }, 10); } } - }, intentFilter); + }, intentFilter, ContextCompat.RECEIVER_EXPORTED); // Stop here for now, the Receiver will re-trigger the whole flow when services can be started. return false; @@ -951,7 +954,7 @@ public void onProviderRecreated(Provider provider) { final Context context = getContext(); IntentFilter intentFilter = new IntentFilter(TBLauncherActivity.LOAD_OVER); - context.registerReceiver(this, intentFilter); + ActivityCompat.registerReceiver(context, this, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED); Intent i = new Intent(TBLauncherActivity.START_LOAD); context.sendBroadcast(i); @@ -978,7 +981,7 @@ public void reloadProviders(int loadStep) { mTimer.start(); IntentFilter intentFilter = new IntentFilter(TBLauncherActivity.LOAD_OVER); - context.registerReceiver(this, intentFilter); + ActivityCompat.registerReceiver(context, this, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED); Intent i = new Intent(TBLauncherActivity.START_LOAD); context.sendBroadcast(i); @@ -999,7 +1002,7 @@ public void reloadProviders() { mTimer.start(); IntentFilter intentFilter = new IntentFilter(TBLauncherActivity.LOAD_OVER); - context.registerReceiver(this, intentFilter); + ActivityCompat.registerReceiver(context, this, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED); Intent i = new Intent(TBLauncherActivity.START_LOAD); context.sendBroadcast(i); diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 From e48d451fe160a39984596ba7e04ded0d462716f4 Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 19 Apr 2024 14:10:26 +0300 Subject: [PATCH 14/18] fix stall on application resume call setRequestedOrientation in Activity onCreate --- app/src/main/java/rocks/tbog/tblauncher/Behaviour.java | 8 ++++---- .../main/java/rocks/tbog/tblauncher/SettingsActivity.java | 1 + .../java/rocks/tbog/tblauncher/TBLauncherActivity.java | 7 ++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java index b828d4429..798a6e5e6 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java +++ b/app/src/main/java/rocks/tbog/tblauncher/Behaviour.java @@ -1624,14 +1624,14 @@ else if (TBApplication.state().isResultListVisible()) { } } - public void setActivityOrientation(@NonNull Activity act) { - if (mPref.getBoolean("lock-portrait", true)) { - if (mPref.getBoolean("sensor-orientation", true)) + public static void setActivityOrientation(@NonNull Activity act, @NonNull SharedPreferences pref) { + if (pref.getBoolean("lock-portrait", true)) { + if (pref.getBoolean("sensor-orientation", true)) act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); else act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); } else { - if (mPref.getBoolean("sensor-orientation", true)) + if (pref.getBoolean("sensor-orientation", true)) act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); else act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); diff --git a/app/src/main/java/rocks/tbog/tblauncher/SettingsActivity.java b/app/src/main/java/rocks/tbog/tblauncher/SettingsActivity.java index 7fb00e19c..3d31df225 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/SettingsActivity.java +++ b/app/src/main/java/rocks/tbog/tblauncher/SettingsActivity.java @@ -98,6 +98,7 @@ public class SettingsActivity extends AppCompatActivity implements PreferenceFra "search-bar-height", "search-bar-text-size", "search-bar-radius", "search-bar-gradient", "search-bar-at-bottom", "search-bar-argb", "search-bar-text-color", "search-bar-icon-color", "search-bar-ripple-color", "search-bar-cursor-argb", "enable-suggestions-keyboard", + "lock-portrait", "sensor-orientation", "search-bar-layout", "quick-list-position" )); private final static ArraySet PREF_LISTS_WITH_DEPENDENCY = new ArraySet<>(Arrays.asList( diff --git a/app/src/main/java/rocks/tbog/tblauncher/TBLauncherActivity.java b/app/src/main/java/rocks/tbog/tblauncher/TBLauncherActivity.java index 6a51e5236..8b90ae648 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/TBLauncherActivity.java +++ b/app/src/main/java/rocks/tbog/tblauncher/TBLauncherActivity.java @@ -109,7 +109,8 @@ protected void onCreate(Bundle savedInstanceState) { * Initialize preferences */ PreferenceManager.setDefaultValues(this, R.xml.preferences, false); - //prefs = PreferenceManager.getDefaultSharedPreferences(this); + var prefs = PreferenceManager.getDefaultSharedPreferences(this); + Behaviour.setActivityOrientation(this, prefs); /* * Permission Manager @@ -157,8 +158,6 @@ protected void onStart() { Log.d(TAG, "onStart(" + this + ")"); super.onStart(); - behaviour.setActivityOrientation(this); - if (DebugInfo.providerStatus(this)) { debugTextView.setVisibility(View.VISIBLE); } @@ -178,8 +177,6 @@ protected void onStop() { protected void onRestart() { Log.d(TAG, "onRestart(" + this + ")"); super.onRestart(); - - behaviour.setActivityOrientation(this); } @Override From b5e5389a9dd9b00a1e6c0340efc030c5ecf313c7 Mon Sep 17 00:00:00 2001 From: TBog Date: Tue, 19 Dec 2023 12:01:42 +0200 Subject: [PATCH 15/18] fix app slow start --- .../java/rocks/tbog/tblauncher/handler/AppsHandler.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/handler/AppsHandler.java b/app/src/main/java/rocks/tbog/tblauncher/handler/AppsHandler.java index fcb20b3b0..b27a2c069 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/handler/AppsHandler.java +++ b/app/src/main/java/rocks/tbog/tblauncher/handler/AppsHandler.java @@ -94,9 +94,11 @@ public void runWhenLoaded(@NonNull Runnable task) { @WorkerThread public static void setTagsForApps(@NonNull Collection apps, @NonNull TagsHandler tagsHandler) { tagsHandler.runWhenLoaded(() -> { - Log.d(TAG, "set " + apps.size() + " cached app(s) tags"); - for (AppEntry appEntry : apps) - appEntry.setTags(tagsHandler.getTags(appEntry.id)); + Utilities.runAsync(() -> { + Log.d(TAG, "set " + apps.size() + " cached app(s) tags"); + for (AppEntry appEntry : apps) + appEntry.setTags(tagsHandler.getTags(appEntry.id)); + }); }); } From d1dda7019cedecb844e30fba8cad72681c071ab0 Mon Sep 17 00:00:00 2001 From: TBog Date: Fri, 19 Apr 2024 14:52:52 +0300 Subject: [PATCH 16/18] fix leaked open resources --- .../customicon/IconSelectDialog.java | 3 +- .../rocks/tbog/tblauncher/db/DBHelper.java | 8 +- .../tbog/tblauncher/entry/ContactEntry.java | 7 +- .../tbog/tblauncher/icons/IconPackXML.java | 74 ++++++++++++++----- 4 files changed, 65 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java index 010206c6b..a2516ccfe 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java +++ b/app/src/main/java/rocks/tbog/tblauncher/customicon/IconSelectDialog.java @@ -72,8 +72,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { Uri imageUri = data != null ? data.getData() : null; if (resultCode == Activity.RESULT_OK && imageUri != null) { Drawable imageDrawable; - try { - InputStream is = context.getContentResolver().openInputStream(imageUri); + try (InputStream is = context.getContentResolver().openInputStream(imageUri)) { imageDrawable = Drawable.createFromStream(is, imageUri.toString()); } catch (Throwable ignore) { imageDrawable = null; diff --git a/app/src/main/java/rocks/tbog/tblauncher/db/DBHelper.java b/app/src/main/java/rocks/tbog/tblauncher/db/DBHelper.java index 30d4cdb10..7d25b77fb 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/db/DBHelper.java +++ b/app/src/main/java/rocks/tbog/tblauncher/db/DBHelper.java @@ -40,10 +40,12 @@ private DBHelper() { } private static SQLiteDatabase getDatabase(Context context) { - if (database == null) { - database = new DB(context); + synchronized (DBHelper.class) { + if (database == null) { + database = new DB(context); + } + return database.getReadableDatabase(); } - return database.getReadableDatabase(); } private static ArrayList readCursor(Cursor cursor) { diff --git a/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java b/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java index 1a73dae07..14dc2390f 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java +++ b/app/src/main/java/rocks/tbog/tblauncher/entry/ContactEntry.java @@ -26,7 +26,7 @@ import androidx.core.content.res.ResourcesCompat; import androidx.preference.PreferenceManager; -import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import rocks.tbog.tblauncher.BuildConfig; @@ -139,10 +139,9 @@ public int getResultLayout(int drawFlags) { protected Drawable getIconDrawable(Context ctx) { Drawable drawable = null; if (iconUri != null) - try { - InputStream inputStream = ctx.getContentResolver().openInputStream(iconUri); + try (InputStream inputStream = ctx.getContentResolver().openInputStream(iconUri)) { drawable = Drawable.createFromStream(inputStream, iconUri.toString()); - } catch (FileNotFoundException ignored) { + } catch (IOException ignored) { } if (drawable == null) { drawable = AppCompatResources.getDrawable(ctx, R.drawable.ic_contact_placeholder); diff --git a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java index 0043b6c56..b952bf841 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java +++ b/app/src/main/java/rocks/tbog/tblauncher/icons/IconPackXML.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; @@ -18,6 +19,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -229,8 +231,9 @@ private BitmapDrawable generateBitmap(@NonNull BitmapDrawable defaultBitmap) { @SuppressLint("DiscouragedApi") private void parseDrawableXML() { - XmlPullParser xpp = null; + XmlResourceParser xpp = null; // search drawable.xml into icons pack apk resource folder + @SuppressLint("DiscouragedApi") int drawableXmlId = packResources.getIdentifier("drawable", "xml", iconPackPackageName); if (drawableXmlId > 0) { xpp = packResources.getXml(drawableXmlId); @@ -264,29 +267,50 @@ private void parseDrawableXML() { } } catch (XmlPullParserException | IOException e) { Log.e(TAG, "parsing drawable.xml", e); + } finally { + xpp.close(); } } - @SuppressLint("DiscouragedApi") - @Nullable - private XmlPullParser findAppFilterXml() throws XmlPullParserException { + @NonNull + private Pair findAppFilterXml() throws XmlPullParserException { + XmlPullParser parser = null; + InputStream inputStream = null; // search appfilter.xml in icon pack's apk resource folder for xml files - int appFilterIdXml = packResources.getIdentifier("appfilter", "xml", iconPackPackageName); - if (appFilterIdXml > 0) { - return packResources.getXml(appFilterIdXml); + try { + inputStream = packResources.getAssets().open("appfilter.xml"); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + parser = factory.newPullParser(); + parser.setInput(inputStream, "UTF-8"); + } catch (Exception e) { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ignored) { + } + } + inputStream = null; + // Catch any exception since we want to fall back to parsing the xml/resource in all cases + @SuppressLint("DiscouragedApi") + int appFilterIdXml = packResources.getIdentifier("appfilter", "xml", iconPackPackageName); + if (appFilterIdXml > 0) { + parser = packResources.getXml(appFilterIdXml); + } } - // search appfilter.xml in icon pack's apk resource folder for raw files (supporting icon pack studio) - int appFilterIdRaw = packResources.getIdentifier("appfilter", "raw", iconPackPackageName); - if (appFilterIdRaw > 0) { - InputStream input = packResources.openRawResource(appFilterIdRaw); - XmlPullParserFactory xppf = XmlPullParserFactory.newInstance(); - XmlPullParser xpp = xppf.newPullParser(); - xpp.setInput(input, "UTF-8"); - return xpp; + if (parser == null) { + // search appfilter.xml in icon pack's apk resource folder for raw files (supporting icon pack studio) + @SuppressLint("DiscouragedApi") + int appFilterIdRaw = packResources.getIdentifier("appfilter", "raw", iconPackPackageName); + if (appFilterIdRaw > 0) { + inputStream = packResources.openRawResource(appFilterIdRaw); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + parser = factory.newPullParser(); + parser.setInput(inputStream, "UTF-8"); + } } - return null; + return Pair.create(parser, inputStream); } @SuppressLint("DiscouragedApi") @@ -294,9 +318,13 @@ private void parseAppFilterXML() { if (packResources == null) return; + XmlPullParser xpp = null; + InputStream inputStream = null; Map calendarDrawablesByPrefix = new ArrayMap<>(0); try { - XmlPullParser xpp = findAppFilterXml(); + var appFilterXml = findAppFilterXml(); + xpp = appFilterXml.first; + inputStream = appFilterXml.second; if (xpp != null) { int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { @@ -392,7 +420,17 @@ private void parseAppFilterXML() { } } } catch (Exception e) { - Log.e(TAG, "parsing appfilter.xml ", e); + Log.e(TAG, "Error parsing appfilter.xml ", e); + } finally { + if (xpp instanceof XmlResourceParser) { + ((XmlResourceParser) xpp).close(); + } else if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + Log.e(TAG, "Error closing appfilter.xml ", e); + } + } } } From 28b1d2121dafbeb3fb6ca84f97befb0aad2eac4f Mon Sep 17 00:00:00 2001 From: TBog Date: Sat, 20 Apr 2024 20:10:09 +0300 Subject: [PATCH 17/18] rename DataHandler context to application --- .../rocks/tbog/tblauncher/handler/DataHandler.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java b/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java index 922bc0a04..8d902bc3c 100644 --- a/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java +++ b/app/src/main/java/rocks/tbog/tblauncher/handler/DataHandler.java @@ -1,5 +1,6 @@ package rocks.tbog.tblauncher.handler; +import android.app.Application; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -104,7 +105,7 @@ public class DataHandler extends BroadcastReceiver ); @NonNull - private final Context context; + private final Application mApplication; private String currentQuery; private final Map providers = new LinkedHashMap<>(); // preserve insert order private boolean mFullLoadOverSent = false; @@ -114,11 +115,12 @@ public class DataHandler extends BroadcastReceiver /** * Initialize all providers */ - public DataHandler(Context ctx) { + public DataHandler(@NonNull Application app) { // Make sure we are in the context of the main application // (otherwise we might receive an exception about broadcast listeners not being able // to bind to services) - context = ctx.getApplicationContext(); + mApplication = app; + Context ctx = app.getApplicationContext(); mTimer.start(); @@ -130,7 +132,7 @@ public DataHandler(Context ctx) { ctx.sendBroadcast(i); // Monitor changes for service preferences (to automatically start and stop services) - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); prefs.registerOnSharedPreferenceChangeListener(this); // add DB providers @@ -161,7 +163,7 @@ public DataHandler(Context ctx) { @NonNull public Context getContext() { - return context; + return mApplication.getApplicationContext(); } /* @@ -170,6 +172,7 @@ public Context getContext() { * to them dynamically. */ private void basicProviders() { + Context context = mApplication; // Filters { ProviderEntry providerEntry = new ProviderEntry(); From f1c4a17feb1f8dd540d889b8364d539dbb130713 Mon Sep 17 00:00:00 2001 From: TBog Date: Thu, 25 Apr 2024 16:37:05 +0300 Subject: [PATCH 18/18] Feature: share text cherrypicked from https://github.com/Neamar/KISS/pull/2111 --- app/src/main/AndroidManifest.xml | 5 +++++ .../main/java/rocks/tbog/tblauncher/Behaviour.java | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2169fe3c3..f797ab16a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -67,6 +67,11 @@ + + + + + 0) { + mSearchEditText.setText(sharedText); + return; + } else { + //Toast.makeText(this, R.string.shared_text_empty, Toast.LENGTH_SHORT).show(); + } + } } executeButtonAction("button-home");