diff --git a/app/src/main/java/com/dkanada/icecons/activities/IconActivity.java b/app/src/main/java/com/dkanada/icecons/activities/IconActivity.java index bb8fd1f08..bbbc6b6b0 100644 --- a/app/src/main/java/com/dkanada/icecons/activities/IconActivity.java +++ b/app/src/main/java/com/dkanada/icecons/activities/IconActivity.java @@ -1,80 +1,67 @@ package com.dkanada.icecons.activities; -import android.graphics.Rect; import android.os.Bundle; -import android.view.Gravity; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; +import android.text.Editable; +import android.text.TextWatcher; +import android.widget.EditText; +import android.widget.GridView; import com.dkanada.icecons.R; -import com.dkanada.icecons.utils.ImageUtils; -import com.dkanada.icecons.utils.ScreenUtils; +import com.dkanada.icecons.adapters.IconAdapter; import java.util.ArrayList; +import java.util.Collections; public class IconActivity extends BaseActivity { - private final ArrayList layoutList = new ArrayList<>(); - private final ArrayList imageList = new ArrayList<>(); + private final IconAdapter mAdapter = new IconAdapter(R.layout.grid_item); + private String[] mImages; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (ScreenUtils.isPortrait(getApplicationContext())) { - createLayout(7); - } else { - createLayout(12); - } - } - - private void createLayout(int width) { - float scale = ScreenUtils.densityScale(getApplicationContext()); - int margin = 16 * Math.round(scale); - - LinearLayout.LayoutParams baseParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1f); + setContentView(R.layout.icon_grid); - ScrollView baseScroller = new ScrollView(this); - baseScroller.setLayoutParams(baseParams); - baseScroller.setBackgroundColor(getResources().getColor(R.color.colorPrimary)); - setContentView(baseScroller); - baseScroller.setVisibility(View.VISIBLE); + mImages = getResources().getStringArray(R.array.icon_pack); - // display width hack - Rect windowRect = new Rect(); - baseScroller.getWindowVisibleDisplayFrame(windowRect); - int windowWidth = windowRect.right - windowRect.left; - - LinearLayout baseLayout = new LinearLayout(this); - baseLayout.setOrientation(LinearLayout.VERTICAL); - baseLayout.setLayoutParams(layoutParams); - baseLayout.setPadding(margin, margin, 0, 0); - baseScroller.addView(baseLayout); + EditText searchBar = findViewById(R.id.searchBar); + searchBar.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + } - String[] images = getResources().getStringArray(R.array.icon_pack); - for (int i = 0; i < images.length; i++) { - if (i % width == 0) { - layoutList.add((i / width), new LinearLayout(this)); - layoutList.get(i / width).setOrientation(LinearLayout.HORIZONTAL); - layoutList.get(i / width).setGravity(Gravity.START); - layoutList.get(i / width).setLayoutParams(containerParams); + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + mAdapter.clearList(); + if (charSequence.length() == 0) { + mAdapter.newLoadAsyncList(() -> { + ArrayList imageList = new ArrayList<>(mImages.length); + Collections.addAll(imageList, mImages); + return imageList; + }).execute(); + } else { + String searchString = charSequence.toString().toLowerCase(); + mAdapter.newLoadAsyncList(() -> { + ArrayList imageList = new ArrayList<>(mImages.length); + for (String s : mImages) { + if (s.contains(searchString)) { + imageList.add(s); + } + } + return imageList; + }).execute(); + } + } - baseLayout.addView(layoutList.get(i / width)); + @Override + public void afterTextChanged(Editable editable) { } - imageList.add(i, new ImageView(this)); - imageList.get(i).setLayoutParams(imageParams); - imageList.get(i).setScaleType(ImageView.ScaleType.FIT_XY); - imageList.get(i).setPadding(0, 0, margin, margin); - imageList.get(i).setAdjustViewBounds(true); + }); - final int resId = getResources().getIdentifier(images[i], "drawable", getPackageName()); - ImageUtils.bitmapLoadAsync(imageList.get(i), getApplicationContext().getResources(), resId, (windowWidth / width) - (margin * width + margin) / width, (windowWidth / width) - (margin * width + margin) / width); + GridView gridView = findViewById(R.id.iconGrid); + gridView.setAdapter(mAdapter); - layoutList.get(i / width).addView(imageList.get(i)); - } + // call onTextChanged + searchBar.setText(null); } } diff --git a/app/src/main/java/com/dkanada/icecons/adapters/IconAdapter.java b/app/src/main/java/com/dkanada/icecons/adapters/IconAdapter.java new file mode 100644 index 000000000..ea45cb26a --- /dev/null +++ b/app/src/main/java/com/dkanada/icecons/adapters/IconAdapter.java @@ -0,0 +1,9 @@ +package com.dkanada.icecons.adapters; + +import java.util.ArrayList; + +public class IconAdapter extends ViewHolderListAdapter { + public IconAdapter(int listItemLayout) { + super(IconViewHolder.class, listItemLayout, new ArrayList<>(0)); + } +} diff --git a/app/src/main/java/com/dkanada/icecons/adapters/IconViewHolder.java b/app/src/main/java/com/dkanada/icecons/adapters/IconViewHolder.java new file mode 100644 index 000000000..d1242223f --- /dev/null +++ b/app/src/main/java/com/dkanada/icecons/adapters/IconViewHolder.java @@ -0,0 +1,77 @@ +package com.dkanada.icecons.adapters; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.view.View; +import android.widget.ImageView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.core.content.res.ResourcesCompat; + +import java.lang.ref.WeakReference; + +public class IconViewHolder extends ViewHolderAdapter.ViewHolder { + private final ImageView icon; + private AsyncTask asyncLoad = null; + private final static View.OnLongClickListener longClickListener = v -> { + CharSequence content = null; + if (v instanceof ImageView) + content = v.getContentDescription(); + if (content != null) + Toast.makeText(v.getContext(), content, Toast.LENGTH_SHORT).show(); + return true; + }; + + public IconViewHolder(View view) { + super(view); + icon = (ImageView) view; + } + + @Override + protected void setContent(String content, int position, @NonNull ViewHolderAdapter> adapter) { + //IconAdapter iconAdapter = (IconAdapter) adapter; + if (asyncLoad != null) + asyncLoad.cancel(false); + + icon.setContentDescription(content); + icon.setOnLongClickListener(longClickListener); + icon.animate().cancel(); + icon.setAlpha(0f); + + asyncLoad = new AsyncLoad(this).execute(content); + } + + private static class AsyncLoad extends AsyncTask { + private final WeakReference weakHolder; + + public AsyncLoad(IconViewHolder holder) { + super(); + weakHolder = new WeakReference<>(holder); + } + + @Override + protected Drawable doInBackground(String... strings) { + IconViewHolder holder = weakHolder.get(); + if (holder == null || strings.length == 0) + return null; + String resIdName = strings[0]; + Context ctx = holder.icon.getContext(); + final int resId = ctx.getResources().getIdentifier(resIdName, "drawable", ctx.getPackageName()); + return ResourcesCompat.getDrawable(ctx.getResources(), resId, null); + } + + @Override + protected void onPostExecute(Drawable drawable) { + IconViewHolder holder = weakHolder.get(); + if (holder != null && this.equals(holder.asyncLoad)) { + holder.asyncLoad = null; + holder.icon.setImageDrawable(drawable); + holder.icon.animate() + .alpha(1f) + .setDuration(1000); + } + } + } +} diff --git a/app/src/main/java/com/dkanada/icecons/adapters/ViewHolderAdapter.java b/app/src/main/java/com/dkanada/icecons/adapters/ViewHolderAdapter.java new file mode 100644 index 000000000..53d2f0e07 --- /dev/null +++ b/app/src/main/java/com/dkanada/icecons/adapters/ViewHolderAdapter.java @@ -0,0 +1,130 @@ +package com.dkanada.icecons.adapters; + +import android.os.AsyncTask; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.dkanada.icecons.BuildConfig; + +import java.util.Collection; + +/** + * Adapter class that implements the View holder pattern. + * The ViewHolder is held as a tag in the list item view. + * + * @param Type of data to send to the ViewHolder + * @param ViewHolder class + */ +public abstract class ViewHolderAdapter> extends BaseAdapter { + @NonNull + final Class mViewHolderClass; + @LayoutRes + final int mListItemLayout; + + protected ViewHolderAdapter(@NonNull Class viewHolderClass, @LayoutRes int listItemLayout) { + mViewHolderClass = viewHolderClass; + mListItemLayout = listItemLayout; + } + + @LayoutRes + protected int getItemViewTypeLayout(int viewType) { + return mListItemLayout; + } + + @Override + public abstract T getItem(int position); + + @Override + public long getItemId(int position) { + return getItem(position).hashCode(); + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Nullable + protected VH getNewViewHolder(View view) { + VH holder = null; + try { + holder = mViewHolderClass.getDeclaredConstructor(View.class).newInstance(view); + } catch (Exception e) { + Log.e("VHA", "ViewHolder can't be instantiated (make sure class and constructor are public)", e); + } + return holder; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View view; + if (convertView == null) { + int viewType = getItemViewType(position); + if (BuildConfig.DEBUG) { + int viewTypeCount = getViewTypeCount(); + if (viewType >= viewTypeCount) + throw new IllegalStateException("ViewType " + viewType + " >= ViewTypeCount " + viewTypeCount); + } + @LayoutRes + int itemLayout = getItemViewTypeLayout(viewType); + view = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false); + } else { + view = convertView; + } + + Object tag = view.getTag(); + VH holder = mViewHolderClass.isInstance(tag) ? mViewHolderClass.cast(tag) : getNewViewHolder(view); + if (holder != null) { + T content = getItem(position); + holder.setContent(content, position, this); + } + return view; + + } + + public static abstract class ViewHolder { + protected ViewHolder(View view) { + view.setTag(this); + } + + protected abstract void setContent(T content, int position, @NonNull ViewHolderAdapter> adapter); + } + + public static abstract class LoadAsyncData extends AsyncTask> { + private final ViewHolderAdapter> adapter; + private final LoadInBackground task; + + public interface LoadInBackground { + @Nullable + Collection loadInBackground(); + } + + public LoadAsyncData(@NonNull ViewHolderAdapter> adapter, @NonNull LoadInBackground loadInBackground) { + super(); + this.adapter = adapter; + task = loadInBackground; + } + + @Override + protected Collection doInBackground(Void... voids) { + return task.loadInBackground(); + } + + @Override + protected void onPostExecute(Collection data) { + if (data == null) + return; + //adapter.addAll(data); + onDataLoadFinished(adapter, data); + } + + protected abstract void onDataLoadFinished(@NonNull ViewHolderAdapter> adapter, @NonNull Collection data); + } +} diff --git a/app/src/main/java/com/dkanada/icecons/adapters/ViewHolderListAdapter.java b/app/src/main/java/com/dkanada/icecons/adapters/ViewHolderListAdapter.java new file mode 100644 index 000000000..41ed2a5c2 --- /dev/null +++ b/app/src/main/java/com/dkanada/icecons/adapters/ViewHolderListAdapter.java @@ -0,0 +1,81 @@ +package com.dkanada.icecons.adapters; + +import android.util.Log; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Collection; +import java.util.List; + +public abstract class ViewHolderListAdapter> extends ViewHolderAdapter { + @NonNull + protected final List mList; + + protected ViewHolderListAdapter(@NonNull Class viewHolderClass, int listItemLayout, @NonNull List list) { + super(viewHolderClass, listItemLayout); + mList = list; + } + + @LayoutRes + protected int getItemViewTypeLayout(int viewType) { + return mListItemLayout; + } + + @Override + public T getItem(int position) { + return mList.get(position); + } + + @Override + public int getCount() { + return mList.size(); + } + + public void addItems(Collection items) { + mList.addAll(items); + notifyDataSetChanged(); + } + + public void addItem(T item) { + mList.add(item); + notifyDataSetChanged(); + } + + public void clearList() { + mList.clear(); + notifyDataSetChanged(); + } + + @Nullable + public LoadAsyncList newLoadAsyncList(@NonNull Class> loadAsyncClass, @NonNull LoadAsyncData.LoadInBackground loadInBackground) { + LoadAsyncList loadAsync = null; + try { + loadAsync = loadAsyncClass.getDeclaredConstructor(this.getClass(), loadInBackground.getClass()).newInstance(this, loadInBackground); + } catch (Exception e) { + Log.e("VHLA", "LoadAsync can't be instantiated (make sure class and constructor are public)", e); + } + return loadAsync; + } + + @NonNull + public LoadAsyncList newLoadAsyncList(@NonNull LoadAsyncData.LoadInBackground loadInBackground) { + return new LoadAsyncList<>(this, loadInBackground); + } + + public static class LoadAsyncList extends LoadAsyncData { + + public LoadAsyncList(@NonNull ViewHolderListAdapter> adapter, @NonNull LoadInBackground loadInBackground) { + super(adapter, loadInBackground); + } + + @Override + protected void onDataLoadFinished(@NonNull ViewHolderAdapter> adapter, @NonNull Collection data) { + ViewHolderListAdapter> listAdapter; + //noinspection unchecked + listAdapter = (ViewHolderListAdapter>) adapter; + listAdapter.addItems(data); + } + } +} diff --git a/app/src/main/res/layout/grid_item.xml b/app/src/main/res/layout/grid_item.xml new file mode 100644 index 000000000..0ec4ec5ca --- /dev/null +++ b/app/src/main/res/layout/grid_item.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/icon_grid.xml b/app/src/main/res/layout/icon_grid.xml new file mode 100644 index 000000000..5df19ceac --- /dev/null +++ b/app/src/main/res/layout/icon_grid.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000..c32bf9882 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 48dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa2716ab2..e69b2963d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,5 +12,6 @@ https://github.com/dkanada/frost https://choosealicense.com/licenses/gpl-3.0 https://creativecommons.org/licenses/by-sa/4.0 + Filter icons true \ No newline at end of file