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"
minSdkVersion 17
targetSdkVersion 23
versionCode 209
versionName "3.0.0 Alpha10"
versionCode 210
versionName "3.0.0 Alpha11"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
ndk {

View File

@ -32,6 +32,8 @@ public class StorageFileProvider {
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 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 {
@ -75,8 +77,6 @@ public class StorageFileProvider {
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 EventBus mDirectoryEventBus = new EventBus();

View File

@ -1,31 +1,16 @@
package com.stardust.scriptdroid.network.download;
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.util.concurrent.TimeUnit;
import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.PublishSubject;
import zlc.season.rxdownload3.RxDownload;
import zlc.season.rxdownload3.core.Failed;
import zlc.season.rxdownload3.core.Mission;
import zlc.season.rxdownload3.core.Status;
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.
*/
@ -34,15 +19,11 @@ public class DownloadManager {
private static DownloadManager sInstance;
private android.app.DownloadManager mManager;
private Context mContext;
private Handler mHandler;
public DownloadManager(Context context) {
mContext = context;
mHandler = new Handler();
mManager = (android.app.DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
}
public static DownloadManager getInstance(Context context) {
@ -67,8 +48,10 @@ public class DownloadManager {
PublishSubject<Integer> progress = PublishSubject.create();
RxDownload.INSTANCE.create(mission)
.subscribe(status -> {
int p = (int) Math.floor((float) status.getDownloadSize() / status.getTotalSize() * 100);
progress.onNext(p);
if (status.getTotalSize() > 0) {
int p = (int) Math.floor((float) status.getDownloadSize() / status.getTotalSize() * 100);
progress.onNext(p);
}
if (status instanceof Succeed) {
progress.onComplete();
RxDownload.INSTANCE.delete(mission);
@ -80,43 +63,6 @@ public class DownloadManager {
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) {
RxDownload.INSTANCE.stop(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;
import android.content.Context;
import android.os.Environment;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -34,7 +35,6 @@ import org.reactivestreams.Subscriber;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -256,13 +256,26 @@ public class ScriptOperations {
public Observable<ScriptFile> download(String url) {
String fileName = DownloadManager.parseFileNameLocally(url);
MaterialDialog progressDialog = createDownloadProgressDialog(url, fileName);
return new FileChooserDialogBuilder(mContext)
.title(R.string.text_select_save_path)
.chooseDir()
.singleChoice()
.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) {
@ -302,6 +315,16 @@ public class ScriptOperations {
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 {

View File

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

View File

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

View File

@ -1,17 +1,24 @@
package com.stardust.scriptdroid.ui.main.community;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.support.design.widget.BottomSheetDialog;
import android.support.design.widget.Snackbar;
import android.util.AttributeSet;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import com.stardust.pio.PFile;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.io.StorageFileProvider;
import com.stardust.scriptdroid.model.script.ScriptFile;
import com.stardust.scriptdroid.model.script.Scripts;
import com.stardust.scriptdroid.network.download.DownloadManager;
import com.stardust.scriptdroid.ui.common.OptionListView;
import com.stardust.scriptdroid.ui.common.ScriptOperations;
import com.stardust.scriptdroid.ui.filechooser.FileChooserDialogBuilder;
import com.stardust.widget.EWebView;
import java.util.regex.Pattern;
@ -41,6 +48,7 @@ public class CommunityWebView extends EWebView {
private void init() {
getWebView().setWebViewClient(new MyWebViewClient());
getWebView().setWebChromeClient(new MyWebChromeClient());
}
private void shouldScriptOptionsDialog(String url) {
@ -111,6 +119,19 @@ public class CommunityWebView extends EWebView {
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();
break;
case 2:
new ScriptOperations(getContext(), mScriptFileList, mScriptFileList.getCurrentDirectory())
.importFile();
break;
}

View File

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

View File

@ -1,6 +1,7 @@
package com.stardust.scriptdroid.ui.viewmodel;
import com.stardust.scriptdroid.model.script.ScriptFile;
import com.stardust.scriptdroid.ui.main.scripts.ScriptListView;
import com.stardust.util.FileSorter;
import java.io.File;
@ -25,6 +26,7 @@ public class ScriptList {
private boolean mFileSortedAscending;
private int mFileSortType = SORT_TYPE_NAME;
public boolean isDirSortedAscending() {
return mDirSortedAscending;
}
@ -110,4 +112,13 @@ public class ScriptList {
FileSorter.sort(mDirectories, getComparator(mDirSortType), mDirSortedAscending);
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.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
@ -80,6 +81,39 @@ public class EWebView extends FrameLayout implements SwipeRefreshLayout.OnRefres
super.onProgressChanged(view, 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 {
@ -113,5 +147,7 @@ public class EWebView extends FrameLayout implements SwipeRefreshLayout.OnRefres
}
return true;
}
}
}

View File

@ -242,6 +242,9 @@
<string name="text_cancel_download">取消下载</string>
<string name="text_download">下载</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">

View File

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