fix: crash when ScriptListView init

This commit is contained in:
hyb1996
2017-10-21 20:36:45 +08:00
parent 1e50e6d9da
commit deda91e6b1
14 changed files with 171 additions and 76 deletions

View File

@@ -10,8 +10,8 @@ android {
applicationId "com.stardust.scriptdroid" applicationId "com.stardust.scriptdroid"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 23 targetSdkVersion 23
versionCode 209 versionCode 210
versionName "3.0.0 Alpha10" versionName "3.0.0 Alpha11"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
ndk { ndk {

View File

@@ -32,6 +32,8 @@ public class StorageFileProvider {
public static final int ALL = 3; public static final int ALL = 3;
public static final String DEFAULT_DIRECTORY_PATH = Environment.getExternalStorageDirectory() + App.getApp().getString(R.string.folder_name); public static final String DEFAULT_DIRECTORY_PATH = Environment.getExternalStorageDirectory() + App.getApp().getString(R.string.folder_name);
public static final PFile DEFAULT_DIRECTORY = new PFile(DEFAULT_DIRECTORY_PATH); public static final PFile DEFAULT_DIRECTORY = new PFile(DEFAULT_DIRECTORY_PATH);
public static final FileFilter SCRIPT_FILTER = file ->
file.isDirectory() || file.getName().endsWith(".js") || file.getName().endsWith(".auto");
public static class DirectoryChangeEvent { public static class DirectoryChangeEvent {
@@ -75,8 +77,6 @@ public class StorageFileProvider {
private static StorageFileProvider externalStorageProvider; private static StorageFileProvider externalStorageProvider;
private static final FileFilter SCRIPT_FILTER = file ->
file.isDirectory() || file.getName().endsWith(".js") || file.getName().endsWith(".auto");
private static final StorageFileProvider DEFAULT_PROVIDER = new StorageFileProvider(DEFAULT_DIRECTORY, 10, SCRIPT_FILTER); private static final StorageFileProvider DEFAULT_PROVIDER = new StorageFileProvider(DEFAULT_DIRECTORY, 10, SCRIPT_FILTER);
private EventBus mDirectoryEventBus = new EventBus(); private EventBus mDirectoryEventBus = new EventBus();

View File

@@ -1,31 +1,16 @@
package com.stardust.scriptdroid.network.download; package com.stardust.scriptdroid.network.download;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import java.io.File; import java.io.File;
import java.util.concurrent.TimeUnit;
import io.reactivex.Flowable;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.PublishSubject; import io.reactivex.subjects.PublishSubject;
import zlc.season.rxdownload3.RxDownload; import zlc.season.rxdownload3.RxDownload;
import zlc.season.rxdownload3.core.Failed; import zlc.season.rxdownload3.core.Failed;
import zlc.season.rxdownload3.core.Mission; import zlc.season.rxdownload3.core.Mission;
import zlc.season.rxdownload3.core.Status;
import zlc.season.rxdownload3.core.Succeed; import zlc.season.rxdownload3.core.Succeed;
import static android.app.DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR;
import static android.app.DownloadManager.COLUMN_STATUS;
import static android.app.DownloadManager.COLUMN_TOTAL_SIZE_BYTES;
import static android.app.DownloadManager.STATUS_FAILED;
import static android.app.DownloadManager.STATUS_RUNNING;
import static android.app.DownloadManager.STATUS_SUCCESSFUL;
/** /**
* Created by Stardust on 2017/10/20. * Created by Stardust on 2017/10/20.
*/ */
@@ -34,15 +19,11 @@ public class DownloadManager {
private static DownloadManager sInstance; private static DownloadManager sInstance;
private android.app.DownloadManager mManager;
private Context mContext; private Context mContext;
private Handler mHandler;
public DownloadManager(Context context) { public DownloadManager(Context context) {
mContext = context; mContext = context;
mHandler = new Handler();
mManager = (android.app.DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
} }
public static DownloadManager getInstance(Context context) { public static DownloadManager getInstance(Context context) {
@@ -67,8 +48,10 @@ public class DownloadManager {
PublishSubject<Integer> progress = PublishSubject.create(); PublishSubject<Integer> progress = PublishSubject.create();
RxDownload.INSTANCE.create(mission) RxDownload.INSTANCE.create(mission)
.subscribe(status -> { .subscribe(status -> {
if (status.getTotalSize() > 0) {
int p = (int) Math.floor((float) status.getDownloadSize() / status.getTotalSize() * 100); int p = (int) Math.floor((float) status.getDownloadSize() / status.getTotalSize() * 100);
progress.onNext(p); progress.onNext(p);
}
if (status instanceof Succeed) { if (status instanceof Succeed) {
progress.onComplete(); progress.onComplete();
RxDownload.INSTANCE.delete(mission); RxDownload.INSTANCE.delete(mission);
@@ -80,43 +63,6 @@ public class DownloadManager {
return progress; return progress;
} }
public Observable<Integer> download$(String url, String path) {
android.app.DownloadManager.Request request = new android.app.DownloadManager.Request(Uri.parse(url));
request.setDestinationUri(Uri.parse("file://" + path));
long id = mManager.enqueue(request);
PublishSubject<Integer> downloadProgress = PublishSubject.create();
Disposable disposable = Observable.interval(0, 200, TimeUnit.MILLISECONDS)
.subscribe(t -> {
int progress = getDownloadProgress(id);
if (progress < 0) {
downloadProgress.onError(new DownloadFailedException(url, path));
}
downloadProgress.onNext(progress);
if (progress == 100) {
downloadProgress.onComplete();
}
});
downloadProgress.doOnComplete(disposable::dispose);
return downloadProgress;
}
private int getDownloadProgress(long id) {
Cursor cursor = mManager.query(new android.app.DownloadManager.Query()
.setFilterById(id));
if (!cursor.moveToFirst()) {
return -1;
}
int total = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_TOTAL_SIZE_BYTES));
int downloaded = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_BYTES_DOWNLOADED_SO_FAR));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
if (status == STATUS_SUCCESSFUL) {
return 100;
} else if (status == STATUS_FAILED) {
return -1;
}
return (int) Math.floor((float) downloaded / total * 100);
}
public void cancelDownload(String url) { public void cancelDownload(String url) {
RxDownload.INSTANCE.stop(url); RxDownload.INSTANCE.stop(url);
RxDownload.INSTANCE.delete(url); RxDownload.INSTANCE.delete(url);

View File

@@ -0,0 +1,37 @@
package com.stardust.scriptdroid.ui.common;
import android.content.Context;
import android.support.annotation.NonNull;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.R;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import io.reactivex.Observable;
import io.reactivex.subjects.PublishSubject;
/**
* Created by Stardust on 2017/10/21.
*/
public class RxDialogs {
public static Observable<Boolean> confirm(Context context, String text) {
PublishSubject<Boolean> subject = PublishSubject.create();
new ThemeColorMaterialDialogBuilder(context)
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.onPositive((dialog, which) -> subject.onNext(true))
.onNegative((dialog, which) -> subject.onNext(false))
.content(text)
.show();
return subject;
}
public static Observable<Boolean> confirm(Context context, int res) {
return confirm(context, context.getString(res));
}
}

View File

@@ -1,6 +1,7 @@
package com.stardust.scriptdroid.ui.common; package com.stardust.scriptdroid.ui.common;
import android.content.Context; import android.content.Context;
import android.os.Environment;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -34,7 +35,6 @@ import org.reactivestreams.Subscriber;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.concurrent.Callable;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -256,13 +256,26 @@ public class ScriptOperations {
public Observable<ScriptFile> download(String url) { public Observable<ScriptFile> download(String url) {
String fileName = DownloadManager.parseFileNameLocally(url); String fileName = DownloadManager.parseFileNameLocally(url);
MaterialDialog progressDialog = createDownloadProgressDialog(url, fileName);
return new FileChooserDialogBuilder(mContext) return new FileChooserDialogBuilder(mContext)
.title(R.string.text_select_save_path) .title(R.string.text_select_save_path)
.chooseDir() .chooseDir()
.singleChoice() .singleChoice()
.map(saveDir -> new File(saveDir, fileName).getPath()) .map(saveDir -> new File(saveDir, fileName).getPath())
.flatMap(savePath -> download(url, savePath, progressDialog)); .flatMap(savePath -> {
if (!new File(savePath).exists()) {
return Observable.just(savePath);
}
return RxDialogs.confirm(mContext, R.string.confirm_overwrite_file)
.flatMap(yes -> {
if (yes) {
new File(savePath).delete();
return Observable.just(savePath);
} else {
return Observable.empty();
}
});
})
.flatMap(savePath -> download(url, savePath, createDownloadProgressDialog(url, fileName)));
} }
private MaterialDialog createDownloadProgressDialog(String url, String fileName) { private MaterialDialog createDownloadProgressDialog(String url, String fileName) {
@@ -302,6 +315,16 @@ public class ScriptOperations {
download(url, tmpFile.getPath(), createDownloadProgressDialog(url, fileName))); download(url, tmpFile.getPath(), createDownloadProgressDialog(url, fileName)));
} }
public void importFile() {
new FileChooserDialogBuilder(mContext)
.dir(Environment.getExternalStorageDirectory().getPath())
.justScriptFile()
.singleChoice(file -> importFile(file.getPath()))
.title(R.string.text_select_file_to_import)
.positiveText(R.string.ok)
.show();
}
private class InputCallback implements MaterialDialog.InputCallback { private class InputCallback implements MaterialDialog.InputCallback {

View File

@@ -159,6 +159,7 @@ public class FileChooseListView extends ScriptListView {
DirectoryViewHolder(View itemView) { DirectoryViewHolder(View itemView) {
super(itemView); super(itemView);
ButterKnife.bind(this, itemView); ButterKnife.bind(this, itemView);
mCheckBox.setVisibility(mCanChooseDir ? VISIBLE : GONE);
} }
@Override @Override

View File

@@ -59,7 +59,11 @@ public class FileChooserDialogBuilder extends ThemeColorMaterialDialogBuilder {
public FileChooserDialogBuilder dir(String rootDir, String initialDir) { public FileChooserDialogBuilder dir(String rootDir, String initialDir) {
mRootDir = new PFile(rootDir); mRootDir = new PFile(rootDir);
if (mRootDir.equals(StorageFileProvider.DEFAULT_DIRECTORY)) {
mFileChooseListView.setStorageFileProvider(StorageFileProvider.getDefault());
} else {
mFileChooseListView.setStorageFileProvider(new StorageFileProvider(mRootDir, 10), new ScriptFile(initialDir)); mFileChooseListView.setStorageFileProvider(new StorageFileProvider(mRootDir, 10), new ScriptFile(initialDir));
}
return this; return this;
} }
@@ -67,6 +71,11 @@ public class FileChooserDialogBuilder extends ThemeColorMaterialDialogBuilder {
return dir(dir, dir); return dir(dir, dir);
} }
public FileChooserDialogBuilder justScriptFile() {
mFileChooseListView.setStorageFileProvider(new StorageFileProvider(mRootDir, 10, StorageFileProvider.SCRIPT_FILTER));
return this;
}
public FileChooserDialogBuilder chooseDir() { public FileChooserDialogBuilder chooseDir() {
mFileChooseListView.setCanChooseDir(true); mFileChooseListView.setCanChooseDir(true);

View File

@@ -1,17 +1,24 @@
package com.stardust.scriptdroid.ui.main.community; package com.stardust.scriptdroid.ui.main.community;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.support.design.widget.BottomSheetDialog; import android.support.design.widget.BottomSheetDialog;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView; import android.webkit.WebView;
import com.stardust.pio.PFile;
import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.io.StorageFileProvider;
import com.stardust.scriptdroid.model.script.ScriptFile; import com.stardust.scriptdroid.model.script.ScriptFile;
import com.stardust.scriptdroid.model.script.Scripts; import com.stardust.scriptdroid.model.script.Scripts;
import com.stardust.scriptdroid.network.download.DownloadManager; import com.stardust.scriptdroid.network.download.DownloadManager;
import com.stardust.scriptdroid.ui.common.OptionListView; import com.stardust.scriptdroid.ui.common.OptionListView;
import com.stardust.scriptdroid.ui.common.ScriptOperations; import com.stardust.scriptdroid.ui.common.ScriptOperations;
import com.stardust.scriptdroid.ui.filechooser.FileChooserDialogBuilder;
import com.stardust.widget.EWebView; import com.stardust.widget.EWebView;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -41,6 +48,7 @@ public class CommunityWebView extends EWebView {
private void init() { private void init() {
getWebView().setWebViewClient(new MyWebViewClient()); getWebView().setWebViewClient(new MyWebViewClient());
getWebView().setWebChromeClient(new MyWebChromeClient());
} }
private void shouldScriptOptionsDialog(String url) { private void shouldScriptOptionsDialog(String url) {
@@ -111,6 +119,19 @@ public class CommunityWebView extends EWebView {
return super.shouldOverrideUrlLoading(view, url); return super.shouldOverrideUrlLoading(view, url);
} }
}
private class MyWebChromeClient extends EWebView.MyWebChromeClient {
@Override
public void openFileChooser(ValueCallback<Uri> callback) {
new FileChooserDialogBuilder(getContext())
.title(R.string.text_select_file_to_upload)
.dir(StorageFileProvider.DEFAULT_DIRECTORY_PATH)
.singleChoice(file -> callback.onReceiveValue(Uri.fromFile(file)))
.cancelListener(dialog -> callback.onReceiveValue(null))
.show();
}
} }
} }

View File

@@ -133,6 +133,8 @@ public class MyScriptListFragment extends ViewPagerFragment implements BackPress
.newScriptFile(); .newScriptFile();
break; break;
case 2: case 2:
new ScriptOperations(getContext(), mScriptFileList, mScriptFileList.getCurrentDirectory())
.importFile();
break; break;
} }

View File

@@ -8,6 +8,7 @@ import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@@ -42,6 +43,8 @@ import io.reactivex.schedulers.Schedulers;
public class ScriptListView extends SwipeRefreshLayout implements SwipeRefreshLayout.OnRefreshListener, PopupMenu.OnMenuItemClickListener { public class ScriptListView extends SwipeRefreshLayout implements SwipeRefreshLayout.OnRefreshListener, PopupMenu.OnMenuItemClickListener {
private static final String LOG_TAG = "ScriptListView";
public interface OnScriptFileClickListener { public interface OnScriptFileClickListener {
void onScriptFileClick(View view, ScriptFile file); void onScriptFileClick(View view, ScriptFile file);
} }
@@ -155,11 +158,10 @@ public class ScriptListView extends SwipeRefreshLayout implements SwipeRefreshLa
private void loadScriptList() { private void loadScriptList() {
setRefreshing(true); setRefreshing(true);
mScriptList.clear();
mStorageFileProvider.getDirectoryFiles(mCurrentDirectory) mStorageFileProvider.getDirectoryFiles(mCurrentDirectory)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.collectInto(mScriptList, (list, file) -> .collectInto(mScriptList.cloneConfig(), (list, file) ->
mScriptList.add(new ScriptFile(file)) list.add(new ScriptFile(file))
) )
.observeOn(Schedulers.computation()) .observeOn(Schedulers.computation())
.doOnSuccess(ScriptList::sort) .doOnSuccess(ScriptList::sort)
@@ -287,6 +289,7 @@ public class ScriptListView extends SwipeRefreshLayout implements SwipeRefreshLa
@Override @Override
public void onBindViewHolder(BindableViewHolder<?> holder, int position) { public void onBindViewHolder(BindableViewHolder<?> holder, int position) {
int positionOfCategoryFile = positionOfCategoryFile(); int positionOfCategoryFile = positionOfCategoryFile();
Log.d(LOG_TAG, String.format("view holder = %s, pos = %d, posOfCategory = %d, size = %d", holder.getClass().toString(), position, positionOfCategoryFile, mScriptList.count()));
BindableViewHolder bindableViewHolder = (BindableViewHolder) holder; BindableViewHolder bindableViewHolder = (BindableViewHolder) holder;
if (position == positionOfCategoryDir || position == positionOfCategoryFile) { if (position == positionOfCategoryDir || position == positionOfCategoryFile) {
// FIXME: 2017/10/20 java.lang.ClassCastException: java.lang.Boolean cannot be cast to com.stardust.scriptdroid.model.script.ScriptFile // FIXME: 2017/10/20 java.lang.ClassCastException: java.lang.Boolean cannot be cast to com.stardust.scriptdroid.model.script.ScriptFile
@@ -297,19 +300,22 @@ public class ScriptListView extends SwipeRefreshLayout implements SwipeRefreshLa
bindableViewHolder.bind(mScriptList.getDir(position - 1), position); bindableViewHolder.bind(mScriptList.getDir(position - 1), position);
return; return;
} }
bindableViewHolder.bind(mScriptList.getFile(position - positionOfCategoryFile() - 1), position); bindableViewHolder.bind(mScriptList.getFile(position - positionOfCategoryFile - 1), position);
} }
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
int viewType;
int positionOfCategoryFile = positionOfCategoryFile(); int positionOfCategoryFile = positionOfCategoryFile();
if (position == positionOfCategoryDir || position == positionOfCategoryFile) { if (position == positionOfCategoryDir || position == positionOfCategoryFile) {
return VIEW_TYPE_CATEGORY; viewType = VIEW_TYPE_CATEGORY;
} else if (position < positionOfCategoryFile) {
viewType = VIEW_TYPE_DIRECTORY;
} else {
viewType = VIEW_TYPE_FILE;
} }
if (position < positionOfCategoryFile) { Log.d(LOG_TAG, String.format("view type = %d, pos = %d, posOfCategory = %d, size = %d", viewType, position, positionOfCategoryFile, mScriptList.count()));
return VIEW_TYPE_DIRECTORY; return viewType;
}
return VIEW_TYPE_FILE;
} }
@Override @Override

View File

@@ -1,6 +1,7 @@
package com.stardust.scriptdroid.ui.viewmodel; package com.stardust.scriptdroid.ui.viewmodel;
import com.stardust.scriptdroid.model.script.ScriptFile; import com.stardust.scriptdroid.model.script.ScriptFile;
import com.stardust.scriptdroid.ui.main.scripts.ScriptListView;
import com.stardust.util.FileSorter; import com.stardust.util.FileSorter;
import java.io.File; import java.io.File;
@@ -25,6 +26,7 @@ public class ScriptList {
private boolean mFileSortedAscending; private boolean mFileSortedAscending;
private int mFileSortType = SORT_TYPE_NAME; private int mFileSortType = SORT_TYPE_NAME;
public boolean isDirSortedAscending() { public boolean isDirSortedAscending() {
return mDirSortedAscending; return mDirSortedAscending;
} }
@@ -110,4 +112,13 @@ public class ScriptList {
FileSorter.sort(mDirectories, getComparator(mDirSortType), mDirSortedAscending); FileSorter.sort(mDirectories, getComparator(mDirSortType), mDirSortedAscending);
FileSorter.sort(mScriptFiles, getComparator(mFileSortType), mFileSortedAscending); FileSorter.sort(mScriptFiles, getComparator(mFileSortType), mFileSortedAscending);
} }
public ScriptList cloneConfig() {
ScriptList list = new ScriptList();
list.mFileSortType = mFileSortType;
list.mDirSortType = mDirSortType;
list.mDirSortedAscending = mDirSortedAscending;
list.mFileSortedAscending = mFileSortedAscending;
return list;
}
} }

View File

@@ -9,6 +9,7 @@ import android.os.Build;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient; import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest; import android.webkit.WebResourceRequest;
import android.webkit.WebSettings; import android.webkit.WebSettings;
@@ -80,6 +81,39 @@ public class EWebView extends FrameLayout implements SwipeRefreshLayout.OnRefres
super.onProgressChanged(view, newProgress); super.onProgressChanged(view, newProgress);
mProgressBar.setProgress(newProgress); mProgressBar.setProgress(newProgress);
} }
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
}
// For Android >= 3.0
@SuppressWarnings("unchecked")
public void openFileChooser(ValueCallback valueCallback, String acceptType) {
openFileChooser(valueCallback);
}
//For Android >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback,
String acceptType, String capture) {
openFileChooser(valueCallback);
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
openFileChooser(value -> {
if (value == null) {
filePathCallback.onReceiveValue(null);
} else {
filePathCallback.onReceiveValue(new Uri[]{value});
}
});
return true;
}
} }
protected class MyWebViewClient extends WebViewClient { protected class MyWebViewClient extends WebViewClient {
@@ -113,5 +147,7 @@ public class EWebView extends FrameLayout implements SwipeRefreshLayout.OnRefres
} }
return true; return true;
} }
} }
} }

View File

@@ -242,6 +242,9 @@
<string name="text_cancel_download">取消下载</string> <string name="text_cancel_download">取消下载</string>
<string name="text_download">下载</string> <string name="text_download">下载</string>
<string name="format_file_downloaded" formatted="true">文件已保存到 %s</string> <string name="format_file_downloaded" formatted="true">文件已保存到 %s</string>
<string name="confirm_overwrite_file">文件已存在,要覆盖吗?</string>
<string name="text_select_file_to_upload">选择要上传的文件</string>
<string name="text_select_file_to_import">选择要导入的文件</string>
<string-array name="ad_showing_mode_keys"> <string-array name="ad_showing_mode_keys">

View File

@@ -97,7 +97,7 @@ public class PFile extends File {
ArrayList<PFile> files = new ArrayList<>(); ArrayList<PFile> files = new ArrayList<>();
for (int i = 0; i < ss.length; i++) { for (int i = 0; i < ss.length; i++) {
if (!ss[i].startsWith(".")) { if (!ss[i].startsWith(".")) {
files.add(new PFile(ss[i])); files.add(new PFile(this, ss[i]));
} }
} }
return files.toArray(new PFile[files.size()]); return files.toArray(new PFile[files.size()]);