From deda91e6b1c0da83aebf2011d1d2a139ef4cda2a Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Sat, 21 Oct 2017 20:36:45 +0800 Subject: [PATCH] fix: crash when ScriptListView init --- app/build.gradle | 4 +- .../scriptdroid/io/StorageFileProvider.java | 4 +- .../network/download/DownloadManager.java | 62 ++----------------- .../scriptdroid/ui/common/RxDialogs.java | 37 +++++++++++ .../ui/common/ScriptOperations.java | 29 ++++++++- .../ui/filechooser/FileChooseListView.java | 1 + .../filechooser/FileChooserDialogBuilder.java | 11 +++- .../ui/main/community/CommunityWebView.java | 21 +++++++ .../ui/main/scripts/MyScriptListFragment.java | 2 + .../ui/main/scripts/ScriptListView.java | 24 ++++--- .../scriptdroid/ui/viewmodel/ScriptList.java | 11 ++++ .../java/com/stardust/widget/EWebView.java | 36 +++++++++++ app/src/main/res/values/strings.xml | 3 + .../src/main/java/com/stardust/pio/PFile.java | 2 +- 14 files changed, 171 insertions(+), 76 deletions(-) create mode 100644 app/src/main/java/com/stardust/scriptdroid/ui/common/RxDialogs.java diff --git a/app/build.gradle b/app/build.gradle index 90815baa..60416bd3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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 { diff --git a/app/src/main/java/com/stardust/scriptdroid/io/StorageFileProvider.java b/app/src/main/java/com/stardust/scriptdroid/io/StorageFileProvider.java index 9d528910..260c5d30 100644 --- a/app/src/main/java/com/stardust/scriptdroid/io/StorageFileProvider.java +++ b/app/src/main/java/com/stardust/scriptdroid/io/StorageFileProvider.java @@ -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(); diff --git a/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java b/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java index 83bb7471..777a70ec 100644 --- a/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java +++ b/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java @@ -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 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 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 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); diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/common/RxDialogs.java b/app/src/main/java/com/stardust/scriptdroid/ui/common/RxDialogs.java new file mode 100644 index 00000000..9a6a5410 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/common/RxDialogs.java @@ -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 confirm(Context context, String text) { + PublishSubject 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 confirm(Context context, int res) { + return confirm(context, context.getString(res)); + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java b/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java index 2abcb3e3..d9d32c71 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java @@ -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 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 { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooseListView.java b/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooseListView.java index ddf0e0aa..31fe13d6 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooseListView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooseListView.java @@ -159,6 +159,7 @@ public class FileChooseListView extends ScriptListView { DirectoryViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); + mCheckBox.setVisibility(mCanChooseDir ? VISIBLE : GONE); } @Override diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooserDialogBuilder.java b/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooserDialogBuilder.java index 91fc8e05..71a80435 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooserDialogBuilder.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/filechooser/FileChooserDialogBuilder.java @@ -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); diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/community/CommunityWebView.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/community/CommunityWebView.java index e1c29eac..ef057411 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/community/CommunityWebView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/community/CommunityWebView.java @@ -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 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(); + } } } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/MyScriptListFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/MyScriptListFragment.java index 28b94535..5b41664d 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/MyScriptListFragment.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/MyScriptListFragment.java @@ -133,6 +133,8 @@ public class MyScriptListFragment extends ViewPagerFragment implements BackPress .newScriptFile(); break; case 2: + new ScriptOperations(getContext(), mScriptFileList, mScriptFileList.getCurrentDirectory()) + .importFile(); break; } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/ScriptListView.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/ScriptListView.java index 4b71ebe2..e83b37a0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/ScriptListView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/scripts/ScriptListView.java @@ -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 diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/viewmodel/ScriptList.java b/app/src/main/java/com/stardust/scriptdroid/ui/viewmodel/ScriptList.java index 3a3efe51..712080a8 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/viewmodel/ScriptList.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/viewmodel/ScriptList.java @@ -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; + } } diff --git a/app/src/main/java/com/stardust/widget/EWebView.java b/app/src/main/java/com/stardust/widget/EWebView.java index 41cdd68e..db875f82 100644 --- a/app/src/main/java/com/stardust/widget/EWebView.java +++ b/app/src/main/java/com/stardust/widget/EWebView.java @@ -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 valueCallback) { + + } + + // For Android >= 3.0 + @SuppressWarnings("unchecked") + public void openFileChooser(ValueCallback valueCallback, String acceptType) { + openFileChooser(valueCallback); + } + + //For Android >= 4.1 + public void openFileChooser(ValueCallback valueCallback, + String acceptType, String capture) { + openFileChooser(valueCallback); + } + + // For Android >= 5.0 + @Override + public boolean onShowFileChooser(WebView webView, + ValueCallback 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; } + + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b60e7ecd..8029ea66 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -242,6 +242,9 @@ 取消下载 下载 文件已保存到 %s + 文件已存在,要覆盖吗? + 选择要上传的文件 + 选择要导入的文件 diff --git a/common/src/main/java/com/stardust/pio/PFile.java b/common/src/main/java/com/stardust/pio/PFile.java index 04130c0b..bbe5d2d4 100644 --- a/common/src/main/java/com/stardust/pio/PFile.java +++ b/common/src/main/java/com/stardust/pio/PFile.java @@ -97,7 +97,7 @@ public class PFile extends File { ArrayList 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()]);