Choices on duplicate icon pack
This commit is contained in:
parent
d25799719e
commit
596bd7bc08
@ -19,6 +19,7 @@ import androidx.core.util.Consumer;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.cringe_studios.code_guard.databinding.ActivityMainBinding;
|
||||
import com.cringe_studios.code_guard.databinding.DialogIconPackExistsBinding;
|
||||
import com.cringe_studios.code_guard.databinding.DialogInputCodeChoiceBinding;
|
||||
import com.cringe_studios.code_guard.fragment.AboutFragment;
|
||||
import com.cringe_studios.code_guard.fragment.EditOTPFragment;
|
||||
@ -26,6 +27,7 @@ import com.cringe_studios.code_guard.fragment.GroupFragment;
|
||||
import com.cringe_studios.code_guard.fragment.HomeFragment;
|
||||
import com.cringe_studios.code_guard.fragment.NamedFragment;
|
||||
import com.cringe_studios.code_guard.fragment.SettingsFragment;
|
||||
import com.cringe_studios.code_guard.icon.IconPack;
|
||||
import com.cringe_studios.code_guard.icon.IconPackException;
|
||||
import com.cringe_studios.code_guard.icon.IconPackMetadata;
|
||||
import com.cringe_studios.code_guard.icon.IconUtil;
|
||||
@ -146,7 +148,9 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
try {
|
||||
if(doc == null) return;
|
||||
IconPackMetadata meta = IconUtil.importIconPack(this, doc);
|
||||
IconPackMetadata meta = IconUtil.loadPackMetadata(this, doc);
|
||||
|
||||
IconPack existingIconPack = IconUtil.loadIconPack(this, meta.getUuid());
|
||||
|
||||
if(!meta.validate()) {
|
||||
DialogUtil.showErrorDialog(this, getString(R.string.error_icon_pack_invalid));
|
||||
@ -158,7 +162,57 @@ public class MainActivity extends BaseActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
DialogUtil.showErrorDialog(this, "Icon pack contains " + meta.getIcons().length + " icons");
|
||||
if(existingIconPack != null) {
|
||||
DialogIconPackExistsBinding binding = DialogIconPackExistsBinding.inflate(getLayoutInflater());
|
||||
binding.iconPackExistsText.setText(getString(R.string.error_icon_pack_exists, meta.getName(), meta.getVersion(), existingIconPack.getMetadata().getName(), existingIconPack.getMetadata().getVersion()));
|
||||
binding.iconPackExistsChoices.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.error_icon_pack_exists_choices)));
|
||||
|
||||
|
||||
AlertDialog dialog = new StyledDialogBuilder(this)
|
||||
.setTitle("Icon pack already exists")
|
||||
.setView(binding.getRoot())
|
||||
.setNeutralButton(R.string.cancel, (d, which) -> {})
|
||||
.create();
|
||||
|
||||
binding.iconPackExistsChoices.setOnItemClickListener((parent, view, position, id) -> {
|
||||
switch(position) {
|
||||
case 0: // Override
|
||||
try {
|
||||
IconUtil.importIconPack(this, doc);
|
||||
Toast.makeText(this, getString(R.string.icon_pack_imported, meta.getIcons().length), Toast.LENGTH_LONG).show();
|
||||
} catch (IconPackException e) {
|
||||
DialogUtil.showErrorDialog(this, "Failed to import icon pack", e);
|
||||
}
|
||||
break;
|
||||
case 1: // Rename existing
|
||||
try {
|
||||
IconUtil.renameIconPack(this, existingIconPack, existingIconPack.getMetadata().getName() + " (" + existingIconPack.getMetadata().getVersion() + ")", UUID.randomUUID().toString());
|
||||
IconUtil.importIconPack(this, doc);
|
||||
Toast.makeText(this, getString(R.string.icon_pack_imported, meta.getIcons().length), Toast.LENGTH_LONG).show();
|
||||
} catch (IconPackException e) {
|
||||
DialogUtil.showErrorDialog(this, "Failed to import icon pack", e);
|
||||
}
|
||||
break;
|
||||
case 2: // Rename imported
|
||||
try {
|
||||
IconUtil.importIconPack(this, doc, meta.getName() + "(" + meta.getVersion() + ")", UUID.randomUUID().toString());
|
||||
Toast.makeText(this, getString(R.string.icon_pack_imported, meta.getIcons().length), Toast.LENGTH_LONG).show();
|
||||
} catch (IconPackException e) {
|
||||
DialogUtil.showErrorDialog(this, "Failed to import icon pack", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IconUtil.importIconPack(this, doc);
|
||||
Toast.makeText(this, getString(R.string.icon_pack_imported, meta.getIcons().length), Toast.LENGTH_LONG).show();
|
||||
} catch (IconPackException e) {
|
||||
DialogUtil.showErrorDialog(this, "Failed to import icon pack", e);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import com.cringe_studios.code_guard.util.SettingsUtil;
|
||||
import com.cringe_studios.code_guard.util.StyledDialogBuilder;
|
||||
import com.cringe_studios.code_guard.util.Theme;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -264,7 +265,17 @@ public class SettingsFragment extends NamedFragment {
|
||||
binding.settingsLoadIconPack.setOnClickListener(v -> ((MainActivity) requireActivity()).promptPickIconPackFile());
|
||||
|
||||
binding.settingsManageIconPacks.setOnClickListener(v -> {
|
||||
List<IconPack> packs = IconUtil.loadAllIconPacks(requireContext());
|
||||
List<String> brokenPacks = new ArrayList<>();
|
||||
List<IconPack> packs = IconUtil.loadAllIconPacks(requireContext(), brokenPacks::add);
|
||||
|
||||
if(!brokenPacks.isEmpty()) {
|
||||
DialogUtil.showYesNo(requireContext(), R.string.broken_icon_packs_title, R.string.broken_icon_packs_message, () -> {
|
||||
for(String pack : brokenPacks) {
|
||||
IconUtil.removeIconPack(requireContext(), pack);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
if(packs.isEmpty()) {
|
||||
Toast.makeText(requireContext(), R.string.no_icon_packs_installed, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
|
@ -41,7 +41,13 @@ public class IconPackListAdapter extends RecyclerView.Adapter<IconPackItem> {
|
||||
holder.getBinding().iconPackName.setText(pack.getMetadata().getName());
|
||||
|
||||
holder.getBinding().iconPackDelete.setOnClickListener(view -> {
|
||||
DialogUtil.showYesNo(context, R.string.delete_pack_title, R.string.delete_pack_message, () -> IconUtil.removeIconPack(context, pack.getMetadata().getUuid()), null);
|
||||
DialogUtil.showYesNo(context, R.string.delete_pack_title, R.string.delete_pack_message, () -> {
|
||||
IconUtil.removeIconPack(context, pack.getMetadata().getUuid());
|
||||
|
||||
int idx = packs.indexOf(pack);
|
||||
packs.remove(idx);
|
||||
notifyItemRemoved(idx);
|
||||
}, null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,18 @@ public class IconPackMetadata {
|
||||
|
||||
private IconPackMetadata() {}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
@ -17,13 +17,15 @@ import com.caverock.androidsvg.SVG;
|
||||
import com.caverock.androidsvg.SVGImageView;
|
||||
import com.caverock.androidsvg.SVGParseException;
|
||||
import com.cringe_studios.code_guard.model.OTPData;
|
||||
import com.cringe_studios.code_guard.util.DialogUtil;
|
||||
import com.cringe_studios.code_guard.util.IOUtil;
|
||||
import com.cringe_studios.code_guard.util.SettingsUtil;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -38,6 +40,7 @@ import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class IconUtil {
|
||||
|
||||
@ -79,10 +82,9 @@ public class IconUtil {
|
||||
return iconPacksDir;
|
||||
}
|
||||
|
||||
public static IconPackMetadata importIconPack(Context context, Uri uri) throws IconPackException {
|
||||
public static void importIconPack(Context context, Uri uri) throws IconPackException {
|
||||
IconPackMetadata meta = loadPackMetadata(context, uri);
|
||||
|
||||
// TODO: check for existing icon pack
|
||||
File iconPackFile = new File(getIconPacksDir(context), meta.getUuid());
|
||||
|
||||
try {
|
||||
@ -90,7 +92,7 @@ public class IconUtil {
|
||||
iconPackFile.createNewFile();
|
||||
}
|
||||
|
||||
try (OutputStream out = new FileOutputStream(iconPackFile);
|
||||
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(iconPackFile));
|
||||
InputStream in = context.getContentResolver().openInputStream(uri)) {
|
||||
if(in == null) throw new IconPackException("Failed to read icon pack");
|
||||
byte[] bytes = IOUtil.readBytes(in);
|
||||
@ -99,8 +101,54 @@ public class IconUtil {
|
||||
}catch(IOException e) {
|
||||
throw new IconPackException("Failed to import icon pack", e);
|
||||
}
|
||||
}
|
||||
|
||||
return meta;
|
||||
public static void importIconPack(Context context, Uri uri, String newName, String newUUID) throws IconPackException {
|
||||
IconPackMetadata meta = loadPackMetadata(context, uri);
|
||||
meta.setName(newName);
|
||||
meta.setUuid(newUUID);
|
||||
|
||||
File iconPackFile = new File(getIconPacksDir(context), meta.getUuid());
|
||||
|
||||
try {
|
||||
if (!iconPackFile.exists()) {
|
||||
iconPackFile.createNewFile();
|
||||
}
|
||||
|
||||
try (InputStream in = context.getContentResolver().openInputStream(uri)) {
|
||||
if(in == null) throw new IconPackException("Failed to read icon pack");
|
||||
writeRenamedPack(in, iconPackFile, meta);
|
||||
}
|
||||
}catch(IOException e) {
|
||||
throw new IconPackException("Failed to import icon pack", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void renameIconPack(Context context, IconPack pack, String newName, String newUUID) throws IconPackException {
|
||||
File packFile = new File(getIconPacksDir(context), pack.getMetadata().getUuid());
|
||||
if(!packFile.exists()) return;
|
||||
|
||||
File newPackFile = new File(getIconPacksDir(context), newUUID);
|
||||
|
||||
String oldName = pack.getMetadata().getName();
|
||||
String oldUUID = pack.getMetadata().getUuid();
|
||||
|
||||
|
||||
loadedPacks.remove(oldUUID);
|
||||
|
||||
pack.getMetadata().setName(newName);
|
||||
pack.getMetadata().setUuid(newUUID);
|
||||
|
||||
try {
|
||||
writeRenamedPack(new BufferedInputStream(new FileInputStream(packFile)), newPackFile, pack.getMetadata());
|
||||
packFile.delete();
|
||||
}catch(IconPackException e) {
|
||||
pack.getMetadata().setName(oldName);
|
||||
pack.getMetadata().setUuid(oldUUID);
|
||||
throw e;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IconPackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeIconPack(Context context, String uuid) {
|
||||
@ -109,12 +157,14 @@ public class IconUtil {
|
||||
loadedPacks.remove(uuid);
|
||||
}
|
||||
|
||||
private static IconPackMetadata loadPackMetadata(Context context, Uri uri) throws IconPackException {
|
||||
public static IconPackMetadata loadPackMetadata(Context context, Uri uri) throws IconPackException {
|
||||
try(InputStream in = context.getContentResolver().openInputStream(uri)) {
|
||||
if(in == null) throw new IconPackException("Failed to read icon pack");
|
||||
try(ZipInputStream zIn = new ZipInputStream(in)) {
|
||||
ZipEntry en;
|
||||
while((en = zIn.getNextEntry()) != null) {
|
||||
if(en.isDirectory()) continue;
|
||||
|
||||
if(en.getName().equals("pack.json")) {
|
||||
byte[] entryBytes = readEntry(zIn, en);
|
||||
return SettingsUtil.GSON.fromJson(new String(entryBytes, StandardCharsets.UTF_8), IconPackMetadata.class); // TODO: validate metadata
|
||||
@ -128,6 +178,28 @@ public class IconUtil {
|
||||
throw new IconPackException("No pack.json");
|
||||
}
|
||||
|
||||
private static void writeRenamedPack(InputStream oldFile, File newFile, IconPackMetadata meta) throws IconPackException {
|
||||
try(ZipInputStream in = new ZipInputStream(oldFile);
|
||||
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(newFile)))) {
|
||||
ZipEntry en;
|
||||
while((en = in.getNextEntry()) != null) {
|
||||
if(en.isDirectory()) continue;
|
||||
|
||||
byte[] entryBytes = readEntry(in, en);
|
||||
if(en.getName().equals("pack.json")) {
|
||||
out.putNextEntry(new ZipEntry("pack.json"));
|
||||
out.write(SettingsUtil.GSON.toJson(meta).getBytes(StandardCharsets.UTF_8));
|
||||
continue;
|
||||
}
|
||||
|
||||
out.putNextEntry(new ZipEntry(en.getName()));
|
||||
out.write(entryBytes);
|
||||
}
|
||||
}catch(IOException e) {
|
||||
throw new IconPackException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, List<Icon>> loadAllIcons(Context context) {
|
||||
List<IconPack> packs = loadAllIconPacks(context);
|
||||
|
||||
@ -148,7 +220,7 @@ public class IconUtil {
|
||||
return icons;
|
||||
}
|
||||
|
||||
public static List<IconPack> loadAllIconPacks(Context context) {
|
||||
public static List<IconPack> loadAllIconPacks(Context context, Consumer<String> brokenPack) {
|
||||
File iconPacksDir = getIconPacksDir(context);
|
||||
|
||||
String[] packIDs = iconPacksDir.list();
|
||||
@ -157,15 +229,24 @@ public class IconUtil {
|
||||
List<IconPack> packs = new ArrayList<>();
|
||||
for(String pack : packIDs) {
|
||||
try {
|
||||
packs.add(loadIconPack(context, pack));
|
||||
IconPack p = loadIconPack(context, pack);
|
||||
if(p == null) continue;
|
||||
if(!p.getMetadata().getUuid().equals(pack)) throw new IconPackException("Invalid metadata");
|
||||
packs.add(p);
|
||||
}catch(IconPackException e) {
|
||||
DialogUtil.showErrorDialog(context, "An icon pack failed to load", e);
|
||||
e.printStackTrace();
|
||||
if(brokenPack != null) brokenPack.accept(pack);
|
||||
//DialogUtil.showErrorDialog(context, "An icon pack failed to load", e);
|
||||
}
|
||||
}
|
||||
|
||||
return packs;
|
||||
}
|
||||
|
||||
public static List<IconPack> loadAllIconPacks(Context context) {
|
||||
return loadAllIconPacks(context, null);
|
||||
}
|
||||
|
||||
public static IconPack loadIconPack(Context context, String uuid) throws IconPackException {
|
||||
if(loadedPacks.containsKey(uuid)) return loadedPacks.get(uuid);
|
||||
|
||||
@ -179,12 +260,14 @@ public class IconUtil {
|
||||
private static IconPack loadIconPack(File file) throws IconPackException {
|
||||
if(!file.exists()) return null;
|
||||
|
||||
try(ZipInputStream in = new ZipInputStream(new FileInputStream(file))) {
|
||||
try(ZipInputStream in = new ZipInputStream(new BufferedInputStream(new FileInputStream(file)))) {
|
||||
IconPackMetadata metadata = null;
|
||||
Map<String, byte[]> files = new HashMap<>();
|
||||
|
||||
ZipEntry en;
|
||||
while((en = in.getNextEntry()) != null) {
|
||||
if(en.isDirectory()) continue;
|
||||
|
||||
byte[] entryBytes = readEntry(in, en);
|
||||
|
||||
if(en.getName().equals("pack.json")) {
|
||||
@ -214,18 +297,11 @@ public class IconUtil {
|
||||
}
|
||||
|
||||
private static byte[] readEntry(ZipInputStream in, ZipEntry en) throws IOException {
|
||||
if (en.getSize() < 0 || en.getSize() > Integer.MAX_VALUE) {
|
||||
if (en.getSize() > Integer.MAX_VALUE) {
|
||||
throw new IOException("Invalid ZIP entry");
|
||||
}
|
||||
|
||||
byte[] entryBytes = new byte[(int) en.getSize()];
|
||||
|
||||
int totalRead = 0;
|
||||
while (totalRead < entryBytes.length) {
|
||||
totalRead += in.read(entryBytes, totalRead, entryBytes.length - totalRead);
|
||||
}
|
||||
|
||||
return entryBytes;
|
||||
return IOUtil.readBytes(in);
|
||||
}
|
||||
|
||||
public static Bitmap generateCodeImage(String issuer, String name) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.cringe_studios.code_guard.util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
|
||||
public class IOUtil {
|
||||
|
||||
public static byte[] readBytes(File file) throws IOException {
|
||||
try(FileInputStream fIn = new FileInputStream(file)) {
|
||||
try(InputStream fIn = new BufferedInputStream(new FileInputStream(file))) {
|
||||
ByteBuffer fileBuffer = ByteBuffer.allocate((int) file.length());
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
@ -34,10 +34,4 @@ public class IOUtil {
|
||||
return bOut.toByteArray();
|
||||
}
|
||||
|
||||
public static void writeBytes(File file, byte[] bytes) throws IOException {
|
||||
try(FileOutputStream fOut = new FileOutputStream(file)) {
|
||||
fOut.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,9 +11,11 @@ import com.cringe_studios.code_guard.model.OTPData;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -148,7 +150,7 @@ public class OTPDatabase {
|
||||
|
||||
byte[] dbBytes = convertToEncryptedBytes(loadedDatabase, loadedKey, parameters);
|
||||
|
||||
try(FileOutputStream fOut = new FileOutputStream(file)) {
|
||||
try(OutputStream fOut = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||
fOut.write(dbBytes);
|
||||
} catch (IOException e) {
|
||||
throw new OTPDatabaseException(e);
|
||||
|
22
app/src/main/res/layout/dialog_icon_pack_exists.xml
Normal file
22
app/src/main/res/layout/dialog_icon_pack_exists.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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/icon_pack_exists_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/error_icon_pack_exists" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/icon_pack_exists_choices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
tools:listitem="@android:layout/simple_list_item_1"/>
|
||||
|
||||
</LinearLayout>
|
@ -123,4 +123,13 @@
|
||||
<string name="patreon_link" translatable="false">https://git.cringe-studios.com/CringeStudios/Code-Guard</string>
|
||||
<string name="error_icon_pack_empty">The icon pack doesn\'t contain any icons</string>
|
||||
<string name="error_icon_pack_invalid">Pack contains invalid metadata. Make sure you selected the correct file</string>
|
||||
<string name="error_icon_pack_exists">The icon pack you\'re trying to import already exists.\n\nImported: %s (version %d)\nExisting: %s (version %d)\n\nWhat do you want to do?</string>
|
||||
<string name="broken_icon_packs_title">Broken icon packs</string>
|
||||
<string name="broken_icon_packs_message">Some icon packs failed to load.\n\nDo you want to delete the broken icon packs?</string>
|
||||
<string name="icon_pack_imported">Icon pack with %d icon(s) imported</string>
|
||||
<string-array name="error_icon_pack_exists_choices">
|
||||
<item>Override</item>
|
||||
<item>Rename existing</item>
|
||||
<item>Rename imported</item>
|
||||
</string-array>
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user