Pick icon from pack
This commit is contained in:
parent
0352d9c777
commit
c100cd3cfb
@ -146,7 +146,7 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
try {
|
||||
if(doc == null) return;
|
||||
IconPackMetadata meta = IconUtil.importIconPack(this, doc);
|
||||
IconPackMetadata meta = IconUtil.importIconPack(this, doc); // TODO: check if pack contains icons
|
||||
DialogUtil.showErrorDialog(this, "Icon pack contains " + meta.getIcons().length + " icons");
|
||||
} catch (IconPackException e) {
|
||||
DialogUtil.showErrorDialog(this, "Failed to import icon pack", e);
|
||||
|
@ -78,14 +78,13 @@ public class EditOTPFragment extends NamedFragment {
|
||||
binding.getRoot().setBackgroundResource(bg);
|
||||
}
|
||||
|
||||
IconUtil.loadEffectiveImage(requireContext(), data, binding.inputImage, null);
|
||||
binding.inputImage.setOnClickListener(v -> {
|
||||
new StyledDialogBuilder(requireContext())
|
||||
.setTitle("Choose Image")
|
||||
.setItems(new String[]{"Image from icon pack", "Image from gallery", "No image", "Reset to default image"}, (d, which) -> {
|
||||
switch(which) {
|
||||
case 0:
|
||||
// TODO: pick from icon pack
|
||||
pickImageFromIconPack();
|
||||
break;
|
||||
case 1:
|
||||
pickGalleryImage();
|
||||
@ -143,6 +142,7 @@ public class EditOTPFragment extends NamedFragment {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if(imageData != null && !imageData.equals(OTPData.IMAGE_DATA_NONE)) return;
|
||||
updateImage();
|
||||
}
|
||||
};
|
||||
@ -179,14 +179,23 @@ public class EditOTPFragment extends NamedFragment {
|
||||
}
|
||||
}
|
||||
|
||||
updateImage();
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void updateImage() {
|
||||
if(imageData != null && !imageData.equals(OTPData.IMAGE_DATA_NONE)) return;
|
||||
IconUtil.loadEffectiveImage(requireContext(), imageData, binding.inputIssuer.getText().toString(), binding.inputName.getText().toString(), binding.inputImage, null);
|
||||
}
|
||||
|
||||
private void pickImageFromIconPack() {
|
||||
// TODO: check if icon packs installed
|
||||
new PickIconDrawerFragment(icon -> {
|
||||
imageData = Base64.encodeToString(icon.getBytes(), Base64.DEFAULT);
|
||||
updateImage();
|
||||
}).show(requireActivity().getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
private void pickGalleryImage() {
|
||||
((MainActivity) requireActivity()).promptPickIconImage(uri -> {
|
||||
if(uri == null) return;
|
||||
|
@ -0,0 +1,59 @@
|
||||
package com.cringe_studios.cringe_authenticator.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.databinding.FragmentPickIconBinding;
|
||||
import com.cringe_studios.cringe_authenticator.icon.Icon;
|
||||
import com.cringe_studios.cringe_authenticator.icon.IconListAdapter;
|
||||
import com.cringe_studios.cringe_authenticator.icon.IconUtil;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
public class PickIconDrawerFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private FragmentPickIconBinding binding;
|
||||
|
||||
private Consumer<Icon> selected;
|
||||
|
||||
public PickIconDrawerFragment(Consumer<Icon> selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
binding = FragmentPickIconBinding.inflate(inflater);
|
||||
|
||||
IconListAdapter adapter = new IconListAdapter(requireContext(), IconUtil.loadAllIcons(requireContext()), icon -> {
|
||||
selected.accept(icon);
|
||||
getParentFragmentManager().beginTransaction().remove(this).commit();
|
||||
});
|
||||
|
||||
binding.pickIconSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
adapter.filter(s.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
binding.pickIconList.setAdapter(adapter);
|
||||
binding.pickIconList.setOnChildClickListener(adapter);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package com.cringe_studios.cringe_authenticator.icon;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseExpandableListAdapter;
|
||||
import android.widget.ExpandableListView;
|
||||
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.R;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.IconListCategoryBinding;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.IconListIconBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class IconListAdapter extends BaseExpandableListAdapter implements ExpandableListView.OnChildClickListener {
|
||||
|
||||
private Context context;
|
||||
|
||||
private Map<String, List<Icon>> icons;
|
||||
private List<String> categories;
|
||||
|
||||
private Map<String, List<Icon>> filteredIcons;
|
||||
|
||||
private Consumer<Icon> selected;
|
||||
|
||||
public IconListAdapter(Context context, Map<String, List<Icon>> icons, Consumer<Icon> selected) {
|
||||
this.context = context;
|
||||
this.icons = icons;
|
||||
this.categories = new ArrayList<>(icons.keySet());
|
||||
this.filteredIcons = new TreeMap<>(icons);
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
public void filter(String query) {
|
||||
Map<String, List<Icon>> filtered = new TreeMap<>();
|
||||
for(String cat : categories) {
|
||||
List<Icon> f = new ArrayList<>();
|
||||
for(Icon i : icons.get(cat)) {
|
||||
if(i.getMetadata().getName().toLowerCase().contains(query.toLowerCase())) {
|
||||
f.add(i);
|
||||
}
|
||||
}
|
||||
filtered.put(cat, f);
|
||||
}
|
||||
|
||||
filteredIcons = filtered;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
return categories.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenCount(int groupPosition) {
|
||||
return filteredIcons.get(categories.get(groupPosition)).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroup(int groupPosition) {
|
||||
return categories.get(groupPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getChild(int groupPosition, int childPosition) {
|
||||
return filteredIcons.get(categories.get(groupPosition)).get(childPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupId(int groupPosition) {
|
||||
return groupPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChildId(int groupPosition, int childPosition) {
|
||||
return childPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
|
||||
IconListCategoryBinding binding = IconListCategoryBinding.inflate(LayoutInflater.from(context));
|
||||
binding.getRoot().setText((String) getGroup(groupPosition));
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
|
||||
IconListIconBinding binding = IconListIconBinding.inflate(LayoutInflater.from(context));
|
||||
|
||||
Icon icon = getChild(groupPosition, childPosition);
|
||||
binding.iconListIconImage.setImageResource(R.drawable.cringeauth_white);
|
||||
IconUtil.loadImage(binding.iconListIconImage, icon.getBytes(), v -> v.setImageDrawable(new ColorDrawable(Color.TRANSPARENT)));
|
||||
binding.iconListIconText.setText(icon.getMetadata().getName());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
|
||||
selected.accept(getChild(groupPosition, childPosition));
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,13 +1,24 @@
|
||||
package com.cringe_studios.cringe_authenticator.icon;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class IconMetadata {
|
||||
|
||||
private String name;
|
||||
private String filename;
|
||||
private String category;
|
||||
private String[] issuer;
|
||||
|
||||
private IconMetadata() {}
|
||||
|
||||
public String getName() {
|
||||
if(name != null) return name;
|
||||
|
||||
String fileName = new File(filename).getName();
|
||||
int i = fileName.lastIndexOf('.');
|
||||
return i == -1 ? fileName : fileName.substring(0, i);
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class IconPack {
|
||||
public Icon findIconForIssuer(String issuer) {
|
||||
for(Icon icon : icons) {
|
||||
for(String i : icon.getMetadata().getIssuer()) {
|
||||
if(issuer.equals(i)) {
|
||||
if(issuer.equalsIgnoreCase(i)) {
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
@ -121,6 +122,26 @@ public class IconUtil {
|
||||
throw new IconPackException("No pack.json");
|
||||
}
|
||||
|
||||
public static Map<String, List<Icon>> loadAllIcons(Context context) {
|
||||
List<IconPack> packs = loadAllIconPacks(context);
|
||||
|
||||
Map<String, List<Icon>> icons = new TreeMap<>();
|
||||
for(IconPack pack : packs) {
|
||||
for(Icon i : pack.getIcons()) {
|
||||
String category = i.getMetadata().getCategory();
|
||||
if(icons.containsKey(category)) {
|
||||
icons.get(category).add(i);
|
||||
}else {
|
||||
List<Icon> is = new ArrayList<>();
|
||||
is.add(i);
|
||||
icons.put(category, is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return icons;
|
||||
}
|
||||
|
||||
public static List<IconPack> loadAllIconPacks(Context context) {
|
||||
File iconPacksDir = getIconPacksDir(context);
|
||||
|
||||
@ -250,19 +271,24 @@ public class IconUtil {
|
||||
}
|
||||
}
|
||||
|
||||
loadImage(view, imageBytes, v -> v.setImageBitmap(IconUtil.generateCodeImage(issuer, name)));
|
||||
}
|
||||
|
||||
public static void loadImage(SVGImageView view, byte[] imageBytes, Consumer<SVGImageView> fallback) {
|
||||
if(imageBytes == null) {
|
||||
view.setImageBitmap(IconUtil.generateCodeImage(issuer, name));
|
||||
if(fallback != null) fallback.accept(view);
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap bm = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
|
||||
if(bm != null) {
|
||||
view.setImageBitmap(bm);
|
||||
}else {
|
||||
Bitmap bm = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
|
||||
if(bm != null) {
|
||||
view.setImageBitmap(bm);
|
||||
}else {
|
||||
try {
|
||||
SVG svg = SVG.getFromInputStream(new ByteArrayInputStream(imageBytes));
|
||||
view.setSVG(svg);
|
||||
}catch(SVGParseException e) {
|
||||
view.setImageBitmap(IconUtil.generateCodeImage(issuer, name));
|
||||
}
|
||||
try {
|
||||
SVG svg = SVG.getFromInputStream(new ByteArrayInputStream(imageBytes));
|
||||
view.setSVG(svg);
|
||||
}catch(SVGParseException e) {
|
||||
if(fallback != null) fallback.accept(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragment.HomeFragment">
|
||||
tools:context=".fragment.MenuDrawerFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
23
app/src/main/res/layout/fragment_pick_icon.xml
Normal file
23
app/src/main/res/layout/fragment_pick_icon.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/pick_icon_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints=""
|
||||
android:hint="Search" />
|
||||
|
||||
<ExpandableListView
|
||||
android:id="@+id/pick_icon_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nestedScrollingEnabled="true" />
|
||||
|
||||
</LinearLayout>
|
9
app/src/main/res/layout/icon_list_category.xml
Normal file
9
app/src/main/res/layout/icon_list_category.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingVertical="16dp"
|
||||
android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
|
||||
tools:text="This is a category">
|
||||
</TextView>
|
22
app/src/main/res/layout/icon_list_icon.xml
Normal file
22
app/src/main/res/layout/icon_list_icon.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingVertical="5dp"
|
||||
android:paddingStart="?android:attr/expandableListPreferredChildPaddingLeft">
|
||||
<com.caverock.androidsvg.SVGImageView
|
||||
android:id="@+id/icon_list_icon_image"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:src="@drawable/baseline_edit_24"
|
||||
android:layout_marginEnd="10dp"/>
|
||||
<TextView
|
||||
android:id="@+id/icon_list_icon_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="start|center"
|
||||
tools:text="This is an icon">
|
||||
</TextView>
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue
Block a user