diff --git a/app/src/main/java/com/github/catvod/spider/Market.java b/app/src/main/java/com/github/catvod/spider/Market.java index c359fadf..ce439fb0 100644 --- a/app/src/main/java/com/github/catvod/spider/Market.java +++ b/app/src/main/java/com/github/catvod/spider/Market.java @@ -39,9 +39,11 @@ public class Market extends Spider { @Override public String detailContent(List ids) throws Exception { - new Thread(() -> download(ids.get(0))).start(); - Init.run(this::finish); - return ""; + Init.execute(() -> download(ids.get(0))); + Vod vod = new Vod(); + vod.setVodPlayFrom("FongMi"); + vod.setVodPlayUrl("FongMi$FongMi"); + return Result.string(vod); } private void finish() { @@ -55,27 +57,24 @@ public class Market extends Spider { private void download(String url) { try { + Init.run(this::finish); Utils.notify("正在下載..."); Response response = OkHttp.newCall(url); File file = FileUtil.getCacheFile(Uri.parse(url).getLastPathSegment()); - download(file, response.body().byteStream(), Double.parseDouble(response.header("Content-Length", "1"))); + download(file, response.body().byteStream()); FileUtil.openFile(FileUtil.chmod(file)); } catch (Exception e) { Utils.notify(e.getMessage()); } } - private void download(File file, InputStream is, double length) throws Exception { + private void download(File file, InputStream is) throws Exception { FileOutputStream os = new FileOutputStream(file); try (BufferedInputStream input = new BufferedInputStream(is)) { byte[] buffer = new byte[4096]; int readBytes; - long totalBytes = 0; while ((readBytes = input.read(buffer)) != -1) { - totalBytes += readBytes; os.write(buffer, 0, readBytes); - int progress = (int) (totalBytes / length * 100.0); - if (progress % 20 == 0) Utils.notify("正在下載..." + progress + "%"); } } } diff --git a/app/src/main/java/com/github/catvod/utils/FileProvider.java b/app/src/main/java/com/github/catvod/utils/FileProvider.java index 269ac97a..8440a50d 100644 --- a/app/src/main/java/com/github/catvod/utils/FileProvider.java +++ b/app/src/main/java/com/github/catvod/utils/FileProvider.java @@ -3,19 +3,15 @@ package com.github.catvod.utils; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; -import android.annotation.SuppressLint; import android.content.ContentProvider; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.res.XmlResourceParser; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; @@ -23,8 +19,6 @@ import android.provider.OpenableColumns; import android.text.TextUtils; import android.webkit.MimeTypeMap; -import com.github.catvod.spider.Init; - import org.xmlpull.v1.XmlPullParserException; import java.io.File; @@ -36,117 +30,48 @@ import java.util.Map; public class FileProvider extends ContentProvider { private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}; - private static final String META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS"; - - private static final String TAG_ROOT_PATH = "root-path"; - private static final String TAG_FILES_PATH = "files-path"; private static final String TAG_CACHE_PATH = "cache-path"; private static final String TAG_EXTERNAL = "external-path"; - private static final String TAG_EXTERNAL_FILES = "external-files-path"; - private static final String TAG_EXTERNAL_CACHE = "external-cache-path"; - private static final String TAG_EXTERNAL_MEDIA = "external-media-path"; - private static final String ATTR_NAME = "name"; private static final String ATTR_PATH = "path"; - private static final String DISPLAYNAME_FIELD = "displayName"; - - private static final File DEVICE_ROOT = new File("/"); - private static final HashMap sCache = new HashMap<>(); - private PathStrategy mStrategy; - private int mResourceId; - /** - * The default FileProvider implementation does not need to be initialized. If you want to - * override this method, you must provide your own subclass of FileProvider. - */ @Override public boolean onCreate() { return true; } - /** - * After the FileProvider is instantiated, this method is called to provide the system with - * information about the provider. - * - * @param context A {@link Context} for the current component. - * @param info A {@link ProviderInfo} for the new provider. - */ - @SuppressWarnings("StringSplitter") @Override public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); - - // Check our security attributes if (info.exported) { throw new SecurityException("Provider must not be exported"); } if (!info.grantUriPermissions) { throw new SecurityException("Provider must grant uri permissions"); } - String authority = info.authority.split(";")[0]; synchronized (sCache) { sCache.remove(authority); } - - mStrategy = getPathStrategy(context, authority, mResourceId); + mStrategy = getPathStrategy(context, authority, 0); } - public static Uri getUriForFile(Context context, String authority, File file) { final PathStrategy strategy = getPathStrategy(context, authority, 0); return strategy.getUriForFile(file); } - - @SuppressLint("StreamFiles") - public static Uri getUriForFile(Context context, String authority, File file, String displayName) { - Uri uri = getUriForFile(context, authority, file); - return uri.buildUpon().appendQueryParameter(DISPLAYNAME_FIELD, displayName).build(); - } - - /** - * Use a content URI returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file - * managed by the FileProvider. - * FileProvider reports the column names defined in {@link OpenableColumns}: - * - * For more information, see - * {@link ContentProvider#query(Uri, String[], String, String[], String) - * ContentProvider.query()}. - * - * @param uri A content URI returned by {@link #getUriForFile}. - * @param projection The list of columns to put into the {@link Cursor}. If null all columns are - * included. - * @param selection Selection criteria to apply. If null then all data that matches the content - * URI is returned. - * @param selectionArgs An array of {@link String}, containing arguments to bind to - * the selection parameter. The query method scans selection from left to - * right and iterates through selectionArgs, replacing the current "?" character in - * selection with the value at the current position in selectionArgs. The - * values are bound to selection as {@link String} values. - * @param sortOrder A {@link String} containing the column name(s) on which to sort - * the resulting {@link Cursor}. - * @return A {@link Cursor} containing the results of the query. - */ - @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - // ContentProvider has already checked granted permissions final File file = mStrategy.getFileForUri(uri); String displayName = uri.getQueryParameter(DISPLAYNAME_FIELD); - if (projection == null) { projection = COLUMNS; } - String[] cols = new String[projection.length]; Object[] values = new Object[projection.length]; int i = 0; @@ -159,29 +84,16 @@ public class FileProvider extends ContentProvider { values[i++] = file.length(); } } - cols = copyOf(cols, i); values = copyOf(values, i); - final MatrixCursor cursor = new MatrixCursor(cols, 1); cursor.addRow(values); return cursor; } - /** - * Returns the MIME type of a content URI returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * - * @param uri A content URI returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * @return If the associated file has an extension, the MIME type associated with that - * extension; otherwise application/octet-stream. - */ @Override public String getType(Uri uri) { - // ContentProvider has already checked granted permissions final File file = mStrategy.getFileForUri(uri); - final int lastDot = file.getName().lastIndexOf('.'); if (lastDot >= 0) { final String extension = file.getName().substring(lastDot + 1); @@ -190,74 +102,32 @@ public class FileProvider extends ContentProvider { return mime; } } - return "application/octet-stream"; } - /** - * By default, this method throws an {@link UnsupportedOperationException}. You must - * subclass FileProvider if you want to provide different functionality. - */ @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException("No external inserts"); } - /** - * By default, this method throws an {@link UnsupportedOperationException}. You must - * subclass FileProvider if you want to provide different functionality. - */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("No external updates"); } - /** - * Deletes the file associated with the specified content URI, as - * returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this - * method does not throw an {@link IOException}; you must check its return value. - * - * @param uri A content URI for a file, as returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * @param selection Ignored. Set to {@code null}. - * @param selectionArgs Ignored. Set to {@code null}. - * @return 1 if the delete succeeds; otherwise, 0. - */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { - // ContentProvider has already checked granted permissions final File file = mStrategy.getFileForUri(uri); return file.delete() ? 1 : 0; } - /** - * By default, FileProvider automatically returns the - * {@link ParcelFileDescriptor} for a file associated with a content:// - * {@link Uri}. To get the {@link ParcelFileDescriptor}, call - * {@link ContentResolver#openFileDescriptor(Uri, String) - * ContentResolver.openFileDescriptor}. - *

- * To override this method, you must provide your own subclass of FileProvider. - * - * @param uri A content URI associated with a file, as returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * @param mode Access mode for the file. May be "r" for read-only access, "rw" for read and - * write access, or "rwt" for read and write access that truncates any existing file. - * @return A new {@link ParcelFileDescriptor} with which you can access the file. - */ - @SuppressLint("UnknownNullness") // b/171012356 @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - // ContentProvider has already checked granted permissions final File file = mStrategy.getFileForUri(uri); final int fileMode = modeToMode(mode); return ParcelFileDescriptor.open(file, fileMode); } - /** - * Return {@link PathStrategy} for given authority, either by parsing or - * returning from cache. - */ private static PathStrategy getPathStrategy(Context context, String authority, int resourceId) { PathStrategy strat; synchronized (sCache) { @@ -280,109 +150,49 @@ public class FileProvider extends ContentProvider { if (info == null) { throw new IllegalArgumentException("Couldn't find meta-data for provider with authority " + authority); } - if (info.metaData == null && resourceId != 0) { info.metaData = new Bundle(1); info.metaData.putInt(META_DATA_FILE_PROVIDER_PATHS, resourceId); } - final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS); if (in == null) { throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data"); } - return in; } - /** - * Parse and return {@link PathStrategy} for given authority as defined in - * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code }. - * - * @see #getPathStrategy(Context, String, int) - */ private static PathStrategy parsePathStrategy(Context context, String authority, int resourceId) throws IOException, XmlPullParserException { final SimplePathStrategy strat = new SimplePathStrategy(authority); - final ProviderInfo info = context.getPackageManager().resolveContentProvider(authority, PackageManager.GET_META_DATA); final XmlResourceParser in = getFileProviderPathsMetaData(context, authority, info, resourceId); - int type; while ((type = in.next()) != END_DOCUMENT) { if (type == START_TAG) { final String tag = in.getName(); - final String name = in.getAttributeValue(null, ATTR_NAME); String path = in.getAttributeValue(null, ATTR_PATH); - File target = null; - if (TAG_ROOT_PATH.equals(tag)) { - target = DEVICE_ROOT; - } else if (TAG_FILES_PATH.equals(tag)) { - target = context.getFilesDir(); - } else if (TAG_CACHE_PATH.equals(tag)) { + if (TAG_CACHE_PATH.equals(tag)) { target = context.getCacheDir(); } else if (TAG_EXTERNAL.equals(tag)) { target = Environment.getExternalStorageDirectory(); - } else if (TAG_EXTERNAL_CACHE.equals(tag)) { - File[] externalCacheDirs = new File[0]; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { - externalCacheDirs = Init.context().getExternalCacheDirs(); - } - if (externalCacheDirs.length > 0) { - target = externalCacheDirs[0]; - } - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && TAG_EXTERNAL_MEDIA.equals(tag)) { - File[] externalMediaDirs = Api21Impl.getExternalMediaDirs(context); - if (externalMediaDirs.length > 0) { - target = externalMediaDirs[0]; - } } - if (target != null) { strat.addRoot(name, buildPath(target, path)); } } } - return strat; } - /** - * Strategy for mapping between {@link File} and {@link Uri}. - *

- * Strategies must be symmetric so that mapping a {@link File} to a - * {@link Uri} and then back to a {@link File} points at the original - * target. - *

- * Strategies must remain consistent across app launches, and not rely on - * dynamic state. This ensures that any generated {@link Uri} can still be - * resolved if your process is killed and later restarted. - * - * @see SimplePathStrategy - */ interface PathStrategy { - /** - * Return a {@link Uri} that represents the given {@link File}. - */ Uri getUriForFile(File file); - /** - * Return a {@link File} that represents the given {@link Uri}. - */ File getFileForUri(Uri uri); } - /** - * Strategy that provides access to files living under a narrow allowed list - * of filesystem roots. It will throw {@link SecurityException} if callers try - * accessing files outside the configured roots. - *

- * For example, if configured with - * {@code addRoot("myfiles", context.getFilesDir())}, then - * {@code context.getFileStreamPath("foo.txt")} would map to - * {@code content://myauthority/myfiles/foo.txt}. - */ static class SimplePathStrategy implements PathStrategy { + private final String mAuthority; private final HashMap mRoots = new HashMap<>(); @@ -390,22 +200,15 @@ public class FileProvider extends ContentProvider { mAuthority = authority; } - /** - * Add a mapping from a name to a filesystem root. The provider only offers - * access to files that live under configured roots. - */ void addRoot(String name, File root) { if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("Name must not be empty"); } - try { - // Resolve to canonical path to keep path checking fast root = root.getCanonicalFile(); } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + root, e); } - mRoots.put(name, root); } @@ -417,8 +220,6 @@ public class FileProvider extends ContentProvider { } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + file); } - - // Find the most-specific root path Map.Entry mostSpecific = null; for (Map.Entry root : mRoots.entrySet()) { final String rootPath = root.getValue().getPath(); @@ -426,20 +227,15 @@ public class FileProvider extends ContentProvider { mostSpecific = root; } } - if (mostSpecific == null) { throw new IllegalArgumentException("Failed to find configured root that contains " + path); } - - // Start at first char of path under root final String rootPath = mostSpecific.getValue().getPath(); if (rootPath.endsWith("/")) { path = path.substring(rootPath.length()); } else { path = path.substring(rootPath.length() + 1); } - - // Encode the tag and path separately path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/"); return new Uri.Builder().scheme("content").authority(mAuthority).encodedPath(path).build(); } @@ -447,34 +243,26 @@ public class FileProvider extends ContentProvider { @Override public File getFileForUri(Uri uri) { String path = uri.getEncodedPath(); - final int splitIndex = path.indexOf('/', 1); final String tag = Uri.decode(path.substring(1, splitIndex)); path = Uri.decode(path.substring(splitIndex + 1)); - final File root = mRoots.get(tag); if (root == null) { throw new IllegalArgumentException("Unable to find configured root for " + uri); } - File file = new File(root, path); try { file = file.getCanonicalFile(); } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + file); } - if (!file.getPath().startsWith(root.getPath())) { throw new SecurityException("Resolved path jumped beyond configured root"); } - return file; } } - /** - * Copied from ContentResolver.java - */ private static int modeToMode(String mode) { int modeBits; if ("r".equals(mode)) { @@ -514,15 +302,4 @@ public class FileProvider extends ContentProvider { System.arraycopy(original, 0, result, 0, newLength); return result; } - - static class Api21Impl { - private Api21Impl() { - // This class is not instantiable. - } - - static File[] getExternalMediaDirs(Context context) { - // Deprecated, otherwise this would belong on ContextCompat as a public method. - return context.getExternalMediaDirs(); - } - } } diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index f5a856c9..ab8d8c31 100644 Binary files a/jar/custom_spider.jar and b/jar/custom_spider.jar differ diff --git a/jar/custom_spider.jar.md5 b/jar/custom_spider.jar.md5 index 39bcfce8..e9c94d93 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -becf6453b2d5c0af08354f97799217e5 +f78dde289126840ebdd4abaf0cb7841d