diff --git a/app/build.gradle b/app/build.gradle index 0ac449cbb..53376f804 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' -def AAVersion = '4.5.2' +def AAVersion = '4.8.0' def SupportLibVersion = '28.0.0' File propFile =new File("E:/资料/jks/autojs-app/sign.properties"); @@ -179,9 +179,9 @@ dependencies { androidTestImplementation('androidx.test.espresso:espresso-core:3.1.1-alpha01', { exclude group: 'com.android.support', module: 'support-annotations' }) - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.2' // Kotlin - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1-native-mt" // Android Annotations annotationProcessor "org.androidannotations:androidannotations:$AAVersion" kapt "org.androidannotations:androidannotations:$AAVersion" @@ -221,42 +221,42 @@ dependencies { //??? implementation 'com.wang.avi:library:2.1.3' //Commons-lang - implementation 'org.apache.commons:commons-lang3:3.6' + implementation 'org.apache.commons:commons-lang3:3.12.0' // 证书签名相关 implementation 'com.madgag.spongycastle:bcpkix-jdk15on:1.56.0.0' //Expandable RecyclerView implementation 'com.thoughtbot:expandablerecyclerview:1.3' // implementation 'org.signal.autox:apkbuilder:1.0.3' // RxJava - implementation "io.reactivex.rxjava2:rxjava:2.2.3" - implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' + implementation "io.reactivex.rxjava2:rxjava:2.2.21" + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' // Retrofit - implementation 'com.squareup.retrofit2:retrofit:2.4.0' - implementation 'com.squareup.retrofit2:converter-gson:2.3.0' - implementation 'com.squareup.retrofit2:converter-gson:2.3.0' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' //Glide implementation('com.github.bumptech.glide:glide:4.8.0', { exclude group: 'com.android.support' }) - kapt 'com.github.bumptech.glide:compiler:4.8.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' + kapt 'com.github.bumptech.glide:compiler:4.13.1' + annotationProcessor 'com.github.bumptech.glide:compiler:4.13.1' //joda time - implementation 'joda-time:joda-time:2.9.9' + implementation 'joda-time:joda-time:2.10.13' // Tasker Plugin implementation 'com.twofortyfouram:android-plugin-client-sdk-for-locale:4.0.3' // Flurry - implementation 'com.flurry.android:analytics:7.0.0@aar' + implementation 'com.flurry.android:analytics:13.1.0@aar' // Bugly - implementation 'com.tencent.bugly:crashreport:2.6.6' + implementation 'com.tencent.bugly:crashreport:4.0.0' // MaterialDialogCommon implementation('com.afollestad.material-dialogs:commons:0.9.2.3', { exclude group: 'com.android.support' }) // Android job - implementation 'com.evernote:android-job:1.2.6' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' + implementation 'com.evernote:android-job:1.4.2' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' // Optional, if you use support library fragments: debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' @@ -265,4 +265,6 @@ dependencies { implementation project(':autojs') implementation project(':apkbuilder') implementation 'androidx.multidex:multidex:2.0.1' + // libs + api fileTree(dir: '/libs', include: ['*.jar']) } diff --git a/app/libs/tbs_sdk_thirdapp_v4.3.0.281_44181_sharewithdownloadwithfile_withoutGame_obfs_20220407_152305.jar b/app/libs/tbs_sdk_thirdapp_v4.3.0.281_44181_sharewithdownloadwithfile_withoutGame_obfs_20220407_152305.jar new file mode 100644 index 000000000..9837444ec Binary files /dev/null and b/app/libs/tbs_sdk_thirdapp_v4.3.0.281_44181_sharewithdownloadwithfile_withoutGame_obfs_20220407_152305.jar differ diff --git a/app/src/main/java/org/autojs/autojs/App.kt b/app/src/main/java/org/autojs/autojs/App.kt index 0770ae113..276e6ce3c 100644 --- a/app/src/main/java/org/autojs/autojs/App.kt +++ b/app/src/main/java/org/autojs/autojs/App.kt @@ -1,21 +1,18 @@ package org.autojs.autojs import android.annotation.SuppressLint -import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri -import androidx.multidex.MultiDexApplication import android.view.View import android.widget.ImageView import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.multidex.MultiDexApplication import com.bumptech.glide.Glide import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.transition.Transition -import com.evernote.android.job.JobRequest import com.flurry.android.FlurryAgent -import com.squareup.leakcanary.LeakCanary import com.stardust.app.GlobalAppContext import com.stardust.autojs.core.ui.inflater.ImageLoader import com.stardust.autojs.core.ui.inflater.util.Drawables @@ -70,11 +67,6 @@ class App : MultiDexApplication() { crashHandler.setBuglyHandler(Thread.getDefaultUncaughtExceptionHandler()) Thread.setDefaultUncaughtExceptionHandler(crashHandler) - if (LeakCanary.isInAnalyzerProcess(this)) { - // This process is dedicated to LeakCanary for heap analysis. - // You should not init your app in this process. - return - } //LeakCanary.install(this); } diff --git a/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java b/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java index 02b78fcf4..39cbacc93 100644 --- a/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java @@ -1,42 +1,64 @@ package org.autojs.autojs.ui.doc; import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; import android.os.Bundle; -import androidx.annotation.Nullable; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import android.text.TextUtils; -import android.webkit.WebView; +import android.os.Environment; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.widget.Toast; -import org.autojs.autojs.Pref; -import org.autojs.autojs.R; -import org.autojs.autojs.ui.main.QueryEvent; -import org.autojs.autojs.ui.main.ViewPagerFragment; +import androidx.annotation.Nullable; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.stardust.util.BackPressedHandler; -import org.autojs.autojs.ui.widget.EWebView; - import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.ViewById; +import org.autojs.autojs.Pref; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; +import org.autojs.autojs.R; +import org.autojs.autojs.tool.SimpleObserver; +import org.autojs.autojs.ui.main.FloatingActionMenu; +import org.autojs.autojs.ui.main.FloatingActionMenu; +import org.autojs.autojs.ui.main.QueryEvent; +import org.autojs.autojs.ui.main.ViewPagerFragment; +import org.autojs.autojs.ui.widget.CallbackBundle; +import org.autojs.autojs.ui.widget.EWebView; +import io.reactivex.android.schedulers.AndroidSchedulers; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -/** - * Created by Stardust on 2017/8/22. - */ @EFragment(R.layout.fragment_online_docs) -public class DocsFragment extends ViewPagerFragment implements BackPressedHandler { +public class DocsFragment extends ViewPagerFragment implements BackPressedHandler, FloatingActionMenu.OnFloatingActionButtonClickListener { public static final String ARGUMENT_URL = "url"; - @ViewById(R.id.eweb_view) + @ViewById(R.id.eweb_view_docs) EWebView mEWebView; + com.tencent.smtt.sdk.WebView mWebView; - WebView mWebView; - - private String mIndexUrl; + private String mIndexUrl = "file:///android_asset/docs/index.html"; private String mPreviousQuery; + static private Dialog mDialog; + public static String tag = "OpenFileDialog"; + static public String sRoot = Environment.getExternalStorageDirectory().getAbsolutePath(); + static final public String sParent = ".."; + static final public String sFolder = "."; + static final public String sEmpty = ""; + static final private String sOnErrorMsg = "No rights to access!"; + private FloatingActionMenu mFloatingActionMenu; public DocsFragment() { @@ -48,31 +70,20 @@ public DocsFragment() { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); + mIndexUrl = getArguments().getString(ARGUMENT_URL, Pref.getDocumentationUrl() + "index.html"); } @AfterViews void setUpViews() { mWebView = mEWebView.getWebView(); - mEWebView.getSwipeRefreshLayout().setOnRefreshListener(() -> { - if (TextUtils.equals(mWebView.getUrl(), mIndexUrl)) { - loadUrl(); - } else { - mEWebView.onRefresh(); - } - }); Bundle savedWebViewState = getArguments().getBundle("savedWebViewState"); if (savedWebViewState != null) { mWebView.restoreState(savedWebViewState); } else { - loadUrl(); + mWebView.loadUrl(mIndexUrl); } } - private void loadUrl() { - mIndexUrl = getArguments().getString(ARGUMENT_URL, Pref.getDocumentationUrl() + "index.html"); - mWebView.loadUrl(mIndexUrl); - } - @Override public void onPause() { @@ -93,9 +104,34 @@ public boolean onBackPressed(Activity activity) { @Override protected void onFabClick(FloatingActionButton fab) { + initFloatingActionMenuIfNeeded(fab); + if (mFloatingActionMenu.isExpanded()) { + mFloatingActionMenu.collapse(); + } else { + mFloatingActionMenu.expand(); + + } + } + private void initFloatingActionMenuIfNeeded(final FloatingActionButton fab) { + if (mFloatingActionMenu != null) + return; + mFloatingActionMenu = getActivity().findViewById(R.id.floating_action_menu); + mFloatingActionMenu.getState() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new SimpleObserver() { + @Override + public void onNext(@io.reactivex.annotations.NonNull Boolean expanding) { + fab.animate() + .rotation(expanding ? 45 : 0) + .setDuration(300) + .start(); + } + }); + mFloatingActionMenu.setOnFloatingActionButtonClickListener(this); } + @Subscribe public void onQuerySummit(QueryEvent event) { if (!isShown()) { @@ -123,4 +159,265 @@ public void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } + + @Override + public void onClick(FloatingActionButton button, int pos) { + switch (pos) { + case 0: + mWebView.loadUrl(mIndexUrl); + break; + case 1: + HashMap images = new HashMap(); + // 下面几句设置各文件类型的图标, 需要你先把图标添加到资源文件夹 + images.put(sRoot, R.drawable.filedialog_root); // 根目录图标 + images.put(sParent, R.drawable.filedialog_folder_up); //返回上一层的图标 + images.put(sFolder, R.drawable.filedialog_folder); //文件夹图标 + images.put(sEmpty, R.drawable.filedialog_file); + mDialog = createDialog(getActivity(), "打开本地文档", new CallbackBundle() { + @Override + public void callback(Bundle bundle) { + String filepath = bundle.getString("filepath").toLowerCase(); + if (filepath.endsWith(".html") || filepath.endsWith(".htm") || filepath.endsWith( + ".xhtml" + ) || filepath.endsWith( + ".xml" + ) || filepath.endsWith( + ".mhtml" + ) || filepath.endsWith( + ".mht" + ) || filepath.endsWith( + ".txt" + ) || filepath.endsWith( + ".js" + ) || filepath.endsWith( + ".log" + ) + ) { + mWebView.loadUrl("file://" + filepath); + } else { + HashMap extraParams = new HashMap(); //define empty hashmap + extraParams.put("style", "1"); + extraParams.put("local", "true"); + com.tencent.smtt.sdk.QbSdk.openFileReader( + getActivity(), + filepath, + extraParams, + new com.tencent.smtt.sdk.ValueCallback() { + @Override + public void onReceiveValue(String it) { + Log.e("TAG", "OpenFile Callback: $it"); + if ("openFileReader open in QB" == it + || "filepath error" == it + || "TbsReaderDialogClosed" == it + || "fileReaderClosed" == it + ) { + com.tencent.smtt.sdk.QbSdk.closeFileReader( + getActivity() + ); + } + } + }); + } + } + }, + "html;htm;xhtml;xml;mhtml;doc;docx;ppt;pptx;xls;xlsx;pdf;txt;epub", + images); + mDialog.show(); + break; + default: + break; + } + + } + + + + // 参数说明 + // context:上下文 + // dialogId:对话框ID + // title:对话框标题 + // callback:一个传递Bundle参数的回调接口 + // suffix:需要选择的文件后缀,比如需要选择wav、mp3文件的时候设置为".wav;.mp3;",注意最后需要一个分号(;) + // images:用来根据后缀显示的图标资源ID。 + // 根目录图标的索引为sRoot; + // 父目录的索引为sParent; + // 文件夹的索引为sFolder; + // 默认图标的索引为sEmpty; + // 其他的直接根据后缀进行索引,比如.wav文件图标的索引为"wav" + public Dialog createDialog(Context context, String title, CallbackBundle callback, String suffix, Map images, String rootDir) { + if (!rootDir.isEmpty() && (new File(rootDir)).isDirectory()) { + sRoot = rootDir; + } + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setView(new FileSelectView(context, callback, suffix, images)); + mDialog = builder.create(); + mDialog.setTitle(title); + return mDialog; + } + + public Dialog createDialog(Context context, String title, CallbackBundle callback, String suffix, Map images) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setView(new FileSelectView(context, callback, suffix, images)); + mDialog = builder.create(); + //dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setTitle(title); + return mDialog; + } + + static class FileSelectView extends ListView implements AdapterView.OnItemClickListener { + + + private CallbackBundle callback = null; + private String path = sRoot; + private List> list = null; + private String suffix = null; + + private Map imageMap = null; + + public FileSelectView(Context context, CallbackBundle callback, String suffix, Map images) { + super(context); + this.imageMap = images; + this.suffix = suffix == null ? "" : suffix.toLowerCase(); + this.callback = callback; + this.setOnItemClickListener(this); + refreshFileList(); + } + + private String getSuffix(String filename) { + int dix = filename.lastIndexOf('.'); + if (dix < 0) { + return ""; + } else { + return filename.substring(dix); + } + } + + private int getImageId(String s) { + if (imageMap == null) { + return 0; + } else if (imageMap.containsKey(s)) { + return imageMap.get(s); + } else if (imageMap.containsKey(sEmpty)) { + return imageMap.get(sEmpty); + } else { + return 0; + } + } + + private int refreshFileList() { + // 刷新文件列表 + File[] files = null; + try { + files = new File(path).listFiles(); + } catch (Exception e) { + files = null; + } + if (files == null) { + // 访问出错 + Toast.makeText(getContext(), sOnErrorMsg, Toast.LENGTH_SHORT).show(); + return -1; + } + if (list != null) { + list.clear(); + } else { + list = new ArrayList>(files.length); + } + + // 用来先保存文件夹和文件夹的两个列表 + ArrayList> lfolders = new ArrayList>(); + ArrayList> lfiles = new ArrayList>(); + + if (!this.path.equals(sRoot)) { + // 添加根目录 和 上一层目录 + Map map = new HashMap(); + map.put("name", sRoot); + map.put("path", sRoot); + map.put("img", getImageId(sRoot)); + list.add(map); + + map = new HashMap(); + map.put("name", sParent); + map.put("path", path); + map.put("img", getImageId(sParent)); + list.add(map); + } + + for (File file : files) { + if (file.isDirectory() && file.listFiles() != null) { + // 添加文件夹 + Map map = new HashMap(); + map.put("name", file.getName()); + map.put("path", file.getPath()); + map.put("img", getImageId(sFolder)); + lfolders.add(map); + } else if (file.isFile()) { + // 添加文件 + String sf = getSuffix(file.getName()).toLowerCase(); +// Toast.makeText(getContext(), sf, Toast.LENGTH_SHORT).show(); + String[] suffixList = suffix.split(";"); + boolean hasSuffix = false; + for (String item : suffixList) { + if (sf.contains("." + item)) { + hasSuffix = true; + break; + } + } + if (suffix == null || suffix.length() == 0 || (sf.length() > 0 && hasSuffix)) { + Map map = new HashMap(); + map.put("name", file.getName()); + map.put("path", file.getPath()); + map.put("img", getImageId(suffixList[0])); + lfiles.add(map); + } + } + } + + list.addAll(lfolders); // 先添加文件夹,确保文件夹显示在上面 + list.addAll(lfiles); //再添加文件 + + + SimpleAdapter adapter = new SimpleAdapter(getContext(), list, R.layout.filedialogitem, new String[]{"img", "name", "path"}, new int[]{R.id.filedialogitem_img, R.id.filedialogitem_name, R.id.filedialogitem_path}); + this.setAdapter(adapter); + return files.length; + } + + @Override + public void onItemClick(AdapterView parent, View v, int position, long id) { + // 条目选择 + String pt = (String) list.get(position).get("path"); + String fn = (String) list.get(position).get("name"); + if (fn.equals(sRoot) || fn.equals(sParent)) { + // 如果是更目录或者上一层 + File fl = new File(pt); + String ppt = fl.getParent(); + if (ppt != null) { + // 返回上一层 + path = ppt; + } else { + // 返回更目录 + path = sRoot; + } + } else { + File fl = new File(pt); + if (fl.isFile()) { + // 如果是文件 + mDialog.dismiss(); // 让文件夹对话框消失 + + // 设置回调的返回值 + Bundle bundle = new Bundle(); + bundle.putString("filepath", pt); + bundle.putString("filename", fn); + // 调用事先设置的回调函数 + this.callback.callback(bundle); + return; + } else if (fl.isDirectory()) { + // 如果是文件夹 + // 那么进入选中的文件夹 + path = pt; + } + } + this.refreshFileList(); + } + } + } diff --git a/app/src/main/java/org/autojs/autojs/ui/doc/DocumentationActivity.java b/app/src/main/java/org/autojs/autojs/ui/doc/DocumentationActivity.java index bb8fe51a7..36a21ca1b 100644 --- a/app/src/main/java/org/autojs/autojs/ui/doc/DocumentationActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/doc/DocumentationActivity.java @@ -19,10 +19,10 @@ public class DocumentationActivity extends BaseActivity { public static final String EXTRA_URL = "url"; - @ViewById(R.id.eweb_view) + @ViewById(R.id.eweb_view_documentation) EWebView mEWebView; - WebView mWebView; + com.tencent.smtt.sdk.WebView mWebView; @AfterViews void setUpViews() { diff --git a/app/src/main/java/org/autojs/autojs/ui/doc/ManualDialog.java b/app/src/main/java/org/autojs/autojs/ui/doc/ManualDialog.java index 87b698ebd..ed483edff 100644 --- a/app/src/main/java/org/autojs/autojs/ui/doc/ManualDialog.java +++ b/app/src/main/java/org/autojs/autojs/ui/doc/ManualDialog.java @@ -24,7 +24,7 @@ public class ManualDialog { @BindView(R.id.title) TextView mTitle; - @BindView(R.id.eweb_view) + @BindView(R.id.eweb_view_manual_dialog) EWebView mEWebView; @BindView(R.id.pin_to_left) diff --git a/app/src/main/java/org/autojs/autojs/ui/main/FloatingActionMenu.java b/app/src/main/java/org/autojs/autojs/ui/main/FloatingActionMenu.java index 81526eec2..0135c3b77 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/FloatingActionMenu.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/FloatingActionMenu.java @@ -3,11 +3,15 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; + import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.google.android.material.floatingactionbutton.FloatingActionButton; + import androidx.interpolator.view.animation.FastOutSlowInInterpolator; + import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -30,11 +34,12 @@ public interface OnFloatingActionButtonClickListener { } private static final int[] ICONS = { + R.drawable.ic_floating_action_menu_home, + R.drawable.ic_floating_action_menu_open, R.drawable.ic_floating_action_menu_dir, R.drawable.ic_floating_action_menu_file, - R.drawable.ic_floating_action_menu_open, R.drawable.ic_project}; - private static final int[] LABELS = {R.string.text_directory, R.string.text_file, R.string.text_import, R.string.text_project}; + private static final int[] LABELS = {R.string.text_homepage, R.string.text_import, R.string.text_directory, R.string.text_file, R.string.text_project}; private TextView[] mLabels; private FloatingActionButton[] mFabs; private View[] mFabContainers; diff --git a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java index bc497c1b0..dbd075c78 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java @@ -48,6 +48,8 @@ import org.autojs.autojs.ui.log.LogActivity_; import org.autojs.autojs.ui.main.community.CommunityFragment; import org.autojs.autojs.ui.main.community.CommunityFragment_; +import org.autojs.autojs.ui.main.community.WebFragment; +import org.autojs.autojs.ui.main.community.WebFragment_; import org.autojs.autojs.ui.main.market.MarketFragment_; import org.autojs.autojs.ui.main.scripts.MyScriptListFragment_; import org.autojs.autojs.ui.main.task.TaskManagerFragment_; @@ -166,10 +168,11 @@ private void setUpTabViewPager() { TabLayout tabLayout = $(R.id.tab); mPagerAdapter = new FragmentPagerAdapterBuilder(this) .add(new MyScriptListFragment_(), R.string.text_file) + .add(new TaskManagerFragment_(), R.string.text_manage) + .add(new WebFragment_(), R.string.text_web) .add(new DocsFragment_(), R.string.text_tutorial) .add(new CommunityFragment_(), R.string.text_community) .add(new MarketFragment_(), R.string.text_market) - .add(new TaskManagerFragment_(), R.string.text_manage) .build(); mViewPager.setAdapter(mPagerAdapter); tabLayout.setupWithViewPager(mViewPager); diff --git a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java index 7603e2966..4a83edff6 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java @@ -1,15 +1,36 @@ package org.autojs.autojs.ui.main.community; import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.widget.Toast; + import androidx.annotation.Nullable; + import com.google.android.material.floatingactionbutton.FloatingActionButton; -import android.webkit.WebView; import org.autojs.autojs.R; + import org.autojs.autojs.network.NodeBB; +import org.autojs.autojs.tool.SimpleObserver; +import org.autojs.autojs.ui.common.ScriptOperations; +import org.autojs.autojs.ui.doc.DocsFragment; +import org.autojs.autojs.ui.main.FloatingActionMenu; +import org.autojs.autojs.ui.main.FloatingActionMenu; import org.autojs.autojs.ui.main.QueryEvent; import org.autojs.autojs.ui.main.ViewPagerFragment; +import org.autojs.autojs.ui.project.ProjectConfigActivity; +import org.autojs.autojs.ui.widget.CallbackBundle; +import io.reactivex.android.schedulers.AndroidSchedulers; + import com.stardust.util.BackPressedHandler; import org.androidannotations.annotations.AfterViews; @@ -18,19 +39,24 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; +import java.io.File; import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Created by Stardust on 2017/8/22. */ @EFragment(R.layout.fragment_community) -public class CommunityFragment extends ViewPagerFragment implements BackPressedHandler { +public class CommunityFragment extends ViewPagerFragment implements BackPressedHandler, FloatingActionMenu.OnFloatingActionButtonClickListener { public static class LoadUrl { - public final String url; + public final String mIndexUrl; public LoadUrl(String url) { - this.url = url; + this.mIndexUrl = url; } } @@ -44,16 +70,29 @@ public VisibilityChange(boolean visible) { } private static final String POSTS_PAGE_PATTERN = "[\\S\\s]+/topic/[0-9]+/[\\S\\s]+"; + static private Dialog mDialog; + public static String tag = "OpenFileDialog"; + static public String sRoot = Environment.getExternalStorageDirectory().getAbsolutePath(); + static final public String sParent = ".."; + static final public String sFolder = "."; + static final public String sEmpty = ""; + static final private String sOnErrorMsg = "No rights to access!"; + private FloatingActionMenu mFloatingActionMenu; - @ViewById(R.id.eweb_view) + @ViewById(R.id.eweb_view_community) CommunityWebView mEWebView; - WebView mWebView; + com.tencent.smtt.sdk.WebView mWebView; + + @ViewById(R.id.fab) + FloatingActionButton mFab; public CommunityFragment() { super(0); setArguments(new Bundle()); } + private String mIndexUrl = "http://www.autoxjs.com/"; + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -63,12 +102,11 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @AfterViews void setUpViews() { mWebView = mEWebView.getWebView(); - String url = "http://www.autoxjs.com/"; Bundle savedWebViewState = getArguments().getBundle("savedWebViewState"); if (savedWebViewState != null) { mWebView.restoreState(savedWebViewState); } else { - mWebView.loadUrl(url); + mWebView.loadUrl(mIndexUrl); } } @@ -92,16 +130,36 @@ public boolean onBackPressed(Activity activity) { @Override protected void onFabClick(FloatingActionButton fab) { - if (isInPostsPage()) { - mWebView.loadUrl("javascript:$('button[component=\"topic/reply\"]').click()"); + initFloatingActionMenuIfNeeded(fab); + if (mFloatingActionMenu.isExpanded()) { + mFloatingActionMenu.collapse(); } else { - mWebView.loadUrl("javascript:$('#new_topic').click()"); + mFloatingActionMenu.expand(); + } } + private void initFloatingActionMenuIfNeeded(final FloatingActionButton fab) { + if (mFloatingActionMenu != null) + return; + mFloatingActionMenu = getActivity().findViewById(R.id.floating_action_menu); + mFloatingActionMenu.getState() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new SimpleObserver() { + @Override + public void onNext(@io.reactivex.annotations.NonNull Boolean expanding) { + fab.animate() + .rotation(expanding ? 45 : 0) + .setDuration(300) + .start(); + } + }); + mFloatingActionMenu.setOnFloatingActionButtonClickListener(this); + } + @Subscribe public void loadUrl(LoadUrl loadUrl) { - mWebView.loadUrl(NodeBB.url(loadUrl.url)); + mWebView.loadUrl(NodeBB.url(loadUrl.mIndexUrl)); } @Subscribe @@ -117,7 +175,7 @@ public void submitQuery(QueryEvent event) { private boolean isInPostsPage() { String url = mWebView.getUrl(); - return url != null && url.matches(POSTS_PAGE_PATTERN); + return url != null && url.matches(POSTS_PAGE_PATTERN); } @Override @@ -137,4 +195,239 @@ public void onPageHide() { super.onPageHide(); EventBus.getDefault().post(new VisibilityChange(false)); } + + @Override + public void onClick(FloatingActionButton button, int pos) { + switch (pos) { + case 0: + mWebView.loadUrl(mIndexUrl); + break; + case 1: + HashMap images = new HashMap(); + // 下面几句设置各文件类型的图标, 需要你先把图标添加到资源文件夹 + images.put(sRoot, R.drawable.filedialog_root); // 根目录图标 + images.put(sParent, R.drawable.filedialog_folder_up); //返回上一层的图标 + images.put(sFolder, R.drawable.filedialog_folder); //文件夹图标 + images.put(sEmpty, R.drawable.filedialog_file); + mDialog = createDialog(getActivity(), "打开本地文档", new CallbackBundle() { + @Override + public void callback(Bundle bundle) { + String filepath = bundle.getString("filepath").toLowerCase(); + if (filepath.endsWith(".html") || filepath.endsWith(".htm") || filepath.endsWith( + ".xhtml" + ) || filepath.endsWith( + ".xml" + ) || filepath.endsWith( + ".mhtml" + ) || filepath.endsWith( + ".mht" + ) || filepath.endsWith( + ".txt" + ) || filepath.endsWith( + ".js" + ) || filepath.endsWith( + ".log" + ) + ) { + mWebView.loadUrl("file://" + filepath); + } else { + HashMap extraParams = new HashMap(); //define empty hashmap + extraParams.put("style", "1"); + extraParams.put("local", "true"); + com.tencent.smtt.sdk.QbSdk.openFileReader( + getActivity(), + filepath, + extraParams, + new com.tencent.smtt.sdk.ValueCallback() { + @Override + public void onReceiveValue(String it) { + Log.e("TAG", "OpenFile Callback: $it"); + if ("openFileReader open in QB" == it + || "filepath error" == it + || "TbsReaderDialogClosed" == it + || "fileReaderClosed" == it + ) { + com.tencent.smtt.sdk.QbSdk.closeFileReader( + getActivity() + ); + } + } + }); + } + } + }, + "html;htm;xhtml;xml;mhtml;doc;docx;ppt;pptx;xls;xlsx;pdf;txt;epub", + images); + mDialog.show(); + break; + default: + break; + } + } + + public Dialog createDialog(Context context, String title, CallbackBundle callback, String suffix, Map images) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setView(new FileSelectView(context, callback, suffix, images)); + mDialog = builder.create(); + //dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setTitle(title); + return mDialog; + } + + static class FileSelectView extends ListView implements AdapterView.OnItemClickListener { + + + private CallbackBundle callback = null; + private String path = sRoot; + private List> list = null; + private String suffix = null; + + private Map imageMap = null; + + public FileSelectView(Context context, CallbackBundle callback, String suffix, Map images) { + super(context); + this.imageMap = images; + this.suffix = suffix == null ? "" : suffix.toLowerCase(); + this.callback = callback; + this.setOnItemClickListener(this); + refreshFileList(); + } + + private String getSuffix(String filename) { + int dix = filename.lastIndexOf('.'); + if (dix < 0) { + return ""; + } else { + return filename.substring(dix); + } + } + + private int getImageId(String s) { + if (imageMap == null) { + return 0; + } else if (imageMap.containsKey(s)) { + return imageMap.get(s); + } else if (imageMap.containsKey(sEmpty)) { + return imageMap.get(sEmpty); + } else { + return 0; + } + } + + private int refreshFileList() { + // 刷新文件列表 + File[] files = null; + try { + files = new File(path).listFiles(); + } catch (Exception e) { + files = null; + } + if (files == null) { + // 访问出错 + Toast.makeText(getContext(), sOnErrorMsg, Toast.LENGTH_SHORT).show(); + return -1; + } + if (list != null) { + list.clear(); + } else { + list = new ArrayList>(files.length); + } + + // 用来先保存文件夹和文件夹的两个列表 + ArrayList> lfolders = new ArrayList>(); + ArrayList> lfiles = new ArrayList>(); + + if (!this.path.equals(sRoot)) { + // 添加根目录 和 上一层目录 + Map map = new HashMap(); + map.put("name", sRoot); + map.put("path", sRoot); + map.put("img", getImageId(sRoot)); + list.add(map); + + map = new HashMap(); + map.put("name", sParent); + map.put("path", path); + map.put("img", getImageId(sParent)); + list.add(map); + } + + for (File file : files) { + if (file.isDirectory() && file.listFiles() != null) { + // 添加文件夹 + Map map = new HashMap(); + map.put("name", file.getName()); + map.put("path", file.getPath()); + map.put("img", getImageId(sFolder)); + lfolders.add(map); + } else if (file.isFile()) { + // 添加文件 + String sf = getSuffix(file.getName()).toLowerCase(); +// Toast.makeText(getContext(), sf, Toast.LENGTH_SHORT).show(); + String[] suffixList = suffix.split(";"); + boolean hasSuffix = false; + for (String item : suffixList) { + if (sf.contains("." + item)) { + hasSuffix = true; + break; + } + } + if (suffix == null || suffix.length() == 0 || (sf.length() > 0 && hasSuffix)) { + Map map = new HashMap(); + map.put("name", file.getName()); + map.put("path", file.getPath()); + map.put("img", getImageId(suffixList[0])); + lfiles.add(map); + } + } + } + + list.addAll(lfolders); // 先添加文件夹,确保文件夹显示在上面 + list.addAll(lfiles); //再添加文件 + + + SimpleAdapter adapter = new SimpleAdapter(getContext(), list, R.layout.filedialogitem, new String[]{"img", "name", "path"}, new int[]{R.id.filedialogitem_img, R.id.filedialogitem_name, R.id.filedialogitem_path}); + this.setAdapter(adapter); + return files.length; + } + + @Override + public void onItemClick(AdapterView parent, View v, int position, long id) { + // 条目选择 + String pt = (String) list.get(position).get("path"); + String fn = (String) list.get(position).get("name"); + if (fn.equals(sRoot) || fn.equals(sParent)) { + // 如果是更目录或者上一层 + File fl = new File(pt); + String ppt = fl.getParent(); + if (ppt != null) { + // 返回上一层 + path = ppt; + } else { + // 返回更目录 + path = sRoot; + } + } else { + File fl = new File(pt); + if (fl.isFile()) { + // 如果是文件 + mDialog.dismiss(); // 让文件夹对话框消失 + + // 设置回调的返回值 + Bundle bundle = new Bundle(); + bundle.putString("filepath", pt); + bundle.putString("filename", fn); + // 调用事先设置的回调函数 + this.callback.callback(bundle); + return; + } else if (fl.isDirectory()) { + // 如果是文件夹 + // 那么进入选中的文件夹 + path = pt; + } + } + this.refreshFileList(); + } + } + } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java index a2a96a501..e636181f4 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java @@ -3,18 +3,17 @@ import android.annotation.SuppressLint; import android.content.Context; import android.net.Uri; - -import com.google.android.material.bottomsheet.BottomSheetDialog; -import com.google.android.material.snackbar.Snackbar; import android.util.AttributeSet; import android.webkit.ValueCallback; -import android.webkit.WebView; import android.widget.Toast; +import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.google.android.material.snackbar.Snackbar; + import org.autojs.autojs.Pref; import org.autojs.autojs.R; -import org.autojs.autojs.network.NodeBB; import org.autojs.autojs.model.script.Scripts; +import org.autojs.autojs.network.NodeBB; import org.autojs.autojs.network.download.DownloadManager; import org.autojs.autojs.ui.common.OptionListView; import org.autojs.autojs.ui.common.ScriptOperations; @@ -111,7 +110,7 @@ private class MyWebViewClient extends EWebView.MyWebViewClient { private final Pattern UPLOAD_FILE_PATTERN = Pattern.compile(NodeBB.url("assets/uploads/files/.+(\\.js|\\.auto)")); @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { + public boolean shouldOverrideUrlLoading(com.tencent.smtt.sdk.WebView view, String url) { if (UPLOAD_FILE_PATTERN.matcher(url).matches()) { shouldScriptOptionsDialog(url); return true; @@ -120,7 +119,7 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { } @Override - public void onPageFinished(WebView view, String url) { + public void onPageFinished(com.tencent.smtt.sdk.WebView view, String url) { evalJavaScript("$('#header').hide();$('#content').css({ top: '0', position: 'absolute' });"); } } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/community/WebFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/community/WebFragment.java new file mode 100644 index 000000000..c941c772e --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/main/community/WebFragment.java @@ -0,0 +1,427 @@ +package org.autojs.autojs.ui.main.community; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.stardust.util.BackPressedHandler; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.ViewById; +import org.autojs.autojs.R; +import org.autojs.autojs.network.NodeBB; +import org.autojs.autojs.tool.SimpleObserver; +import org.autojs.autojs.ui.main.FloatingActionMenu; +import org.autojs.autojs.ui.main.QueryEvent; +import org.autojs.autojs.ui.main.ViewPagerFragment; +import org.autojs.autojs.ui.widget.CallbackBundle; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.io.File; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.reactivex.android.schedulers.AndroidSchedulers; + +/** + * Created by Stardust on 2017/8/22. + */ +@EFragment(R.layout.fragment_web) +public class WebFragment extends ViewPagerFragment implements BackPressedHandler, FloatingActionMenu.OnFloatingActionButtonClickListener { + + public static class LoadUrl { + public final String mIndexUrl; + + public LoadUrl(String url) { + this.mIndexUrl = url; + } + + } + + public static class VisibilityChange { + public final boolean visible; + + public VisibilityChange(boolean visible) { + this.visible = visible; + } + } + + private static final String POSTS_PAGE_PATTERN = "[\\S\\s]+/topic/[0-9]+/[\\S\\s]+"; + static private Dialog mDialog; + public static String tag = "OpenFileDialog"; + static public String sRoot = Environment.getExternalStorageDirectory().getAbsolutePath(); + static final public String sParent = ".."; + static final public String sFolder = "."; + static final public String sEmpty = ""; + static final private String sOnErrorMsg = "No rights to access!"; + private FloatingActionMenu mFloatingActionMenu; + + @ViewById(R.id.eweb_view_web) + CommunityWebView mEWebView; + com.tencent.smtt.sdk.WebView mWebView; + + @ViewById(R.id.fab) + FloatingActionButton mFab; + + public WebFragment() { + super(0); + setArguments(new Bundle()); + } + + private String mIndexUrl = "https://0x3.com/"; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EventBus.getDefault().register(this); + } + + @AfterViews + void setUpViews() { + mWebView = mEWebView.getWebView(); + Bundle savedWebViewState = getArguments().getBundle("savedWebViewState"); + if (savedWebViewState != null) { + mWebView.restoreState(savedWebViewState); + } else { + mWebView.loadUrl(mIndexUrl); + } + } + + @Override + public void onPause() { + super.onPause(); + Bundle savedWebViewState = new Bundle(); + mWebView.saveState(savedWebViewState); + getArguments().putBundle("savedWebViewState", savedWebViewState); + } + + @Override + public boolean onBackPressed(Activity activity) { + if (mWebView.canGoBack()) { + mWebView.goBack(); + return true; + } + return false; + } + + + @Override + protected void onFabClick(FloatingActionButton fab) { + initFloatingActionMenuIfNeeded(fab); + if (mFloatingActionMenu.isExpanded()) { + mFloatingActionMenu.collapse(); + } else { + mFloatingActionMenu.expand(); + + } + } + + private void initFloatingActionMenuIfNeeded(final FloatingActionButton fab) { + if (mFloatingActionMenu != null) + return; + mFloatingActionMenu = getActivity().findViewById(R.id.floating_action_menu); + mFloatingActionMenu.getState() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new SimpleObserver() { + @Override + public void onNext(@io.reactivex.annotations.NonNull Boolean expanding) { + fab.animate() + .rotation(expanding ? 45 : 0) + .setDuration(300) + .start(); + } + }); + mFloatingActionMenu.setOnFloatingActionButtonClickListener(this); + } + + @Subscribe + public void loadUrl(LoadUrl loadUrl) { + mWebView.loadUrl(NodeBB.url(loadUrl.mIndexUrl)); + } + + @Subscribe + public void submitQuery(QueryEvent event) { + if (!isShown() || event == QueryEvent.CLEAR) { + return; + } + String query = URLEncoder.encode(event.getQuery()); + String url = String.format("http://www.autojs.org/search?term=%s&in=titlesposts", query); + mWebView.loadUrl(url); + event.collapseSearchView(); + } + + private boolean isInPostsPage() { + String url = mWebView.getUrl(); + return url != null && url.matches(POSTS_PAGE_PATTERN); + } + + @Override + public void onDestroy() { + super.onDestroy(); + EventBus.getDefault().unregister(this); + } + + @Override + public void onPageShow() { + super.onPageShow(); + EventBus.getDefault().post(new VisibilityChange(true)); + } + + @Override + public void onPageHide() { + super.onPageHide(); + EventBus.getDefault().post(new VisibilityChange(false)); + } + + @Override + public void onClick(FloatingActionButton button, int pos) { + switch (pos) { + case 0: + mWebView.loadUrl(mIndexUrl); + break; + case 1: + HashMap images = new HashMap(); + // 下面几句设置各文件类型的图标, 需要你先把图标添加到资源文件夹 + images.put(sRoot, R.drawable.filedialog_root); // 根目录图标 + images.put(sParent, R.drawable.filedialog_folder_up); //返回上一层的图标 + images.put(sFolder, R.drawable.filedialog_folder); //文件夹图标 + images.put(sEmpty, R.drawable.filedialog_file); + mDialog = createDialog(getActivity(), "打开本地文档", new CallbackBundle() { + @Override + public void callback(Bundle bundle) { + String filepath = bundle.getString("filepath").toLowerCase(); + if (filepath.endsWith(".html") || filepath.endsWith(".htm") || filepath.endsWith( + ".xhtml" + ) || filepath.endsWith( + ".xml" + ) || filepath.endsWith( + ".mhtml" + ) || filepath.endsWith( + ".mht" + ) || filepath.endsWith( + ".txt" + ) || filepath.endsWith( + ".js" + ) || filepath.endsWith( + ".log" + ) + ) { + mWebView.loadUrl("file://" + filepath); + } else { + HashMap extraParams = new HashMap(); //define empty hashmap + extraParams.put("style", "1"); + extraParams.put("local", "true"); + com.tencent.smtt.sdk.QbSdk.openFileReader( + getActivity(), + filepath, + extraParams, + new com.tencent.smtt.sdk.ValueCallback() { + @Override + public void onReceiveValue(String it) { + Log.e("TAG", "OpenFile Callback: $it"); + if ("openFileReader open in QB" == it + || "filepath error" == it + || "TbsReaderDialogClosed" == it + || "fileReaderClosed" == it + ) { + com.tencent.smtt.sdk.QbSdk.closeFileReader( + getActivity() + ); + } + } + }); + } + } + }, + "html;htm;xhtml;xml;mhtml;doc;docx;ppt;pptx;xls;xlsx;pdf;txt;epub", + images); + mDialog.show(); + break; + default: + break; + } + } + + public Dialog createDialog(Context context, String title, CallbackBundle callback, String suffix, Map images) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setView(new FileSelectView(context, callback, suffix, images)); + mDialog = builder.create(); + //dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setTitle(title); + return mDialog; + } + + static class FileSelectView extends ListView implements AdapterView.OnItemClickListener { + + + private CallbackBundle callback = null; + private String path = sRoot; + private List> list = null; + private String suffix = null; + + private Map imageMap = null; + + public FileSelectView(Context context, CallbackBundle callback, String suffix, Map images) { + super(context); + this.imageMap = images; + this.suffix = suffix == null ? "" : suffix.toLowerCase(); + this.callback = callback; + this.setOnItemClickListener(this); + refreshFileList(); + } + + private String getSuffix(String filename) { + int dix = filename.lastIndexOf('.'); + if (dix < 0) { + return ""; + } else { + return filename.substring(dix); + } + } + + private int getImageId(String s) { + if (imageMap == null) { + return 0; + } else if (imageMap.containsKey(s)) { + return imageMap.get(s); + } else if (imageMap.containsKey(sEmpty)) { + return imageMap.get(sEmpty); + } else { + return 0; + } + } + + private int refreshFileList() { + // 刷新文件列表 + File[] files = null; + try { + files = new File(path).listFiles(); + } catch (Exception e) { + files = null; + } + if (files == null) { + // 访问出错 + Toast.makeText(getContext(), sOnErrorMsg, Toast.LENGTH_SHORT).show(); + return -1; + } + if (list != null) { + list.clear(); + } else { + list = new ArrayList>(files.length); + } + + // 用来先保存文件夹和文件夹的两个列表 + ArrayList> lfolders = new ArrayList>(); + ArrayList> lfiles = new ArrayList>(); + + if (!this.path.equals(sRoot)) { + // 添加根目录 和 上一层目录 + Map map = new HashMap(); + map.put("name", sRoot); + map.put("path", sRoot); + map.put("img", getImageId(sRoot)); + list.add(map); + + map = new HashMap(); + map.put("name", sParent); + map.put("path", path); + map.put("img", getImageId(sParent)); + list.add(map); + } + + for (File file : files) { + if (file.isDirectory() && file.listFiles() != null) { + // 添加文件夹 + Map map = new HashMap(); + map.put("name", file.getName()); + map.put("path", file.getPath()); + map.put("img", getImageId(sFolder)); + lfolders.add(map); + } else if (file.isFile()) { + // 添加文件 + String sf = getSuffix(file.getName()).toLowerCase(); +// Toast.makeText(getContext(), sf, Toast.LENGTH_SHORT).show(); + String[] suffixList = suffix.split(";"); + boolean hasSuffix = false; + for (String item : suffixList) { + if (sf.contains("." + item)) { + hasSuffix = true; + break; + } + } + if (suffix == null || suffix.length() == 0 || (sf.length() > 0 && hasSuffix)) { + Map map = new HashMap(); + map.put("name", file.getName()); + map.put("path", file.getPath()); + map.put("img", getImageId(suffixList[0])); + lfiles.add(map); + } + } + } + + list.addAll(lfolders); // 先添加文件夹,确保文件夹显示在上面 + list.addAll(lfiles); //再添加文件 + + + SimpleAdapter adapter = new SimpleAdapter(getContext(), list, R.layout.filedialogitem, new String[]{"img", "name", "path"}, new int[]{R.id.filedialogitem_img, R.id.filedialogitem_name, R.id.filedialogitem_path}); + this.setAdapter(adapter); + return files.length; + } + + @Override + public void onItemClick(AdapterView parent, View v, int position, long id) { + // 条目选择 + String pt = (String) list.get(position).get("path"); + String fn = (String) list.get(position).get("name"); + if (fn.equals(sRoot) || fn.equals(sParent)) { + // 如果是更目录或者上一层 + File fl = new File(pt); + String ppt = fl.getParent(); + if (ppt != null) { + // 返回上一层 + path = ppt; + } else { + // 返回更目录 + path = sRoot; + } + } else { + File fl = new File(pt); + if (fl.isFile()) { + // 如果是文件 + mDialog.dismiss(); // 让文件夹对话框消失 + + // 设置回调的返回值 + Bundle bundle = new Bundle(); + bundle.putString("filepath", pt); + bundle.putString("filename", fn); + // 调用事先设置的回调函数 + this.callback.callback(bundle); + return; + } else if (fl.isDirectory()) { + // 如果是文件夹 + // 那么进入选中的文件夹 + path = pt; + } + } + this.refreshFileList(); + } + } + +} diff --git a/app/src/main/java/org/autojs/autojs/ui/main/market/MarketFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/market/MarketFragment.java index 68070e1d8..b64307398 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/market/MarketFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/market/MarketFragment.java @@ -29,7 +29,7 @@ public class MarketFragment extends ViewPagerFragment implements BackPressedHand @ViewById(R.id.eweb_view_market) EWebView mEWebView; - WebView mWebView; + com.tencent.smtt.sdk.WebView mWebView; MarketJavascriptInterface javascriptInterface ; diff --git a/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java index 3ec2a9dc2..e0d0d3aa6 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java @@ -3,7 +3,10 @@ import android.app.Activity; import android.os.Bundle; import android.preference.PreferenceManager; +import android.widget.Toast; + import androidx.annotation.Nullable; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.stardust.app.GlobalAppContext; @@ -12,6 +15,7 @@ import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.ViewById; + import org.autojs.autojs.Pref; import org.autojs.autojs.R; import org.autojs.autojs.external.fileprovider.AppFileProvider; @@ -27,6 +31,7 @@ import org.autojs.autojs.ui.project.ProjectConfigActivity; import org.autojs.autojs.ui.project.ProjectConfigActivity_; import org.autojs.autojs.ui.viewmodel.ExplorerItemList; + import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -158,18 +163,21 @@ public void onClick(FloatingActionButton button, int pos) { return; switch (pos) { case 0: - new ScriptOperations(getContext(), mExplorerView, mExplorerView.getCurrentPage()) - .newDirectory(); + //返回主界面 break; case 1: new ScriptOperations(getContext(), mExplorerView, mExplorerView.getCurrentPage()) - .newFile(); + .importFile(); break; case 2: new ScriptOperations(getContext(), mExplorerView, mExplorerView.getCurrentPage()) - .importFile(); + .newDirectory(); break; case 3: + new ScriptOperations(getContext(), mExplorerView, mExplorerView.getCurrentPage()) + .newFile(); + break; + case 4: ProjectConfigActivity_.intent(getContext()) .extra(ProjectConfigActivity.EXTRA_PARENT_DIRECTORY, mExplorerView.getCurrentPage().getPath()) .extra(ProjectConfigActivity.EXTRA_NEW_PROJECT, true) diff --git a/app/src/main/java/org/autojs/autojs/ui/user/WebActivity.java b/app/src/main/java/org/autojs/autojs/ui/user/WebActivity.java index 28bbb16a3..bd8019b21 100644 --- a/app/src/main/java/org/autojs/autojs/ui/user/WebActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/user/WebActivity.java @@ -22,7 +22,7 @@ public class WebActivity extends BaseActivity implements OnActivityResultDelegat private OnActivityResultDelegate.Mediator mMediator = new OnActivityResultDelegate.Mediator(); - @ViewById(R.id.eweb_view) + @ViewById(R.id.eweb_view_web) EWebView mEWebView; @AfterViews diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/CallbackBundle.java b/app/src/main/java/org/autojs/autojs/ui/widget/CallbackBundle.java new file mode 100644 index 000000000..6188e669a --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/widget/CallbackBundle.java @@ -0,0 +1,8 @@ +// filename: CallbackBundle.java +package org.autojs.autojs.ui.widget; + +import android.os.Bundle; + +public interface CallbackBundle { + abstract void callback(Bundle bundle); +} diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/DownloadManagerUtil.java b/app/src/main/java/org/autojs/autojs/ui/widget/DownloadManagerUtil.java new file mode 100644 index 000000000..563853f96 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/widget/DownloadManagerUtil.java @@ -0,0 +1,56 @@ +package org.autojs.autojs.ui.widget; + +import android.app.DownloadManager; +import android.content.Context; +import android.net.Uri; +import android.os.Environment; + +public class DownloadManagerUtil { + private Context mContext; + + public DownloadManagerUtil(Context context) { + mContext = context; + } + + /** + * + * @param url 文件地址 + * @param title 通知栏标题 + * @param desc 通知栏描述 + * @return + */ + public long download(String url, String title, String desc) { + Uri uri = Uri.parse(url); + DownloadManager.Request req = new DownloadManager.Request(uri); + //设置WIFI下进行更新 + //req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); + //下载中和下载完后都显示通知栏 + req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + //使用系统默认的下载路径 此处为应用内 /android/data/packages ,所以兼容7.0 + req.setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, title); + //通知栏标题 + req.setTitle(title); + //通知栏描述信息 + req.setDescription(desc); + //设置类型为.apk + req.setMimeType("application/vnd.android.package-archive"); + //获取下载任务ID + DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); + return dm.enqueue(req); + } + + + /** + * 下载前先移除前一个任务,防止重复下载 + * + * @param downloadId + */ + public void clearCurrentTask(long downloadId) { + DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); + try { + dm.remove(downloadId); + } catch (IllegalArgumentException ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/DownloadReceiver.java b/app/src/main/java/org/autojs/autojs/ui/widget/DownloadReceiver.java new file mode 100644 index 000000000..ec815ddb2 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/widget/DownloadReceiver.java @@ -0,0 +1,47 @@ +package org.autojs.autojs.ui.widget; + +import android.app.DownloadManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.util.Log; + +public class DownloadReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { + long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); + installApk(context, id); + } else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) { + // DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + //获取所有下载任务Ids组 + //long[] ids = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS); + //点击通知栏取消所有下载 + //manager.remove(ids); + //Toast.makeText(context, "下载任务已取消", Toast.LENGTH_SHORT).show(); + //处理 如果还未完成下载,用户点击Notification ,跳转到下载中心 + Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); + viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(viewDownloadIntent); + } + } + + private static void installApk(Context context, long downloadId) { + DownloadManager dManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + Intent intentInstall = new Intent(Intent.ACTION_VIEW); + Uri downloadFileUri = dManager.getUriForDownloadedFile(downloadId); + if (downloadFileUri != null) { + /* Log.d("DownloadManager", downloadFileUri.toString()); + install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive"); + install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(install);*/ + intentInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intentInstall.setDataAndType(downloadFileUri, "application/vnd.android.package-archive"); + context.startActivity(intentInstall); + } else { + Log.e("DownloadManager", "download error"); + } + } +} diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/EWebView.java b/app/src/main/java/org/autojs/autojs/ui/widget/EWebView.java deleted file mode 100644 index 670b496ac..000000000 --- a/app/src/main/java/org/autojs/autojs/ui/widget/EWebView.java +++ /dev/null @@ -1,230 +0,0 @@ -package org.autojs.autojs.ui.widget; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import android.util.AttributeSet; -import android.webkit.ValueCallback; -import android.webkit.WebChromeClient; -import android.webkit.WebResourceRequest; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.FrameLayout; -import android.widget.ProgressBar; - -import com.stardust.app.OnActivityResultDelegate; - -import org.autojs.autojs.R; -import org.autojs.autojs.tool.ImageSelector; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; - -/** - * Created by Stardust on 2017/8/22. - */ - -public class EWebView extends FrameLayout implements SwipeRefreshLayout.OnRefreshListener, OnActivityResultDelegate { - - private static final List IMAGE_TYPES = Arrays.asList("png", "jpg", "bmp"); - private static final int CHOOSE_IMAGE = 42222; - - private WebView mWebView; - private ProgressBar mProgressBar; - private SwipeRefreshLayout mSwipeRefreshLayout; - - public EWebView(Context context) { - super(context); - init(); - } - - public EWebView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public SwipeRefreshLayout getSwipeRefreshLayout() { - return mSwipeRefreshLayout; - } - - private void init() { - inflate(getContext(), R.layout.ewebview, this); - mWebView = findViewById(R.id.web_view); - mSwipeRefreshLayout = findViewById(R.id.swipe_refresh_layout); - mProgressBar = findViewById(R.id.progress_bar); - mSwipeRefreshLayout.setOnRefreshListener(this); - setUpWebView(); - } - - private void setUpWebView() { - WebSettings settings = mWebView.getSettings(); - settings.setUseWideViewPort(true); - settings.setBuiltInZoomControls(true); - settings.setLoadWithOverviewMode(true); - settings.setJavaScriptEnabled(true); - settings.setJavaScriptCanOpenWindowsAutomatically(true); - settings.setDomStorageEnabled(true); - settings.setDisplayZoomControls(false); - mWebView.setWebViewClient(new MyWebViewClient()); - mWebView.setWebChromeClient(new MyWebChromeClient()); - } - - public WebView getWebView() { - return mWebView; - } - - public void evalJavaScript(String script) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - mWebView.evaluateJavascript(script, null); - } else { - mWebView.loadUrl("javascript:" + script); - } - } - - @SuppressLint("CheckResult") - @Override - public void onRefresh() { - mWebView.reload(); - Observable.timer(2, TimeUnit.SECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(t -> mSwipeRefreshLayout.setRefreshing(false)); - - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - - } - - protected class MyWebChromeClient extends WebChromeClient { - - @Override - public void onProgressChanged(WebView view, int newProgress) { - super.onProgressChanged(view, newProgress); - mProgressBar.setProgress(newProgress); - } - - - - //For Android >= 4.1 - public void openFileChooser(ValueCallback valueCallback, - String acceptType, String capture) { - if (acceptType == null) { - openFileChooser(valueCallback, null); - } else { - openFileChooser(valueCallback, acceptType.split(",")); - } - } - - public boolean openFileChooser(ValueCallback valueCallback, - String[] acceptType) { - if (getContext() instanceof OnActivityResultDelegate.DelegateHost && - getContext() instanceof Activity && isImageType(acceptType)) { - chooseImage(valueCallback); - return true; - } - return false; - } - - // For Android >= 5.0 - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @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}); - } - }, fileChooserParams.getAcceptTypes()); - return true; - } - - - } - - private void chooseImage(ValueCallback valueCallback) { - DelegateHost delegateHost = ((OnActivityResultDelegate.DelegateHost) getContext()); - Mediator mediator = delegateHost.getOnActivityResultDelegateMediator(); - Activity activity = (Activity) getContext(); - new ImageSelector(activity, mediator, (selector, uri) -> valueCallback.onReceiveValue(uri)) - .disposable() - .select(); - } - - private boolean isImageType(String[] acceptTypes) { - if (acceptTypes == null) { - return false; - } - for (String acceptType : acceptTypes) { - for (String imageType : IMAGE_TYPES) { - if (acceptType.contains(imageType)) { - return true; - } - } - } - return false; - } - - protected class MyWebViewClient extends WebViewClient { - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - super.onPageStarted(view, url, favicon); - mProgressBar.setProgress(0); - mProgressBar.setVisibility(VISIBLE); - } - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - mProgressBar.setVisibility(GONE); - mSwipeRefreshLayout.setRefreshing(false); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { - return shouldOverrideUrlLoading(view, request.getUrl().toString()); - } - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if ((!url.endsWith(".apk")&&!url.contains(".apk?")) - &&(url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://"))) { - view.loadUrl(url); - } else { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - List intentActivities = getContext().getPackageManager().queryIntentActivities(intent, 0); - if (intentActivities.isEmpty()) { - return false; - } - try { - getContext().startActivity(Intent.createChooser(intent, getResources().getString(R.string.text_open_with))); - } catch (ActivityNotFoundException e) { - e.printStackTrace(); - return false; - } - } - return true; - } - - - } -} diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/EWebView.kt b/app/src/main/java/org/autojs/autojs/ui/widget/EWebView.kt new file mode 100644 index 000000000..c1ab82747 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/widget/EWebView.kt @@ -0,0 +1,408 @@ +package org.autojs.autojs.ui.widget + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Build +import android.text.InputFilter +import android.util.AttributeSet +import android.util.Log +import android.webkit.JavascriptInterface +import android.webkit.URLUtil +import android.webkit.ValueCallback +import android.widget.EditText +import android.widget.FrameLayout +import android.widget.ProgressBar +import android.widget.Toast +import androidx.annotation.RequiresApi +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import org.autojs.autojs.ui.widget.DownloadManagerUtil +import com.stardust.app.GlobalAppContext +import com.stardust.app.OnActivityResultDelegate +import com.stardust.app.OnActivityResultDelegate.DelegateHost +import com.stardust.autojs.execution.ScriptExecution +import com.stardust.autojs.script.StringScriptSource +import com.tencent.smtt.sdk.WebView +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import org.autojs.autojs.R +import org.autojs.autojs.model.script.Scripts.run +import org.autojs.autojs.tool.ImageSelector +import java.io.IOException +import java.io.InputStream +import java.util.* +import java.util.concurrent.TimeUnit + +/** + * Created by Stardust on 2017/8/22. + */ +open class EWebView : FrameLayout, SwipeRefreshLayout.OnRefreshListener, OnActivityResultDelegate { + private lateinit var mWebView: com.tencent.smtt.sdk.WebView + private lateinit var mProgressBar: ProgressBar + private lateinit var mSwipeRefreshLayout: SwipeRefreshLayout + private lateinit var downloadManagerUtil: DownloadManagerUtil + private var downloadId = 0L + + constructor(context: Context?) : super(context!!) { + init() + } + + constructor(context: Context?, attrs: AttributeSet?) : super( + context!!, attrs + ) { + init() + } + + private fun init() { + inflate(context, R.layout.ewebview, this) + // 在调用TBS初始化、创建WebView之前进行如下配置 + val map: java.util.HashMap = HashMap() + map[com.tencent.smtt.export.external.TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER] = + true + map[com.tencent.smtt.export.external.TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE] = + true + com.tencent.smtt.sdk.QbSdk.initTbsSettings(map) + // 设置可调试 +// com.tencent.smtt.sdk.WebView.setWebContentsDebuggingEnabled(true) + mWebView = com.tencent.smtt.sdk.WebView(context) + mSwipeRefreshLayout = findViewById(R.id.swipe_refresh_layout) + mSwipeRefreshLayout.addView( + mWebView, + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + ) + mProgressBar = findViewById(R.id.progress_bar) + mSwipeRefreshLayout.setOnRefreshListener(this) + downloadManagerUtil = DownloadManagerUtil(GlobalAppContext.get()) + webInit(mWebView) + } + + private fun webInit(mWebView: com.tencent.smtt.sdk.WebView) { + if (Build.VERSION.SDK_INT >= 26) { + mWebView.settings.safeBrowsingEnabled = false + } + with(mWebView.settings) { + // JS相关 + javaScriptEnabled = true //设置支持Javascript交互 + javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口 + allowFileAccess = true //设置可以访问文件 + defaultTextEncodingName = "utf-8"//设置编码格式 + // 视图设置 + setSupportMultipleWindows(false) + layoutAlgorithm = com.tencent.smtt.sdk.WebSettings.LayoutAlgorithm.NORMAL + loadWithOverviewMode = true // 缩放至屏幕的大小 + setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。 + builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放 + displayZoomControls = false //设置原生的缩放控件,启用时被leakcanary检测到内存泄露 + useWideViewPort = true //让WebView读取网页设置的viewport,pc版网页 + loadsImagesAutomatically = true //设置自动加载图片 + blockNetworkImage = false // 阻止网络图片加載, 测试发现此项(TBS045947)在启用TBS内核时失效 + // cache设置 + cacheMode = android.webkit.WebSettings.LOAD_CACHE_ELSE_NETWORK //使用缓存 + domStorageEnabled = true // 开启 DOM storage API 功能 + databaseEnabled = true //开启 database storage API 功能 + } + mWebView!!.webViewClient = MyWebViewClient() + mWebView!!.webChromeClient = MyWebChromeClient() + mWebView.setDownloadListener { url: String, userAgent: String, contentDisposition: String, mimeType: String, contentLength: Long -> + run { + // 通过系统下载器下载(会在通知栏显示下载进度条) + val fileName: String = URLUtil.guessFileName( + url, contentDisposition, + mimeType + ) + //先移除上一个下载任务,防止重复下载 + // 此处为单个文件下载,downloadId为下载任务id,可根据业务调整 + if (downloadId != 0L) { + downloadManagerUtil.clearCurrentTask(downloadId) + } + downloadId = downloadManagerUtil.download(url, fileName, fileName) + Toast.makeText(GlobalAppContext.get(), "正在后台下载:$fileName", Toast.LENGTH_LONG) + .show() + } + } + mWebView.addJavascriptInterface(JsAPI(), "android") + } + + fun evalJavaScript(script: String) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mWebView!!.evaluateJavascript(script, null) + } else { + mWebView!!.loadUrl("javascript:$script") + } + } + + @SuppressLint("CheckResult") + override fun onRefresh() { + mSwipeRefreshLayout!!.isRefreshing = false + mWebView!!.reload() + Observable.timer(2, TimeUnit.SECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { t: Long? -> mSwipeRefreshLayout!!.isRefreshing = false } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {} + + protected open inner class MyWebViewClient() : com.tencent.smtt.sdk.WebViewClient() { + //如果只是为了获取网页源代码的话,可以重写onPageFinished方法,在onPageFinished方法里执行相应的逻辑就好。但是当框架里显示的内容发生变化时,onPageFinished方法不会再掉用,只会调用onLoadResource方法,所以此处需要重写此方法。 + override fun onLoadResource(view: com.tencent.smtt.sdk.WebView, url: String?) { + super.onLoadResource(view, url) + } + + override fun onPageStarted( + view: com.tencent.smtt.sdk.WebView, + url: String, + favicon: Bitmap? + ) { + super.onPageStarted(view, url, favicon) +// view.settings.blockNetworkImage = true + mProgressBar!!.progress = 0 + mProgressBar!!.visibility = VISIBLE + if (url != null) { + if ((url.startsWith("http") && url.indexOf("autoxjs.com") < 0) || (url.startsWith("file") && !url.startsWith( + "file:///android_asset" + )) + ) { + var jsCode = + "javascript: " + readAssetsTxt(context, "modules/vconsole.min.js") + Log.i("onPageStarted", jsCode) + view.evaluateJavascript( + jsCode, + com.tencent.smtt.sdk.ValueCallback { + Log.i("evaluateJavascript", "JS return: $it") + }) + } + } + } + + override fun onPageFinished(view: com.tencent.smtt.sdk.WebView, url: String) { + view.settings.blockNetworkImage = false + super.onPageFinished(view, url) + mProgressBar!!.visibility = GONE + mSwipeRefreshLayout!!.isRefreshing = false + } + + override fun shouldOverrideUrlLoading( + view: com.tencent.smtt.sdk.WebView, + request: com.tencent.smtt.export.external.interfaces.WebResourceRequest + ): Boolean { + return shouldOverrideUrlLoading(view, request.url.toString()) + } + + override fun shouldOverrideUrlLoading( + view: com.tencent.smtt.sdk.WebView, + url: String + ): Boolean { + // 区分正规scheme和其它APP自定义的scheme + if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://")) { + view.loadUrl(url) + } else if (url.indexOf("mobile_web") < 0) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + } + return true + } + } + + protected open inner class MyWebChromeClient : com.tencent.smtt.sdk.WebChromeClient() { + //设置响应js 的Alert()函数 + override fun onJsAlert( + p0: com.tencent.smtt.sdk.WebView?, + url: String?, + message: String?, + result: com.tencent.smtt.export.external.interfaces.JsResult? + ): Boolean { + val b: android.app.AlertDialog.Builder = + android.app.AlertDialog.Builder(GlobalAppContext.get()) + b.setTitle("Alert") + b.setMessage(message) + b.setPositiveButton( + R.string.ok, + DialogInterface.OnClickListener { _, _ -> result?.confirm() }) + b.setCancelable(false) + b.create().show() + return true + } + + //设置响应js 的Confirm()函数 + override fun onJsConfirm( + p0: com.tencent.smtt.sdk.WebView?, + url: String?, + message: String?, + result: com.tencent.smtt.export.external.interfaces.JsResult? + ): Boolean { + val b: android.app.AlertDialog.Builder = + android.app.AlertDialog.Builder(GlobalAppContext.get()) + b.setTitle("Confirm") + b.setMessage(message) + b.setPositiveButton( + R.string.ok, + DialogInterface.OnClickListener { _, _ -> result?.confirm() }) + b.setNegativeButton( + R.string.cancel, + DialogInterface.OnClickListener { _, _ -> result?.cancel() }) + b.create().show() + return true + } + + //设置响应js 的Prompt()函数 + override fun onJsPrompt( + p0: com.tencent.smtt.sdk.WebView?, + url: String?, + message: String?, + defaultValue: String?, + result: com.tencent.smtt.export.external.interfaces.JsPromptResult + ): Boolean { + val b: android.app.AlertDialog.Builder = + android.app.AlertDialog.Builder(GlobalAppContext.get()) + val inputServer = EditText(GlobalAppContext.get()) + inputServer.filters = arrayOf(InputFilter.LengthFilter(255)) + inputServer.setText(defaultValue) + b.setTitle("Prompt") + b.setMessage(message) + b.setView(inputServer) + b.setPositiveButton( + R.string.ok, + DialogInterface.OnClickListener { _, _ -> + val value = inputServer.text.toString() + result?.confirm(value) + }) + b.setNegativeButton( + R.string.cancel, + DialogInterface.OnClickListener { _, _ -> result.cancel() }) + b.create().show() + return true + } + + override fun onProgressChanged(view: com.tencent.smtt.sdk.WebView, newProgress: Int) { + super.onProgressChanged(view, newProgress) + mProgressBar!!.progress = newProgress + } + + //For Android >= 4.1 + fun openFileChooser( + valueCallback: ValueCallback, + acceptType: String?, capture: String? + ) { + if (acceptType == null) { + openFileChooser(valueCallback, null) + } else { + openFileChooser(valueCallback, acceptType.split(",").toTypedArray()) + } + } + + open fun openFileChooser( + valueCallback: ValueCallback, + acceptType: Array? + ): Boolean { + if (context is DelegateHost && + context is Activity && isImageType(acceptType) + ) { + chooseImage(valueCallback) + return true + } + return false + } + + // For Android >= 5.0 + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + override fun onShowFileChooser( + webView: WebView?, + p1: com.tencent.smtt.sdk.ValueCallback>?, + fileChooserParams: FileChooserParams? + ): Boolean { + if (fileChooserParams != null) { + openFileChooser({ value: Uri? -> + if (value == null) { + p1?.onReceiveValue(null) + } else { + p1?.onReceiveValue(arrayOf(value)) + } + }, fileChooserParams.acceptTypes) + } + return true + } + } + + private fun chooseImage(valueCallback: ValueCallback) { + val delegateHost = context as DelegateHost + val mediator = delegateHost.onActivityResultDelegateMediator + val activity = context as Activity + ImageSelector( + activity, + mediator + ) { selector: ImageSelector?, uri: Uri? -> valueCallback.onReceiveValue(uri) } + .disposable() + .select() + } + + private fun isImageType(acceptTypes: Array?): Boolean { + if (acceptTypes == null) { + return false + } + for (acceptType in acceptTypes) { + for (imageType in IMAGE_TYPES) { + if (acceptType.contains(imageType)) { + return true + } + } + } + return false + } + + companion object { + private val IMAGE_TYPES = Arrays.asList("png", "jpg", "bmp") + private const val CHOOSE_IMAGE = 42222 + } + + fun getWebView(): com.tencent.smtt.sdk.WebView { + return mWebView + } + + fun getSwipeRefreshLayout(): SwipeRefreshLayout { + return mSwipeRefreshLayout + } + + internal class JsAPI { + private var execution: ScriptExecution? = null + + @JavascriptInterface + fun run(code: String?, name: String?) { + stop(execution) + execution = run(StringScriptSource(name, code)) + } + + @JavascriptInterface + fun run(code: String?) { + stop(execution) + execution = run(StringScriptSource("", code)) + } + + @JavascriptInterface + fun stop(execution: ScriptExecution?) { + execution?.engine?.forceStop() + } + } + + fun readAssetsTxt(context: Context, fileName: String): String? { + try { + //Return an AssetManager instance for your application's package + val `is`: InputStream = context.assets.open("$fileName") + val size: Int = `is`.available() + // Read the entire asset into a local byte buffer. + val buffer = ByteArray(size) + `is`.read(buffer) + `is`.close() + // Convert the buffer into a string. + // Finally stick the string into the text view. + return String(buffer, Charsets.UTF_8) + } catch (e: IOException) { + // Should never happen! +// throw new RuntimeException(e); + e.message?.let { Log.e("", it) } + } + return "读取错误,请检查文件名" + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/filedialog_file.png b/app/src/main/res/drawable/filedialog_file.png new file mode 100644 index 000000000..fc05cef39 Binary files /dev/null and b/app/src/main/res/drawable/filedialog_file.png differ diff --git a/app/src/main/res/drawable/filedialog_folder.png b/app/src/main/res/drawable/filedialog_folder.png new file mode 100644 index 000000000..b43ce9c97 Binary files /dev/null and b/app/src/main/res/drawable/filedialog_folder.png differ diff --git a/app/src/main/res/drawable/filedialog_folder_up.png b/app/src/main/res/drawable/filedialog_folder_up.png new file mode 100644 index 000000000..a008bd9d3 Binary files /dev/null and b/app/src/main/res/drawable/filedialog_folder_up.png differ diff --git a/app/src/main/res/drawable/filedialog_root.png b/app/src/main/res/drawable/filedialog_root.png new file mode 100644 index 000000000..b822b1213 Binary files /dev/null and b/app/src/main/res/drawable/filedialog_root.png differ diff --git a/app/src/main/res/drawable/ic_floating_action_menu_home.png b/app/src/main/res/drawable/ic_floating_action_menu_home.png new file mode 100644 index 000000000..a0af9bb74 Binary files /dev/null and b/app/src/main/res/drawable/ic_floating_action_menu_home.png differ diff --git a/app/src/main/res/layout/activity_documentation.xml b/app/src/main/res/layout/activity_documentation.xml index 8b855b4a7..12ae9349b 100644 --- a/app/src/main/res/layout/activity_documentation.xml +++ b/app/src/main/res/layout/activity_documentation.xml @@ -22,7 +22,7 @@ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8ada97c47..5ff8ba67b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -14,39 +14,50 @@ android:clipChildren="false" android:clipToPadding="false"> - - - - + - + android:layout_alignParentTop="true" + android:paddingTop="16dp" + android:theme="@style/AppTheme.AppBarOverlay"> + + + + - + \ No newline at end of file diff --git a/app/src/main/res/layout/ewebview.xml b/app/src/main/res/layout/ewebview.xml index b737612c6..1276463f7 100644 --- a/app/src/main/res/layout/ewebview.xml +++ b/app/src/main/res/layout/ewebview.xml @@ -10,10 +10,10 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/floating_manual_dialog.xml b/app/src/main/res/layout/floating_manual_dialog.xml index 3c05a15e9..2499e8572 100644 --- a/app/src/main/res/layout/floating_manual_dialog.xml +++ b/app/src/main/res/layout/floating_manual_dialog.xml @@ -64,7 +64,7 @@ diff --git a/app/src/main/res/layout/fragment_community.xml b/app/src/main/res/layout/fragment_community.xml index 57aa969fa..0fb4cf67d 100644 --- a/app/src/main/res/layout/fragment_community.xml +++ b/app/src/main/res/layout/fragment_community.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_online_docs.xml b/app/src/main/res/layout/fragment_online_docs.xml index c7c1a7165..07fbfba86 100644 --- a/app/src/main/res/layout/fragment_online_docs.xml +++ b/app/src/main/res/layout/fragment_online_docs.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_web.xml b/app/src/main/res/layout/fragment_web.xml new file mode 100644 index 000000000..ec156d056 --- /dev/null +++ b/app/src/main/res/layout/fragment_web.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d626e196c..f3fede260 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -24,5 +24,7 @@ Issues不可用,请联系软件开发者. 未知错误 OK + 主页 + 搜索 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1701c0410..b271321b2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -475,6 +475,9 @@ Issues不可用,请联系软件开发者. 未知错误 OK + 主页 + 网络 + 搜索 diff --git a/autojs/src/main/assets/modules/vconsole.min.js b/autojs/src/main/assets/modules/vconsole.min.js new file mode 100644 index 000000000..ac4b48c3a --- /dev/null +++ b/autojs/src/main/assets/modules/vconsole.min.js @@ -0,0 +1,12 @@ +/*! + * vConsole v3.11.2 (https://github.com/Tencent/vConsole) + * + * Tencent is pleased to support the open source community by making vConsole available. + * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("VConsole",[],n):"object"==typeof exports?exports.VConsole=n():t.VConsole=n()}(this||self,(function(){return function(){var __webpack_modules__={8406:function(t,n,e){"use strict";function o(t,n){for(var e=0;e=0?n:null}(t);return[null==n?"":";path="+n,null==e?"":";domain="+e,null==o?"":";expires="+o.toUTCString(),void 0===r||!1===r?"":";secure",null===i?"":";SameSite="+i].join("")};n.formatCookie=function(t,n,o){return[encodeURIComponent(t),"=",encodeURIComponent(n),e(o)].join("")}},6025:function(t,n,e){"use strict";var o=e(8406);Object.defineProperty(n,"eR",{enumerable:!0,get:function(){return o.CookieStorage}});var r=e(9390);var i=e(4370)},4370:function(t,n){"use strict";function e(t,n){return function(t){if(Array.isArray(t))return t}(t)||function(t,n){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var e=[],o=!0,r=!1,i=void 0;try{for(var c,a=t[Symbol.iterator]();!(o=(c=a.next()).done)&&(e.push(c.value),!n||e.length!==n);o=!0);}catch(t){r=!0,i=t}finally{try{o||null==a.return||a.return()}finally{if(r)throw i}}return e}(t,n)||function(t,n){if(!t)return;if("string"==typeof t)return o(t,n);var e=Object.prototype.toString.call(t).slice(8,-1);"Object"===e&&t.constructor&&(e=t.constructor.name);if("Map"===e||"Set"===e)return Array.from(t);if("Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e))return o(t,n)}(t,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,o=new Array(n);es;)if((a=l[s++])!=a)return!0}else for(;u>s;s++)if((t||s in l)&&l[s]===e)return t||s||0;return!t&&-1}};t.exports={includes:c(!0),indexOf:c(!1)}},4805:function(t,n,e){var o=e(2938),r=e(5044),i=e(1324),c=e(97),a=e(4822),l=[].push,u=function(t){var n=1==t,e=2==t,u=3==t,s=4==t,f=6==t,d=7==t,v=5==t||f;return function(p,h,_,g){for(var m,b,y=i(p),E=r(y),w=o(h,_,3),O=c(E.length),L=0,C=g||a,T=n?C(p,O):e||d?C(p,0):void 0;O>L;L++)if((v||L in E)&&(b=w(m=E[L],L,y),t))if(n)T[L]=b;else if(b)switch(t){case 3:return!0;case 5:return m;case 6:return L;case 2:l.call(T,m)}else switch(t){case 4:return!1;case 7:l.call(T,m)}return f?-1:u||s?s:T}};t.exports={forEach:u(0),map:u(1),filter:u(2),some:u(3),every:u(4),find:u(5),findIndex:u(6),filterOut:u(7)}},9269:function(t,n,e){var o=e(6544),r=e(3649),i=e(4061),c=r("species");t.exports=function(t){return i>=51||!o((function(){var n=[];return(n.constructor={})[c]=function(){return{foo:1}},1!==n[t](Boolean).foo}))}},4822:function(t,n,e){var o=e(794),r=e(4521),i=e(3649)("species");t.exports=function(t,n){var e;return r(t)&&("function"!=typeof(e=t.constructor)||e!==Array&&!r(e.prototype)?o(e)&&null===(e=e[i])&&(e=void 0):e=void 0),new(void 0===e?Array:e)(0===n?0:n)}},9624:function(t){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},3058:function(t,n,e){var o=e(8191),r=e(9624),i=e(3649)("toStringTag"),c="Arguments"==r(function(){return arguments}());t.exports=o?r:function(t){var n,e,o;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=function(t,n){try{return t[n]}catch(t){}}(n=Object(t),i))?e:c?r(n):"Object"==(o=r(n))&&"function"==typeof n.callee?"Arguments":o}},3478:function(t,n,e){var o=e(4402),r=e(929),i=e(6683),c=e(4615);t.exports=function(t,n){for(var e=r(n),a=c.f,l=i.f,u=0;u=74)&&(o=c.match(/Chrome\/(\d+)/))&&(r=o[1]),t.exports=r&&+r},5690:function(t){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},7263:function(t,n,e){var o=e(7583),r=e(6683).f,i=e(57),c=e(1270),a=e(460),l=e(3478),u=e(4451);t.exports=function(t,n){var e,s,f,d,v,p=t.target,h=t.global,_=t.stat;if(e=h?o:_?o[p]||a(p,{}):(o[p]||{}).prototype)for(s in n){if(d=n[s],f=t.noTargetGet?(v=r(e,s))&&v.value:e[s],!u(h?s:p+(_?".":"#")+s,t.forced)&&void 0!==f){if(typeof d==typeof f)continue;l(d,f)}(t.sham||f&&f.sham)&&i(d,"sham",!0),c(e,s,d,t)}}},6544:function(t){t.exports=function(t){try{return!!t()}catch(t){return!0}}},2938:function(t,n,e){var o=e(6163);t.exports=function(t,n,e){if(o(t),void 0===n)return t;switch(e){case 0:return function(){return t.call(n)};case 1:return function(e){return t.call(n,e)};case 2:return function(e,o){return t.call(n,e,o)};case 3:return function(e,o,r){return t.call(n,e,o,r)}}return function(){return t.apply(n,arguments)}}},5897:function(t,n,e){var o=e(1287),r=e(7583),i=function(t){return"function"==typeof t?t:void 0};t.exports=function(t,n){return arguments.length<2?i(o[t])||i(r[t]):o[t]&&o[t][n]||r[t]&&r[t][n]}},7583:function(t,n,e){var o=function(t){return t&&t.Math==Math&&t};t.exports=o("object"==typeof globalThis&&globalThis)||o("object"==typeof window&&window)||o("object"==typeof self&&self)||o("object"==typeof e.g&&e.g)||function(){return this}()||Function("return this")()},4402:function(t,n,e){var o=e(1324),r={}.hasOwnProperty;t.exports=Object.hasOwn||function(t,n){return r.call(o(t),n)}},4639:function(t){t.exports={}},482:function(t,n,e){var o=e(5897);t.exports=o("document","documentElement")},275:function(t,n,e){var o=e(8494),r=e(6544),i=e(6668);t.exports=!o&&!r((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},5044:function(t,n,e){var o=e(6544),r=e(9624),i="".split;t.exports=o((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==r(t)?i.call(t,""):Object(t)}:Object},9734:function(t,n,e){var o=e(1314),r=Function.toString;"function"!=typeof o.inspectSource&&(o.inspectSource=function(t){return r.call(t)}),t.exports=o.inspectSource},2743:function(t,n,e){var o,r,i,c=e(9491),a=e(7583),l=e(794),u=e(57),s=e(4402),f=e(1314),d=e(9137),v=e(4639),p="Object already initialized",h=a.WeakMap;if(c||f.state){var _=f.state||(f.state=new h),g=_.get,m=_.has,b=_.set;o=function(t,n){if(m.call(_,t))throw new TypeError(p);return n.facade=t,b.call(_,t,n),n},r=function(t){return g.call(_,t)||{}},i=function(t){return m.call(_,t)}}else{var y=d("state");v[y]=!0,o=function(t,n){if(s(t,y))throw new TypeError(p);return n.facade=t,u(t,y,n),n},r=function(t){return s(t,y)?t[y]:{}},i=function(t){return s(t,y)}}t.exports={set:o,get:r,has:i,enforce:function(t){return i(t)?r(t):o(t,{})},getterFor:function(t){return function(n){var e;if(!l(n)||(e=r(n)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return e}}}},4521:function(t,n,e){var o=e(9624);t.exports=Array.isArray||function(t){return"Array"==o(t)}},4451:function(t,n,e){var o=e(6544),r=/#|\.prototype\./,i=function(t,n){var e=a[c(t)];return e==u||e!=l&&("function"==typeof n?o(n):!!n)},c=i.normalize=function(t){return String(t).replace(r,".").toLowerCase()},a=i.data={},l=i.NATIVE="N",u=i.POLYFILL="P";t.exports=i},794:function(t){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},6268:function(t){t.exports=!1},8640:function(t,n,e){var o=e(4061),r=e(6544);t.exports=!!Object.getOwnPropertySymbols&&!r((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&o&&o<41}))},9491:function(t,n,e){var o=e(7583),r=e(9734),i=o.WeakMap;t.exports="function"==typeof i&&/native code/.test(r(i))},3590:function(t,n,e){var o,r=e(2569),i=e(8728),c=e(5690),a=e(4639),l=e(482),u=e(6668),s=e(9137),f=s("IE_PROTO"),d=function(){},v=function(t){return"