Select mod with external APP
This commit is contained in:
parent
2777f7999a
commit
c45f144b10
|
@ -2,9 +2,12 @@ package com.tungsten.fcl.ui.manage;
|
|||
|
||||
import static com.tungsten.fclcore.util.Logging.LOG;
|
||||
import static com.tungsten.fclcore.util.StringUtils.isNotBlank;
|
||||
import static com.tungsten.fcllibrary.browser.FileBrowser.SELECTED_FILES;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
|
@ -277,22 +280,42 @@ public class ModListPage extends FCLCommonPage implements ManageUI.VersionLoadab
|
|||
builder.setSelectionMode(SelectionMode.MULTIPLE_SELECTION);
|
||||
builder.create().browse(getActivity(), RequestCodes.SELECT_MODS_CODE, (requestCode, resultCode, data) -> {
|
||||
if (requestCode == RequestCodes.SELECT_MODS_CODE && resultCode == Activity.RESULT_OK && data != null) {
|
||||
List<File> res = FileBrowser.getSelectedFiles(data).stream().filter(Objects::nonNull).map(File::new).collect(Collectors.toList());
|
||||
|
||||
ArrayList<Uri> selectedFiles = data.getParcelableArrayListExtra(SELECTED_FILES);
|
||||
List<Object> res = selectedFiles.stream().filter(Objects::nonNull).map(uri -> {
|
||||
if (Objects.equals(uri.getScheme(), ContentResolver.SCHEME_CONTENT) || Objects.equals(uri.getScheme(), ContentResolver.SCHEME_FILE)) {
|
||||
return uri;
|
||||
} else {
|
||||
return new File(uri.toString());
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
// It's guaranteed that succeeded and failed are thread safe here.
|
||||
List<String> succeeded = new ArrayList<>(res.size());
|
||||
List<String> failed = new ArrayList<>();
|
||||
|
||||
Task.runAsync(() -> {
|
||||
for (File file : res) {
|
||||
try {
|
||||
modManager.addMod(file.toPath());
|
||||
succeeded.add(file.getName());
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.WARNING, "Unable to add mod " + file, e);
|
||||
failed.add(file.getName());
|
||||
for (Object obj : res) {
|
||||
if (obj instanceof File) {
|
||||
File file = (File) obj;
|
||||
try {
|
||||
modManager.addMod(file.toPath());
|
||||
succeeded.add(file.getName());
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.WARNING, "Unable to add mod " + file, e);
|
||||
failed.add(file.getName());
|
||||
|
||||
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
||||
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Uri uri = (Uri) obj;
|
||||
modManager.addMod(getActivity(), uri);
|
||||
succeeded.add(new File(uri.getPath()).getName());
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.WARNING, "Unable to add mod " + obj.toString(), e);
|
||||
failed.add(obj.toString());
|
||||
|
||||
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
||||
}
|
||||
}
|
||||
}
|
||||
}).withRunAsync(Schedulers.androidUIThread(), () -> {
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
*/
|
||||
package com.tungsten.fclcore.mod;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.tungsten.fclcore.game.GameRepository;
|
||||
import com.tungsten.fclcore.mod.modinfo.FabricModMetadata;
|
||||
|
@ -31,9 +34,24 @@ import com.tungsten.fclcore.util.io.CompressingUtils;
|
|||
import com.tungsten.fclcore.util.io.FileUtils;
|
||||
import com.tungsten.fclcore.util.versioning.VersionNumber;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public final class ModManager {
|
||||
@FunctionalInterface
|
||||
|
@ -167,6 +185,28 @@ public final class ModManager {
|
|||
addModInfo(newFile);
|
||||
}
|
||||
|
||||
public void addMod(Activity activity, Uri uri) throws IOException {
|
||||
if (!isFileNameMod(uri))
|
||||
throw new IllegalArgumentException("File " + uri.toString() + " is not a valid mod file.");
|
||||
|
||||
if (!loaded)
|
||||
refreshMods();
|
||||
|
||||
Path modsDirectory = getModsDirectory();
|
||||
Files.createDirectories(modsDirectory);
|
||||
String name = new File(uri.getPath()).getName();
|
||||
Path newFile = modsDirectory.resolve(name);
|
||||
InputStream inputStream = activity.getContentResolver().openInputStream(uri);
|
||||
if(inputStream == null) {
|
||||
throw new IOException("Failed to open content stream");
|
||||
}
|
||||
try (FileOutputStream outputStream = new FileOutputStream(newFile.toFile())){
|
||||
IOUtils.copy(inputStream,outputStream);
|
||||
}
|
||||
inputStream.close();
|
||||
addModInfo(newFile);
|
||||
}
|
||||
|
||||
public void removeMods(LocalModFile... localModFiles) throws IOException {
|
||||
for (LocalModFile localModFile : localModFiles) {
|
||||
Files.deleteIfExists(localModFile.getFile());
|
||||
|
@ -279,6 +319,10 @@ public final class ModManager {
|
|||
return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".litemod");
|
||||
}
|
||||
|
||||
public static boolean isFileNameMod(Uri uri) {
|
||||
return isFileNameMod(new File(uri.toString()).toPath());
|
||||
}
|
||||
|
||||
public static boolean isFileMod(Path modFile) {
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) {
|
||||
if (Files.exists(fs.getPath("mcmod.info")) || Files.exists(fs.getPath("META-INF/mods.toml"))) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.tungsten.fcllibrary.browser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
@ -10,6 +12,9 @@ import android.view.View;
|
|||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContract;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
|
@ -43,11 +48,51 @@ public class FileBrowserActivity extends FCLActivity implements View.OnClickList
|
|||
private FCLButton sharedDir;
|
||||
private FCLButton privateDir;
|
||||
private FCLButton openExternal;
|
||||
private FCLButton selectExternal;
|
||||
private FCLButton confirm;
|
||||
|
||||
private Path currentPath;
|
||||
|
||||
private ArrayList<String> selectedFiles;
|
||||
private ArrayList<Uri> extSelected;
|
||||
|
||||
private final ActivityResultLauncher<Object> launcher = registerForActivityResult(new ActivityResultContract<Object, Uri>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Intent createIntent(@NonNull Context context, Object o) {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, fileBrowser.getSelectionMode() == SelectionMode.MULTIPLE_SELECTION);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri parseResult(int resultCode, @Nullable Intent data) {
|
||||
if (data == null || resultCode != Activity.RESULT_OK) {
|
||||
return null;
|
||||
}
|
||||
ClipData clipData = data.getClipData();
|
||||
if (clipData != null && clipData.getItemCount() > 0) {
|
||||
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||
Uri uri = clipData.getItemAt(i).getUri();
|
||||
if (uri != null) {
|
||||
extSelected.add(uri);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
extSelected.add(data.getData());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, result -> {
|
||||
if (!extSelected.isEmpty()) {
|
||||
Intent intent = new Intent();
|
||||
intent.putParcelableArrayListExtra(FileBrowser.SELECTED_FILES, extSelected);
|
||||
FileBrowserActivity.this.setResult(Activity.RESULT_OK, intent);
|
||||
FileBrowserActivity.this.finish();
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
@ -73,16 +118,23 @@ public class FileBrowserActivity extends FCLActivity implements View.OnClickList
|
|||
sharedDir = findViewById(R.id.shared_dir);
|
||||
privateDir = findViewById(R.id.private_dir);
|
||||
openExternal = findViewById(R.id.open_external);
|
||||
selectExternal = findViewById(R.id.select_external);
|
||||
confirm = findViewById(R.id.confirm);
|
||||
sharedDir.setOnClickListener(this);
|
||||
privateDir.setOnClickListener(this);
|
||||
openExternal.setOnClickListener(this);
|
||||
selectExternal.setOnClickListener(this);
|
||||
confirm.setOnClickListener(this);
|
||||
|
||||
selectedFiles = new ArrayList<>();
|
||||
extSelected = new ArrayList<>();
|
||||
currentText = findViewById(R.id.current_folder);
|
||||
listView = findViewById(R.id.list);
|
||||
refreshList(currentPath != null ? currentPath : new File(fileBrowser.getInitDir()).toPath());
|
||||
|
||||
if (fileBrowser.getLibMode() != LibMode.FILE_CHOOSER) {
|
||||
selectExternal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMode() {
|
||||
|
@ -177,7 +229,10 @@ public class FileBrowserActivity extends FCLActivity implements View.OnClickList
|
|||
intent.setDataAndType(uri, "*/*");
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
startActivity(Intent.createChooser(intent,getString(R.string.file_browser_open_external)));
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.file_browser_open_external)));
|
||||
}
|
||||
if (view == selectExternal) {
|
||||
launcher.launch(null);
|
||||
}
|
||||
if (view == confirm) {
|
||||
if (selectedFiles.size() == 0 && fileBrowser.getLibMode() != LibMode.FILE_BROWSER) {
|
||||
|
@ -190,4 +245,10 @@ public class FileBrowserActivity extends FCLActivity implements View.OnClickList
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
callback = false;
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.tungsten.fcllibrary.component.theme.ThemeEngine;
|
|||
import com.tungsten.fcllibrary.util.LocaleUtils;
|
||||
|
||||
public class FCLActivity extends AppCompatActivity {
|
||||
public boolean callback = true;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
@ -64,6 +65,8 @@ public class FCLActivity extends AppCompatActivity {
|
|||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
ResultListener.onActivityResult(requestCode, resultCode, data);
|
||||
if (callback) {
|
||||
ResultListener.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,18 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:text="@string/file_browser_open_external"/>
|
||||
|
||||
<com.tungsten.fcllibrary.component.view.FCLButton
|
||||
android:id="@+id/select_external"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/open_external"
|
||||
app:layout_constraintStart_toEndOf="@+id/split"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:text="@string/file_browser_select_external"/>
|
||||
|
||||
<com.tungsten.fcllibrary.component.view.FCLTextView
|
||||
android:id="@+id/mode_hint"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -34,4 +34,5 @@
|
|||
<string name="dialog_info">提示</string>
|
||||
<string name="dialog_positive">确认</string>
|
||||
<string name="dialog_negative">取消</string>
|
||||
<string name="file_browser_select_external">使用外部应用选择</string>
|
||||
</resources>
|
|
@ -8,6 +8,7 @@
|
|||
<string name="file_browser_shared">Shared directory</string>
|
||||
<string name="file_browser_private">Private directory</string>
|
||||
<string name="file_browser_open_external">Open in external APP(Like MT)</string>
|
||||
<string name="file_browser_select_external">Select with external APP</string>
|
||||
<string name="file_browser_private_alert">Cannot access to private directory, please check the permission.</string>
|
||||
<string name="file_browser_mode">Mode:</string>
|
||||
<string name="file_browser_mode_browse">Browse File</string>
|
||||
|
|
Loading…
Reference in New Issue