diff --git a/Aria/src/main/java/com/arialyy/aria/core/Aria.java b/Aria/src/main/java/com/arialyy/aria/core/Aria.java index 99ab3d33..dbb99ffa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/Aria.java +++ b/Aria/src/main/java/com/arialyy/aria/core/Aria.java @@ -51,8 +51,7 @@ import com.arialyy.aria.core.upload.UploadTask; * Aria.upload(this) * .load(filePath) //文件路径,必填 * .setUploadUrl(uploadUrl) //上传路径,必填 - * .setFileName(fileName) //文件名 - * .setAttachment(fileKey) //服务器读取文件的key + * .setAttachment(fileKey) //服务器读取文件的key,必填 * .start(); * * diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index db0e4cd4..2bfd5868 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -124,7 +124,7 @@ import java.util.Map; UploadReceiver upload(Object obj) { IReceiver receiver = mReceivers.get(getKey(false, obj)); if (receiver == null) { - receiver = putReceiver(true, obj); + receiver = putReceiver(false, obj); } return (receiver instanceof UploadReceiver) ? (UploadReceiver) receiver : null; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java index 6b9b573d..9cfed938 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/ExecutePool.java @@ -47,12 +47,12 @@ public class ExecutePool implements IPool { @Override public boolean putTask(TASK task) { synchronized (LOCK) { if (task == null) { - Log.e(TAG, "下载任务不能为空!!"); + Log.e(TAG, "任务不能为空!!"); return false; } String url = task.getKey(); if (mExecuteQueue.contains(task)) { - Log.e(TAG, "队列中已经包含了该任务,任务下载链接【" + url + "】"); + Log.e(TAG, "队列中已经包含了该任务,任务key【" + url + "】"); return false; } else { if (mExecuteQueue.size() >= mSize) { @@ -144,7 +144,7 @@ public class ExecutePool implements IPool { @Override public TASK getTask(String downloadUrl) { synchronized (LOCK) { if (TextUtils.isEmpty(downloadUrl)) { - Log.e(TAG, "请传入有效的下载链接"); + Log.e(TAG, "请传入有效的任务key"); return null; } String key = CommonUtil.keyToHashKey(downloadUrl); @@ -168,7 +168,7 @@ public class ExecutePool implements IPool { @Override public boolean removeTask(String downloadUrl) { synchronized (LOCK) { if (TextUtils.isEmpty(downloadUrl)) { - Log.e(TAG, "请传入有效的下载链接"); + Log.e(TAG, "请传入有效的任务key"); return false; } String key = CommonUtil.keyToHashKey(downloadUrl); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 4ebee3c7..80b70cfa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.upload; import android.support.annotation.NonNull; +import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.AbsCmd; import com.arialyy.aria.core.command.CmdFactory; @@ -29,6 +30,8 @@ import com.arialyy.aria.util.CommonUtil; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Created by Aria.Lao on 2017/2/6. @@ -50,6 +53,11 @@ public class UploadReceiver implements IReceiver { if (entity == null) { entity = new UploadEntity(); } + String regex = "[/|\\\\|//]"; + Pattern p = Pattern.compile(regex); + String[] strs = p.split(filePath); + String fileName = strs[strs.length - 1]; + entity.setFileName(fileName); entity.setFilePath(filePath); return new UploadTarget(entity, targetName); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java index 4ecd1ad1..ed1acf21 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java @@ -33,6 +33,14 @@ public class UploadTarget extends AbsTarget { taskEntity = new UploadTaskEntity(entity); } + /** + * 设置userAgent + */ + public UploadTarget setUserAngent(@NonNull String userAgent) { + taskEntity.userAgent = userAgent; + return this; + } + /** * 设置上传路径 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTask.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTask.java index 5b79b1ed..9c084a2a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTask.java @@ -41,11 +41,11 @@ public class UploadTask implements ITask { } @Override public String getKey() { - return null; + return mUploadEntity.getFilePath(); } @Override public boolean isRunning() { - return false; + return mUtil.isRunning(); } public UploadEntity getUploadEntity() { @@ -197,7 +197,7 @@ public class UploadTask implements ITask { */ private void sendInState2Target(int state) { if (outHandler.get() != null) { - outHandler.get().obtainMessage(state, task).sendToTarget(); + outHandler.get().obtainMessage(state, task.get()).sendToTarget(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java index 50e7a013..07818b8d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTaskEntity.java @@ -30,6 +30,7 @@ public class UploadTaskEntity extends ITaskEntity { public String attachment; //文件上传需要的key public String contentType = "multipart/form-data"; //上传的文件类型 public String charset = "utf-8"; + public String userAgent = "User-Agent"; /** * 文件上传表单 diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java index 12e5a702..ce9abc61 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadUtil.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ package com.arialyy.aria.core.upload; import android.util.Log; @@ -11,7 +26,6 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.Set; @@ -28,8 +42,6 @@ public class UploadUtil implements Runnable { private UploadEntity mUploadEntity; private UploadTaskEntity mTaskEntity; private IUploadListener mListener; - private PrintWriter mWriter; - private OutputStream mOutputStream; private HttpURLConnection mHttpConn; private long mCurrentLocation = 0; private boolean isCancel = false; @@ -51,7 +63,7 @@ public class UploadUtil implements Runnable { new Thread(this).start(); } - public void cancel(){ + public void cancel() { isCancel = true; isRunning = false; } @@ -66,15 +78,16 @@ public class UploadUtil implements Runnable { mListener.onPre(); - URL url = null; + URL url; try { url = new URL(mTaskEntity.uploadUrl); mHttpConn = (HttpURLConnection) url.openConnection(); mHttpConn.setUseCaches(false); mHttpConn.setDoOutput(true); mHttpConn.setDoInput(true); - mHttpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); - mHttpConn.setRequestProperty("User-Agent", "CodeJava Agent"); + mHttpConn.setRequestProperty("Content-Type", + mTaskEntity.contentType + "; boundary=" + BOUNDARY); + mHttpConn.setRequestProperty("User-Agent", mTaskEntity.userAgent); //mHttpConn.setRequestProperty("Range", "bytes=" + 0 + "-" + "100"); //内部缓冲区---分段上传防止oom mHttpConn.setChunkedStreamingMode(1024); @@ -85,81 +98,78 @@ public class UploadUtil implements Runnable { mHttpConn.setRequestProperty(key, mTaskEntity.headers.get(key)); } - mOutputStream = mHttpConn.getOutputStream(); - mWriter = new PrintWriter(new OutputStreamWriter(mOutputStream, mTaskEntity.charset), true); + OutputStream outputStream = mHttpConn.getOutputStream(); + PrintWriter writer = + new PrintWriter(new OutputStreamWriter(outputStream, mTaskEntity.charset), true); //添加文件上传表单字段 keys = mTaskEntity.formFields.keySet(); for (String key : keys) { - addFormField(key, mTaskEntity.formFields.get(key)); + addFormField(writer, key, mTaskEntity.formFields.get(key)); } mListener.onStart(uploadFile.length()); - addFilePart(mTaskEntity.attachment, uploadFile); - Log.d(TAG, finish() + ""); - } catch (MalformedURLException e) { - e.printStackTrace(); - fail(); + uploadFile(writer, outputStream, mTaskEntity.attachment, uploadFile); + Log.d(TAG, finish(writer) + ""); } catch (IOException e) { e.printStackTrace(); fail(); } } - public boolean isRunning() { + boolean isRunning() { return isRunning; } private void fail() { - mWriter.flush(); - mWriter.close(); mListener.onFail(); } /** * 添加文件上传表单字段 */ - private void addFormField(String name, String value) { - mWriter.append(PREFIX).append(BOUNDARY).append(LINE_END); - mWriter.append("Content-Disposition: form-data; name=\"") + private void addFormField(PrintWriter writer, String name, String value) { + writer.append(PREFIX).append(BOUNDARY).append(LINE_END); + writer.append("Content-Disposition: form-data; name=\"") .append(name) .append("\"") .append(LINE_END); - mWriter.append("Content-Type: text/plain; charset=") + writer.append("Content-Type: text/plain; charset=") .append(mTaskEntity.charset) .append(LINE_END); - mWriter.append(LINE_END); - mWriter.append(value).append(LINE_END); - mWriter.flush(); + writer.append(LINE_END); + writer.append(value).append(LINE_END); + writer.flush(); } /** * 上传文件 * - * @param fieldName 文件上传attachment + * @param attachment 文件上传attachment * @throws IOException */ - private void addFilePart(String fieldName, File uploadFile) throws IOException { - String fileName = uploadFile.getName(); - mWriter.append(PREFIX).append(BOUNDARY).append(LINE_END); - mWriter.append("Content-Disposition: form-data; name=\"") - .append(fieldName) + private void uploadFile(PrintWriter writer, OutputStream outputStream, String attachment, + File uploadFile) throws IOException { + writer.append(PREFIX).append(BOUNDARY).append(LINE_END); + writer.append("Content-Disposition: form-data; name=\"") + .append(attachment) .append("\"; filename=\"") - .append(fileName) + .append(mTaskEntity.uploadEntity.getFileName()) .append("\"") .append(LINE_END); - mWriter.append("Content-Type: ") - .append(URLConnection.guessContentTypeFromName(fileName)) + writer.append("Content-Type: ") + //.append(URLConnection.guessContentTypeFromName(mTaskEntity.uploadEntity.getFileName())) + .append(mTaskEntity.contentType) .append(LINE_END); - mWriter.append("Content-Transfer-Encoding: binary").append(LINE_END); - mWriter.append(LINE_END); - mWriter.flush(); + writer.append("Content-Transfer-Encoding: binary").append(LINE_END); + writer.append(LINE_END); + writer.flush(); FileInputStream inputStream = new FileInputStream(uploadFile); - byte[] buffer = new byte[1024]; - int bytesRead = -1; + byte[] buffer = new byte[4096]; + int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { mCurrentLocation += bytesRead; - mOutputStream.write(buffer, 0, bytesRead); + outputStream.write(buffer, 0, bytesRead); if (isCancel) { break; } @@ -167,10 +177,11 @@ public class UploadUtil implements Runnable { mListener.onProgress(mCurrentLocation); } - mOutputStream.flush(); + outputStream.flush(); + outputStream.close(); inputStream.close(); - mWriter.append(LINE_END); - mWriter.flush(); + writer.append(LINE_END); + writer.flush(); isRunning = false; if (isCancel) { mListener.onCancel(); @@ -184,28 +195,28 @@ public class UploadUtil implements Runnable { * * @throws IOException */ - private String finish() throws IOException { + private String finish(PrintWriter writer) throws IOException { StringBuilder response = new StringBuilder(); - mWriter.append(LINE_END).flush(); - mWriter.append(PREFIX).append(BOUNDARY).append(PREFIX).append(LINE_END); - mWriter.close(); + writer.append(LINE_END).flush(); + writer.append(PREFIX).append(BOUNDARY).append(PREFIX).append(LINE_END); + writer.close(); int status = mHttpConn.getResponseCode(); if (status == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(mHttpConn.getInputStream())); - String line = null; + String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); mHttpConn.disconnect(); } else { - throw new IOException("Server returned non-OK status: " + status); + Log.w(TAG, "state_code = " + status); } - mWriter.flush(); - mWriter.close(); + writer.flush(); + writer.close(); return response.toString(); } diff --git a/Aria/src/main/java/com/arialyy/aria/util/MultipartUtility.java b/Aria/src/main/java/com/arialyy/aria/util/MultipartUtility.java new file mode 100644 index 00000000..3b1d9843 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/util/MultipartUtility.java @@ -0,0 +1,151 @@ +package com.arialyy.aria.util; + +import java.io.BufferedReader; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +/** + * This utility class provides an abstraction layer for sending multipart HTTP + * POST requests to a web server. + * @author www.codejava.net + * + */ +public class MultipartUtility { + private final String boundary; + private static final String LINE_FEED = "\r\n"; + private HttpURLConnection httpConn; + private String charset; + private OutputStream outputStream; + private PrintWriter writer; + + /** + * This constructor initializes a new HTTP POST request with content type + * is set to multipart/form-data + * @param requestURL + * @param charset + * @throws IOException + */ + public MultipartUtility(String requestURL, String charset) + throws IOException { + this.charset = charset; + + // creates a unique boundary based on time stamp + boundary = "===" + System.currentTimeMillis() + "==="; + + URL url = new URL(requestURL); + httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setUseCaches(false); + httpConn.setDoOutput(true); // indicates POST method + httpConn.setDoInput(true); + httpConn.setRequestProperty("Content-Type", + "multipart/form-data; boundary=" + boundary); + //httpConn.setRequestProperty("User-Agent", "CodeJava Agent"); + //httpConn.setRequestProperty("Test", "Bonjour"); + outputStream = httpConn.getOutputStream(); + writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), + true); + } + + /** + * Adds a form field to the request + * @param name field name + * @param value field value + */ + public void addFormField(String name, String value) { + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + name + "\"") + .append(LINE_FEED); + writer.append("Content-Type: text/plain; charset=" + charset).append( + LINE_FEED); + writer.append(LINE_FEED); + writer.append(value).append(LINE_FEED); + writer.flush(); + } + + /** + * Adds a upload file section to the request + * @param fieldName name attribute in + * @param uploadFile a File to be uploaded + * @throws IOException + */ + public void addFilePart(String fieldName, File uploadFile) + throws IOException { + String fileName = uploadFile.getName(); + writer.append("--" + boundary).append(LINE_FEED); + writer.append( + "Content-Disposition: form-data; name=\"" + fieldName + + "\"; filename=\"" + fileName + "\"") + .append(LINE_FEED); + writer.append( + "Content-Type: " + + URLConnection.guessContentTypeFromName(fileName)) + .append(LINE_FEED); + writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); + writer.append(LINE_FEED); + writer.flush(); + + FileInputStream inputStream = new FileInputStream(uploadFile); + byte[] buffer = new byte[4096]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + inputStream.close(); + + writer.append(LINE_FEED); + writer.flush(); + } + + /** + * Adds a header field to the request. + * @param name - name of the header field + * @param value - value of the header field + */ + public void addHeaderField(String name, String value) { + writer.append(name + ": " + value).append(LINE_FEED); + writer.flush(); + } + + /** + * Completes the request and receives response from the server. + * @return a list of Strings as response in case the server returned + * status OK, otherwise an exception is thrown. + * @throws IOException + */ + public List finish() throws IOException { + List response = new ArrayList(); + + writer.append(LINE_FEED).flush(); + writer.append("--" + boundary + "--").append(LINE_FEED); + writer.close(); + + // checks server's status code first + int status = httpConn.getResponseCode(); + if (status == HttpURLConnection.HTTP_OK) { + BufferedReader reader = new BufferedReader(new InputStreamReader( + httpConn.getInputStream())); + String line = null; + while ((line = reader.readLine()) != null) { + response.add(line); + } + reader.close(); + httpConn.disconnect(); + } else { + throw new IOException("Server returned non-OK status: " + status); + } + + return response; + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5aacf3df..edc9d707 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,10 +13,8 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme.NoActionBar"> - - @@ -25,10 +23,11 @@ - - - - + + + + + diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index 0df80bbc..d56a452b 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -1,114 +1,37 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - package com.arialyy.simple; -import android.Manifest; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.support.v7.widget.Toolbar; -import android.view.Gravity; import android.view.View; -import android.widget.Button; import butterknife.Bind; -import com.arialyy.frame.permission.OnPermissionCallback; -import com.arialyy.frame.permission.PermissionManager; -import com.arialyy.frame.util.show.T; +import butterknife.OnClick; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityMainBinding; -import com.arialyy.simple.dialog_task.DownloadDialog; -import com.arialyy.simple.fragment_task.FragmentActivity; -import com.arialyy.simple.multi_task.MultiTaskActivity; -import com.arialyy.simple.notification.SimpleNotification; -import com.arialyy.simple.pop_task.DownloadPopupWindow; -import com.arialyy.simple.single_task.SingleTaskActivity; +import com.arialyy.simple.download.DownloadActivity; +import com.arialyy.simple.upload.UploadActivity; /** - * Created by Lyy on 2016/10/13. + * Created by Aria.Lao on 2017/3/1. */ public class MainActivity extends BaseActivity { - @Bind(R.id.toolbar) Toolbar mBar; - @Bind(R.id.single_task) Button mSigleBt; - @Bind(R.id.multi_task) Button mMultiBt; - @Bind(R.id.dialog_task) Button mDialogBt; - @Bind(R.id.pop_task) Button mPopBt; - @Override protected int setLayoutId() { - return R.layout.activity_main; - } + @Bind(R.id.toolbar) Toolbar mBar; @Override protected void init(Bundle savedInstanceState) { super.init(savedInstanceState); - setSupportActionBar(mBar); - mBar.setTitle("多线程多任务下载"); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - setEnable(true); - } else { //6.0处理 - boolean hasPermission = PermissionManager.getInstance() - .checkPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (hasPermission) { - setEnable(true); - } else { - setEnable(false); - PermissionManager.getInstance().requestPermission(this, new OnPermissionCallback() { - @Override public void onSuccess(String... permissions) { - setEnable(true); - } + mBar.setTitle("Aria Demo"); + } - @Override public void onFail(String... permissions) { - T.showShort(MainActivity.this, "没有文件读写权限"); - setEnable(false); - } - }, Manifest.permission.WRITE_EXTERNAL_STORAGE); - } - } + @Override protected int setLayoutId() { + return R.layout.activity_main; } - private void setEnable(boolean enable) { - mSigleBt.setEnabled(enable); - mMultiBt.setEnabled(enable); - mDialogBt.setEnabled(enable); - mPopBt.setEnabled(enable); + @OnClick(R.id.download) public void downloadDemo() { + startActivity(new Intent(this, DownloadActivity.class)); } - public void onClick(View view) { - switch (view.getId()) { - case R.id.single_task: - startActivity(new Intent(this, SingleTaskActivity.class)); - break; - case R.id.multi_task: - startActivity(new Intent(this, MultiTaskActivity.class)); - break; - case R.id.dialog_task: - DownloadDialog dialog = new DownloadDialog(this); - dialog.show(); - break; - case R.id.pop_task: - DownloadPopupWindow pop = new DownloadPopupWindow(this); - //pop.showAsDropDown(mRootView); - pop.showAtLocation(mRootView, Gravity.CENTER_VERTICAL, 0, 0); - break; - case R.id.fragment_task: - startActivity(new Intent(this, FragmentActivity.class)); - break; - case R.id.notification: - SimpleNotification notification = new SimpleNotification(this); - notification.start(); - break; - } + @OnClick(R.id.upload) public void uploadDemo() { + startActivity(new Intent(this, UploadActivity.class)); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/arialyy/simple/download/DownloadActivity.java b/app/src/main/java/com/arialyy/simple/download/DownloadActivity.java new file mode 100644 index 00000000..eec0ce78 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/download/DownloadActivity.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package com.arialyy.simple.download; + +import android.Manifest; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.Gravity; +import android.view.View; +import android.widget.Button; +import butterknife.Bind; +import com.arialyy.frame.permission.OnPermissionCallback; +import com.arialyy.frame.permission.PermissionManager; +import com.arialyy.frame.util.show.T; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.databinding.ActivityDownloadMeanBinding; +import com.arialyy.simple.download.fragment_download.FragmentActivity; +import com.arialyy.simple.download.multi_download.MultiTaskActivity; + +/** + * Created by Lyy on 2016/10/13. + */ +public class DownloadActivity extends BaseActivity { + @Bind(R.id.toolbar) Toolbar mBar; + @Bind(R.id.single_task) Button mSigleBt; + @Bind(R.id.multi_task) Button mMultiBt; + @Bind(R.id.dialog_task) Button mDialogBt; + @Bind(R.id.pop_task) Button mPopBt; + + @Override protected int setLayoutId() { + return R.layout.activity_download_mean; + } + + @Override protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + setSupportActionBar(mBar); + mBar.setTitle("多线程多任务下载"); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + setEnable(true); + } else { //6.0处理 + boolean hasPermission = PermissionManager.getInstance() + .checkPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (hasPermission) { + setEnable(true); + } else { + setEnable(false); + PermissionManager.getInstance().requestPermission(this, new OnPermissionCallback() { + @Override public void onSuccess(String... permissions) { + setEnable(true); + } + + @Override public void onFail(String... permissions) { + T.showShort(DownloadActivity.this, "没有文件读写权限"); + setEnable(false); + } + }, Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + } + } + + private void setEnable(boolean enable) { + mSigleBt.setEnabled(enable); + mMultiBt.setEnabled(enable); + mDialogBt.setEnabled(enable); + mPopBt.setEnabled(enable); + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.single_task: + startActivity(new Intent(this, SingleTaskActivity.class)); + break; + case R.id.multi_task: + startActivity(new Intent(this, MultiTaskActivity.class)); + break; + case R.id.dialog_task: + DownloadDialog dialog = new DownloadDialog(this); + dialog.show(); + break; + case R.id.pop_task: + DownloadPopupWindow pop = new DownloadPopupWindow(this); + //pop.showAsDropDown(mRootView); + pop.showAtLocation(mRootView, Gravity.CENTER_VERTICAL, 0, 0); + break; + case R.id.fragment_task: + startActivity(new Intent(this, FragmentActivity.class)); + break; + case R.id.notification: + SimpleNotification notification = new SimpleNotification(this); + notification.start(); + break; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/dialog_task/DownloadDialog.java b/app/src/main/java/com/arialyy/simple/download/DownloadDialog.java similarity index 98% rename from app/src/main/java/com/arialyy/simple/dialog_task/DownloadDialog.java rename to app/src/main/java/com/arialyy/simple/download/DownloadDialog.java index f1092a96..62a7e593 100644 --- a/app/src/main/java/com/arialyy/simple/dialog_task/DownloadDialog.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadDialog.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.dialog_task; +package com.arialyy.simple.download; import android.content.Context; import android.os.Environment; diff --git a/app/src/main/java/com/arialyy/simple/module/DownloadModule.java b/app/src/main/java/com/arialyy/simple/download/DownloadModule.java similarity index 98% rename from app/src/main/java/com/arialyy/simple/module/DownloadModule.java rename to app/src/main/java/com/arialyy/simple/download/DownloadModule.java index 4a084193..a4303708 100644 --- a/app/src/main/java/com/arialyy/simple/module/DownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadModule.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.simple.module; +package com.arialyy.simple.download; import android.content.BroadcastReceiver; import android.content.Context; @@ -29,8 +29,7 @@ import com.arialyy.frame.util.AndroidUtils; import com.arialyy.frame.util.StringUtil; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; -import com.arialyy.simple.multi_task.FileListEntity; -import com.arialyy.simple.single_task.SingleTaskActivity; +import com.arialyy.simple.download.multi_download.FileListEntity; import com.arialyy.simple.base.BaseModule; import java.io.File; import java.util.ArrayList; diff --git a/app/src/main/java/com/arialyy/simple/pop_task/DownloadPopupWindow.java b/app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java similarity index 99% rename from app/src/main/java/com/arialyy/simple/pop_task/DownloadPopupWindow.java rename to app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java index 176ba3e0..c5631a4f 100644 --- a/app/src/main/java/com/arialyy/simple/pop_task/DownloadPopupWindow.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.pop_task; +package com.arialyy.simple.download; import android.content.Context; import android.graphics.Color; diff --git a/app/src/main/java/com/arialyy/simple/notification/SimpleNotification.java b/app/src/main/java/com/arialyy/simple/download/SimpleNotification.java similarity index 98% rename from app/src/main/java/com/arialyy/simple/notification/SimpleNotification.java rename to app/src/main/java/com/arialyy/simple/download/SimpleNotification.java index e16d1d87..e4e6c605 100644 --- a/app/src/main/java/com/arialyy/simple/notification/SimpleNotification.java +++ b/app/src/main/java/com/arialyy/simple/download/SimpleNotification.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.notification; +package com.arialyy.simple.download; import android.app.NotificationManager; import android.content.Context; diff --git a/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java similarity index 99% rename from app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java rename to app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java index a398b82b..12941c7f 100644 --- a/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.simple.single_task; +package com.arialyy.simple.download; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/app/src/main/java/com/arialyy/simple/fragment_task/DownloadFragment.java b/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java similarity index 98% rename from app/src/main/java/com/arialyy/simple/fragment_task/DownloadFragment.java rename to app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java index edd7155a..d9599818 100644 --- a/app/src/main/java/com/arialyy/simple/fragment_task/DownloadFragment.java +++ b/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.fragment_task; +package com.arialyy.simple.download.fragment_download; import android.os.Bundle; import android.os.Environment; diff --git a/app/src/main/java/com/arialyy/simple/fragment_task/FragmentActivity.java b/app/src/main/java/com/arialyy/simple/download/fragment_download/FragmentActivity.java similarity index 86% rename from app/src/main/java/com/arialyy/simple/fragment_task/FragmentActivity.java rename to app/src/main/java/com/arialyy/simple/download/fragment_download/FragmentActivity.java index ff7a30cd..385c5769 100644 --- a/app/src/main/java/com/arialyy/simple/fragment_task/FragmentActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/fragment_download/FragmentActivity.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.fragment_task; +package com.arialyy.simple.download.fragment_download; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; diff --git a/app/src/main/java/com/arialyy/simple/multi_task/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java similarity index 99% rename from app/src/main/java/com/arialyy/simple/multi_task/DownloadAdapter.java rename to app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java index 17a7d3be..50326e33 100644 --- a/app/src/main/java/com/arialyy/simple/multi_task/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.simple.multi_task; +package com.arialyy.simple.download.multi_download; import android.content.Context; import android.content.res.Resources; diff --git a/app/src/main/java/com/arialyy/simple/multi_task/DownloadNumDialog.java b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadNumDialog.java similarity index 97% rename from app/src/main/java/com/arialyy/simple/multi_task/DownloadNumDialog.java rename to app/src/main/java/com/arialyy/simple/download/multi_download/DownloadNumDialog.java index 9f666f3e..c6170809 100644 --- a/app/src/main/java/com/arialyy/simple/multi_task/DownloadNumDialog.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadNumDialog.java @@ -15,7 +15,7 @@ */ -package com.arialyy.simple.multi_task; +package com.arialyy.simple.download.multi_download; import android.annotation.SuppressLint; import android.os.Bundle; diff --git a/app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java b/app/src/main/java/com/arialyy/simple/download/multi_download/FileListAdapter.java similarity index 98% rename from app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java rename to app/src/main/java/com/arialyy/simple/download/multi_download/FileListAdapter.java index 65ebccf7..f2349390 100644 --- a/app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/FileListAdapter.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.multi_task; +package com.arialyy.simple.download.multi_download; import android.content.Context; import android.view.View; diff --git a/app/src/main/java/com/arialyy/simple/multi_task/FileListEntity.java b/app/src/main/java/com/arialyy/simple/download/multi_download/FileListEntity.java similarity index 70% rename from app/src/main/java/com/arialyy/simple/multi_task/FileListEntity.java rename to app/src/main/java/com/arialyy/simple/download/multi_download/FileListEntity.java index 15d584a8..0f356b11 100644 --- a/app/src/main/java/com/arialyy/simple/multi_task/FileListEntity.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/FileListEntity.java @@ -1,4 +1,4 @@ -package com.arialyy.simple.multi_task; +package com.arialyy.simple.download.multi_download; /** * Created by AriaL on 2017/1/6. diff --git a/app/src/main/java/com/arialyy/simple/multi_task/DownloadActivity.java b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java similarity index 83% rename from app/src/main/java/com/arialyy/simple/multi_task/DownloadActivity.java rename to app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java index 61d4836a..304f685a 100644 --- a/app/src/main/java/com/arialyy/simple/multi_task/DownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java @@ -1,26 +1,28 @@ -package com.arialyy.simple.multi_task; +package com.arialyy.simple.download.multi_download; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; import butterknife.Bind; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; -import com.arialyy.simple.databinding.ActivityDownloadBinding; +import com.arialyy.simple.databinding.ActivityMultiDownloadBinding; /** * Created by AriaL on 2017/1/6. */ -public class DownloadActivity extends BaseActivity { - @Bind(R.id.list) RecyclerView mList; - private DownloadAdapter mAdapter; +public class MultiDownloadActivity extends BaseActivity { + @Bind(R.id.list) RecyclerView mList; + @Bind(R.id.toolbar) Toolbar mBar; + private DownloadAdapter mAdapter; @Override protected int setLayoutId() { - return R.layout.activity_download; + return R.layout.activity_multi_download; } @Override protected void init(Bundle savedInstanceState) { @@ -28,6 +30,7 @@ public class DownloadActivity extends BaseActivity { mAdapter = new DownloadAdapter(this, Aria.download(this).getTaskList()); mList.setLayoutManager(new LinearLayoutManager(this)); mList.setAdapter(mAdapter); + mBar.setTitle("多任务下载"); } @Override protected void dataCallback(int result, Object data) { diff --git a/app/src/main/java/com/arialyy/simple/multi_task/MultiTaskActivity.java b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiTaskActivity.java similarity index 95% rename from app/src/main/java/com/arialyy/simple/multi_task/MultiTaskActivity.java rename to app/src/main/java/com/arialyy/simple/download/multi_download/MultiTaskActivity.java index e8cb4af5..1817754a 100644 --- a/app/src/main/java/com/arialyy/simple/multi_task/MultiTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiTaskActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.simple.multi_task; +package com.arialyy.simple.download.multi_download; import android.content.Intent; import android.os.Bundle; @@ -28,7 +28,7 @@ import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityMultiBinding; -import com.arialyy.simple.module.DownloadModule; +import com.arialyy.simple.download.DownloadModule; import java.util.ArrayList; import java.util.List; @@ -65,7 +65,7 @@ public class MultiTaskActivity extends BaseActivity { Aria.download(this).stopAllTask(); break; case R.id.turn: - startActivity(new Intent(this, DownloadActivity.class)); + startActivity(new Intent(this, MultiDownloadActivity.class)); break; } } diff --git a/app/src/main/java/com/arialyy/simple/upload.java b/app/src/main/java/com/arialyy/simple/upload.java deleted file mode 100644 index bdf1a22d..00000000 --- a/app/src/main/java/com/arialyy/simple/upload.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.arialyy.simple; - -import android.view.View; -import android.widget.ProgressBar; -import butterknife.Bind; -import com.arialyy.aria.core.upload.IUploadListener; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTaskEntity; -import com.arialyy.aria.core.upload.UploadUtil; -import com.arialyy.simple.base.BaseActivity; -import com.arialyy.simple.databinding.ActivityUploadBinding; -import com.arialyy.simple.widget.HorizontalProgressBarWithNumber; - -/** - * Created by Aria.Lao on 2017/2/9. - */ - -public class upload extends BaseActivity { - - @Bind(R.id.pb) HorizontalProgressBarWithNumber mPb; - - @Override protected int setLayoutId() { - return R.layout.activity_upload; - } - - public void onClick(View view) { - UploadEntity entity = new UploadEntity(); - entity.setFilePath("/sdcard/Download/test.zip"); - entity.setFileName("test.pdf"); - UploadTaskEntity taskEntity = new UploadTaskEntity(entity); - taskEntity.uploadUrl = "http://172.18.104.189:8080/upload/sign_file"; - taskEntity.attachment = "file"; - UploadUtil util = new UploadUtil(taskEntity, new IUploadListener() { - long fileSize = 0; - - @Override public void onPre() { - - } - - @Override public void onStart(long fileSize) { - this.fileSize = fileSize; - } - - @Override public void onResume(long resumeLocation) { - - } - - @Override public void onStop(long stopLocation) { - - } - - @Override public void onProgress(long currentLocation) { - int p = (int) (currentLocation * 100 / fileSize); - mPb.setProgress(p); - } - - @Override public void onCancel() { - - } - - @Override public void onComplete() { - - } - - @Override public void onFail() { - - } - }); - util.start(); - } -} diff --git a/app/src/main/java/com/arialyy/simple/upload/UploadActivity.java b/app/src/main/java/com/arialyy/simple/upload/UploadActivity.java new file mode 100644 index 00000000..c75b2cda --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/upload/UploadActivity.java @@ -0,0 +1,197 @@ +package com.arialyy.simple.upload; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import butterknife.Bind; +import butterknife.OnClick; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.upload.IUploadListener; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.core.upload.UploadUtil; +import com.arialyy.aria.util.MultipartUtility; +import com.arialyy.frame.util.FileUtil; +import com.arialyy.frame.util.show.L; +import com.arialyy.frame.util.show.T; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.databinding.ActivityUploadMeanBinding; +import com.arialyy.simple.widget.HorizontalProgressBarWithNumber; +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.List; + +/** + * Created by Aria.Lao on 2017/2/9. + */ + +public class UploadActivity extends BaseActivity { + @Bind(R.id.pb) HorizontalProgressBarWithNumber mPb; + private static final int START = 0; + private static final int STOP = 1; + private static final int CANCEL = 2; + private static final int RUNNING = 3; + private static final int COMPLETE = 4; + + private static final String FILE_PATH = "/sdcard/Download/test.zip"; + + private Handler mHandler = new Handler() { + @Override public void handleMessage(Message msg) { + super.handleMessage(msg); + UploadTask task = (UploadTask) msg.obj; + switch (msg.what) { + case START: + getBinding().setFileSize(FileUtil.formatFileSize(task.getFileSize())); + break; + case STOP: + mPb.setProgress(0); + break; + case CANCEL: + mPb.setProgress(0); + break; + case RUNNING: + int p = (int) (task.getCurrentProgress() * 100 / task.getFileSize()); + mPb.setProgress(p); + break; + case COMPLETE: + T.showShort(UploadActivity.this, "上传完成"); + mPb.setProgress(100); + break; + } + } + }; + + @Override protected int setLayoutId() { + return R.layout.activity_upload_mean; + } + + @Override protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + //test(); + } + + private void test(){ + String charset = "UTF-8"; + File uploadFile1 = new File("/sdcard/Download/test.zip"); + String requestURL = "http://172.18.104.50:8080/UploadActivity/sign_file"; + + try { + MultipartUtility multipart = new MultipartUtility(requestURL, charset); + + //multipart.addHeaderField("Test-Header", "Header-Value"); + + multipart.addFilePart("file", uploadFile1); + List response = multipart.finish(); + + System.out.println("SERVER REPLIED:"); + + for (String line : response) { + System.out.println(line); + } + } catch (IOException ex) { + System.err.println(ex); + } + + //UploadEntity entity = new UploadEntity(); + //entity.setFilePath("/sdcard/Download/test.zip"); + //entity.setFileName("test.pdf"); + //UploadTaskEntity taskEntity = new UploadTaskEntity(entity); + //taskEntity.uploadUrl = "http://172.18.104.50:8080/UploadActivity/sign_file"; + //taskEntity.attachment = "file"; + //UploadUtil util = new UploadUtil(taskEntity, new IUploadListener() { + // long fileSize = 0; + // + // @Override public void onPre() { + // + // } + // + // @Override public void onStart(long fileSize) { + // this.fileSize = fileSize; + // } + // + // @Override public void onResume(long resumeLocation) { + // + // } + // + // @Override public void onStop(long stopLocation) { + // + // } + // + // @Override public void onProgress(long currentLocation) { + // int p = (int) (currentLocation * 100 / fileSize); + // mPb.setProgress(p); + // } + // + // @Override public void onCancel() { + // + // } + // + // @Override public void onComplete() { + // + // } + // + // @Override public void onFail() { + // + // } + //}); + //util.start(); + } + + @OnClick(R.id.upload) void upload() { + //Aria.upload(this) + // .load(FILE_PATH) + // .setUploadUrl("http://172.18.104.50:8080/upload/sign_file") + // .setAttachment("file") + // .start(); + test(); + } + + @OnClick(R.id.stop) void stop() { + Aria.upload(this).load(FILE_PATH).stop(); + } + + @OnClick(R.id.remove) void remove() { + Aria.upload(this).load(FILE_PATH).cancel(); + } + + @Override protected void onResume() { + super.onResume(); + Aria.upload(this).addSchedulerListener(new UploadListener(mHandler)); + } + + static class UploadListener extends Aria.UploadSchedulerListener { + WeakReference handler; + + UploadListener(Handler handler) { + this.handler = new WeakReference<>(handler); + } + + @Override public void onTaskStart(UploadTask task) { + super.onTaskStart(task); + handler.get().obtainMessage(START, task).sendToTarget(); + } + + @Override public void onTaskStop(UploadTask task) { + super.onTaskStop(task); + handler.get().obtainMessage(STOP, task).sendToTarget(); + } + + @Override public void onTaskCancel(UploadTask task) { + super.onTaskCancel(task); + handler.get().obtainMessage(CANCEL, task).sendToTarget(); + } + + @Override public void onTaskRunning(UploadTask task) { + super.onTaskRunning(task); + handler.get().obtainMessage(RUNNING, task).sendToTarget(); + } + + @Override public void onTaskComplete(UploadTask task) { + super.onTaskComplete(task); + handler.get().obtainMessage(COMPLETE, task).sendToTarget(); + } + } +} diff --git a/app/src/main/res/layout/activity_download_mean.xml b/app/src/main/res/layout/activity_download_mean.xml new file mode 100644 index 00000000..08ccd012 --- /dev/null +++ b/app/src/main/res/layout/activity_download_mean.xml @@ -0,0 +1,69 @@ + + + + + + + +