├── .gitignore ├── .idea └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── joe │ │ └── epmediademo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── Ress │ │ │ └── msyh.ttf │ ├── java │ │ └── com │ │ │ └── joe │ │ │ └── epmediademo │ │ │ ├── Activity │ │ │ ├── EditActivity.java │ │ │ ├── MainActivity.java │ │ │ └── MergeActivity.java │ │ │ ├── Application │ │ │ └── MyApplication.java │ │ │ └── Utils │ │ │ └── UriUtils.java │ └── res │ │ ├── layout │ │ ├── activity_edit.xml │ │ ├── activity_main.xml │ │ └── activity_merge.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── joe │ └── epmediademo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EpMediaDemo 2 | 基于FFmpeg开发的视频处理框架,简单易用,体积小,帮助使用者快速实现视频处理功能。包含以下功能:剪辑,裁剪,旋转,镜像,合并,分离,添加LOGO,添加滤镜,添加背景音乐,加速减速视频,倒放音视频。 3 | 简单的Demo,后面逐渐完善各类功能的使用。 4 | 原库地址 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '26.0.2' 6 | defaultConfig { 7 | applicationId "com.joe.epmediademo" 8 | minSdkVersion 18 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:25.1.0' 25 | compile 'com.android.support:support-v4:25.1.0' 26 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 27 | compile 'com.github.yangjie10930:EpMedia:v0.9.3' 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/joe/epmediademo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.joe.epmediademo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.joe.epmediademo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/assets/Ress/msyh.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangjie10930/EpMediaDemo/c222287426904b13fda63b10bf448b0cff2e1499/app/src/main/assets/Ress/msyh.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/joe/epmediademo/Activity/EditActivity.java: -------------------------------------------------------------------------------- 1 | package com.joe.epmediademo.Activity; 2 | 3 | import android.app.ProgressDialog; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Environment; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.CheckBox; 12 | import android.widget.CompoundButton; 13 | import android.widget.EditText; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.joe.epmediademo.Application.MyApplication; 18 | import com.joe.epmediademo.R; 19 | import com.joe.epmediademo.Utils.UriUtils; 20 | 21 | import VideoHandle.EpEditor; 22 | import VideoHandle.EpVideo; 23 | import VideoHandle.OnEditorListener; 24 | 25 | public class EditActivity extends AppCompatActivity implements View.OnClickListener { 26 | 27 | private static final int CHOOSE_FILE = 10; 28 | private CheckBox cb_clip, cb_crop, cb_rotation, cb_mirror, cb_text; 29 | private EditText et_clip_start, et_clip_end, et_crop_x, et_crop_y, et_crop_w, et_crop_h, et_rotation, et_text_x, et_text_y, et_text; 30 | private TextView tv_file; 31 | private Button bt_file, bt_exec; 32 | private String videoUrl; 33 | private ProgressDialog mProgressDialog; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_edit); 39 | initView(); 40 | } 41 | 42 | private void initView() { 43 | cb_clip = (CheckBox) findViewById(R.id.cb_clip); 44 | cb_crop = (CheckBox) findViewById(R.id.cb_crop); 45 | cb_rotation = (CheckBox) findViewById(R.id.cb_rotation); 46 | cb_mirror = (CheckBox) findViewById(R.id.cb_mirror); 47 | cb_text = (CheckBox) findViewById(R.id.cb_text); 48 | et_clip_start = (EditText) findViewById(R.id.et_clip_start); 49 | et_clip_end = (EditText) findViewById(R.id.et_clip_end); 50 | et_crop_x = (EditText) findViewById(R.id.et_crop_x); 51 | et_crop_y = (EditText) findViewById(R.id.et_crop_y); 52 | et_crop_w = (EditText) findViewById(R.id.et_crop_w); 53 | et_crop_h = (EditText) findViewById(R.id.et_crop_h); 54 | et_rotation = (EditText) findViewById(R.id.et_rotation); 55 | et_text_x = (EditText) findViewById(R.id.et_text_x); 56 | et_text_y = (EditText) findViewById(R.id.et_text_y); 57 | et_text = (EditText) findViewById(R.id.et_text); 58 | tv_file = (TextView) findViewById(R.id.tv_file); 59 | bt_file = (Button) findViewById(R.id.bt_file); 60 | bt_exec = (Button) findViewById(R.id.bt_exec); 61 | bt_file.setOnClickListener(this); 62 | bt_exec.setOnClickListener(this); 63 | cb_mirror.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 64 | @Override 65 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 66 | if(isChecked){ 67 | cb_rotation.setChecked(true); 68 | } 69 | } 70 | }); 71 | cb_rotation.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 72 | @Override 73 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 74 | if(!isChecked){ 75 | cb_mirror.setChecked(false); 76 | } 77 | } 78 | }); 79 | mProgressDialog = new ProgressDialog(this); 80 | mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 81 | mProgressDialog.setMax(100); 82 | mProgressDialog.setCancelable(false); 83 | mProgressDialog.setCanceledOnTouchOutside(false); 84 | mProgressDialog.setTitle("正在处理"); 85 | } 86 | 87 | @Override 88 | public void onClick(View v) { 89 | switch (v.getId()) { 90 | case R.id.bt_file: 91 | chooseFile(); 92 | break; 93 | case R.id.bt_exec: 94 | execVideo(); 95 | // test(); 96 | break; 97 | } 98 | } 99 | 100 | /** 101 | * 选择文件 102 | */ 103 | private void chooseFile() { 104 | Intent intent = new Intent(); 105 | intent.setType("video/*"); 106 | intent.setAction(Intent.ACTION_GET_CONTENT); 107 | intent.addCategory(Intent.CATEGORY_OPENABLE); 108 | startActivityForResult(intent, CHOOSE_FILE); 109 | } 110 | 111 | @Override 112 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 113 | switch (requestCode) { 114 | case CHOOSE_FILE: 115 | if (resultCode == RESULT_OK) { 116 | videoUrl = UriUtils.getPath(EditActivity.this, data.getData()); 117 | tv_file.setText(videoUrl); 118 | break; 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * 开始编辑 125 | */ 126 | private void execVideo(){ 127 | if(videoUrl != null && !"".equals(videoUrl)){ 128 | EpVideo epVideo = new EpVideo(videoUrl); 129 | if(cb_clip.isChecked()) 130 | epVideo.clip(Float.parseFloat(et_clip_start.getText().toString().trim()),Float.parseFloat(et_clip_end.getText().toString().trim())); 131 | if(cb_crop.isChecked()) 132 | epVideo.crop(Integer.parseInt(et_crop_w.getText().toString().trim()),Integer.parseInt(et_crop_h.getText().toString().trim()),Integer.parseInt(et_crop_x.getText().toString().trim()),Integer.parseInt(et_crop_y.getText().toString().trim())); 133 | if(cb_rotation.isChecked()) 134 | epVideo.rotation(Integer.parseInt(et_rotation.getText().toString().trim()),cb_mirror.isChecked()); 135 | if(cb_text.isChecked()) 136 | epVideo.addText(Integer.parseInt(et_text_x.getText().toString().trim()),Integer.parseInt(et_text_y.getText().toString().trim()),30,"red",MyApplication.getSavePath() + "msyh.ttf",et_text.getText().toString().trim()); 137 | mProgressDialog.setProgress(0); 138 | mProgressDialog.show(); 139 | final String outPath = MyApplication.getSavePath() + "out.mp4"; 140 | EpEditor.exec(epVideo, new EpEditor.OutputOption(outPath), new OnEditorListener() { 141 | @Override 142 | public void onSuccess() { 143 | Toast.makeText(EditActivity.this, "编辑完成:"+outPath, Toast.LENGTH_SHORT).show(); 144 | mProgressDialog.dismiss(); 145 | 146 | Intent v = new Intent(Intent.ACTION_VIEW); 147 | v.setDataAndType(Uri.parse(outPath), "video/mp4"); 148 | startActivity(v); 149 | } 150 | 151 | @Override 152 | public void onFailure() { 153 | Toast.makeText(EditActivity.this, "编辑失败", Toast.LENGTH_SHORT).show(); 154 | mProgressDialog.dismiss(); 155 | } 156 | 157 | @Override 158 | public void onProgress(float v) { 159 | mProgressDialog.setProgress((int) (v * 100)); 160 | } 161 | }); 162 | }else{ 163 | Toast.makeText(this, "选择一个视频", Toast.LENGTH_SHORT).show(); 164 | } 165 | } 166 | 167 | private void test(){ 168 | final String outPath = "/storage/emulated/0/Download/music.mp4"; 169 | EpEditor.music(videoUrl, "/storage/emulated/0/DownLoad/huluwa.aac", outPath, 1.0f, 1.0f, new OnEditorListener() { 170 | @Override 171 | public void onSuccess() { 172 | Toast.makeText(EditActivity.this, "编辑完成:"+outPath, Toast.LENGTH_SHORT).show(); 173 | 174 | Intent v = new Intent(Intent.ACTION_VIEW); 175 | v.setDataAndType(Uri.parse(outPath), "video/mp4"); 176 | startActivity(v); 177 | } 178 | 179 | @Override 180 | public void onFailure() { 181 | Toast.makeText(EditActivity.this, "编辑失败", Toast.LENGTH_SHORT).show(); 182 | } 183 | 184 | @Override 185 | public void onProgress(float v) { 186 | 187 | } 188 | }); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /app/src/main/java/com/joe/epmediademo/Activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.joe.epmediademo.Activity; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import com.joe.epmediademo.R; 9 | 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | findViewById(R.id.bt_one).setOnClickListener(new View.OnClickListener() { 21 | @Override 22 | public void onClick(View v) { 23 | startActivity(new Intent(MainActivity.this,EditActivity.class)); 24 | } 25 | }); 26 | findViewById(R.id.bt_more).setOnClickListener(new View.OnClickListener() { 27 | @Override 28 | public void onClick(View v) { 29 | startActivity(new Intent(MainActivity.this,MergeActivity.class)); 30 | } 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/joe/epmediademo/Activity/MergeActivity.java: -------------------------------------------------------------------------------- 1 | package com.joe.epmediademo.Activity; 2 | 3 | import android.app.ProgressDialog; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Button; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | import com.joe.epmediademo.Application.MyApplication; 14 | import com.joe.epmediademo.R; 15 | import com.joe.epmediademo.Utils.UriUtils; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import VideoHandle.EpEditor; 21 | import VideoHandle.EpVideo; 22 | import VideoHandle.OnEditorListener; 23 | 24 | public class MergeActivity extends AppCompatActivity implements View.OnClickListener { 25 | 26 | private static final int CHOOSE_FILE = 11; 27 | private TextView tv_add; 28 | private Button bt_add, bt_merge; 29 | private List videoList; 30 | private ProgressDialog mProgressDialog; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_merge); 36 | initView(); 37 | } 38 | 39 | private void initView() { 40 | tv_add = (TextView) findViewById(R.id.tv_add); 41 | bt_add = (Button) findViewById(R.id.bt_add); 42 | bt_merge = (Button) findViewById(R.id.bt_merge); 43 | videoList = new ArrayList<>(); 44 | mProgressDialog = new ProgressDialog(this); 45 | mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 46 | mProgressDialog.setMax(100); 47 | mProgressDialog.setCancelable(false); 48 | mProgressDialog.setCanceledOnTouchOutside(false); 49 | mProgressDialog.setTitle("正在处理"); 50 | bt_add.setOnClickListener(this); 51 | bt_merge.setOnClickListener(this); 52 | } 53 | 54 | @Override 55 | public void onClick(View v) { 56 | switch (v.getId()) { 57 | case R.id.bt_add: 58 | chooseFile(); 59 | break; 60 | case R.id.bt_merge: 61 | mergeVideo(); 62 | break; 63 | } 64 | } 65 | 66 | /** 67 | * 选择文件 68 | */ 69 | private void chooseFile() { 70 | Intent intent = new Intent(); 71 | intent.setType("video/*"); 72 | intent.setAction(Intent.ACTION_GET_CONTENT); 73 | intent.addCategory(Intent.CATEGORY_OPENABLE); 74 | startActivityForResult(intent, CHOOSE_FILE); 75 | } 76 | 77 | @Override 78 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 79 | switch (requestCode) { 80 | case CHOOSE_FILE: 81 | if (resultCode == RESULT_OK) { 82 | String videoUrl = UriUtils.getPath(MergeActivity.this, data.getData()); 83 | tv_add.setText(tv_add.getText() + videoUrl + "\n"); 84 | videoList.add(new EpVideo(videoUrl)); 85 | break; 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * 合并视频 92 | */ 93 | private void mergeVideo() { 94 | if (videoList.size() > 1) { 95 | mProgressDialog.setProgress(0); 96 | mProgressDialog.show(); 97 | final String outPath = MyApplication.getSavePath() + "outmerge.mp4"; 98 | EpEditor.merge(videoList, new EpEditor.OutputOption(outPath), new OnEditorListener() { 99 | @Override 100 | public void onSuccess() { 101 | Toast.makeText(MergeActivity.this, "编辑完成:"+outPath, Toast.LENGTH_SHORT).show(); 102 | mProgressDialog.dismiss(); 103 | 104 | Intent v = new Intent(Intent.ACTION_VIEW); 105 | v.setDataAndType(Uri.parse(outPath), "video/mp4"); 106 | startActivity(v); 107 | } 108 | 109 | @Override 110 | public void onFailure() { 111 | Toast.makeText(MergeActivity.this, "编辑失败", Toast.LENGTH_SHORT).show(); 112 | mProgressDialog.dismiss(); 113 | } 114 | 115 | @Override 116 | public void onProgress(float v) { 117 | mProgressDialog.setProgress((int) (v * 100)); 118 | } 119 | 120 | }); 121 | } else { 122 | Toast.makeText(this, "至少添加两个视频", Toast.LENGTH_SHORT).show(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/com/joe/epmediademo/Application/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.joe.epmediademo.Application; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.os.Environment; 6 | import android.util.Log; 7 | 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.InputStream; 11 | 12 | /** 13 | * Created by Yj on 2017/8/24. 14 | */ 15 | 16 | public class MyApplication extends Application { 17 | 18 | private static String savePath; 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | choseSavePath(); 24 | copyFilesFassets(getApplicationContext(), "Ress", savePath); 25 | } 26 | 27 | private void choseSavePath() { 28 | savePath = Environment.getExternalStorageDirectory().getPath() + "/EpMedia/"; 29 | File file = new File(savePath); 30 | if (!file.exists()) { 31 | file.mkdirs(); 32 | } 33 | } 34 | 35 | /** 36 | * 获取缓存路径 37 | * 38 | * @return 39 | */ 40 | public static String getSavePath() { 41 | return savePath; 42 | } 43 | 44 | /** 45 | * 从assets目录中复制文件到本地 46 | * 47 | * @param context Context 48 | * @param oldPath String 原文件路径 49 | * @param newPath String 复制后路径 50 | */ 51 | public static void copyFilesFassets(Context context, String oldPath, String newPath) { 52 | try { 53 | String fileNames[] = context.getAssets().list(oldPath); 54 | if (fileNames.length > 0) { 55 | File file = new File(newPath); 56 | file.mkdirs(); 57 | for (String fileName : fileNames) { 58 | copyFilesFassets(context, oldPath + "/" + fileName, newPath + "/" + fileName); 59 | } 60 | } else { 61 | InputStream is = context.getAssets().open(oldPath); 62 | File ff = new File(newPath); 63 | if (!ff.exists()) { 64 | FileOutputStream fos = new FileOutputStream(ff); 65 | byte[] buffer = new byte[1024]; 66 | int byteCount = 0; 67 | while ((byteCount = is.read(buffer)) != -1) { 68 | fos.write(buffer, 0, byteCount); 69 | } 70 | fos.flush(); 71 | is.close(); 72 | fos.close(); 73 | } 74 | } 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/joe/epmediademo/Utils/UriUtils.java: -------------------------------------------------------------------------------- 1 | package com.joe.epmediademo.Utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ContentUris; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.os.Environment; 10 | import android.provider.DocumentsContract; 11 | import android.provider.MediaStore; 12 | 13 | /** 14 | * Created by Administrator on 2016/11/30. 15 | */ 16 | 17 | @SuppressLint("NewApi") 18 | public class UriUtils { 19 | 20 | /** 21 | * Get a file path from a Uri. This will get the the path for Storage Access 22 | * Framework Documents, as well as the _data field for the MediaStore and 23 | * other file-based ContentProviders. 24 | * 25 | * @param context The context. 26 | * @param uri The Uri to query. 27 | */ 28 | public static String getPath(final Context context, final Uri uri) { 29 | 30 | final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; 31 | 32 | // DocumentProvider 33 | if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { 34 | // ExternalStorageProvider 35 | if (isExternalStorageDocument(uri)) { 36 | final String docId = DocumentsContract.getDocumentId(uri); 37 | final String[] split = docId.split(":"); 38 | final String type = split[0]; 39 | 40 | if ("primary".equalsIgnoreCase(type)) { 41 | return Environment.getExternalStorageDirectory() + "/" + split[1]; 42 | } 43 | 44 | // TODO handle non-primary volumes 45 | } 46 | // DownloadsProvider 47 | else if (isDownloadsDocument(uri)) { 48 | 49 | final String id = DocumentsContract.getDocumentId(uri); 50 | final Uri contentUri = ContentUris.withAppendedId( 51 | Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); 52 | 53 | return getDataColumn(context, contentUri, null, null); 54 | } 55 | // MediaProvider 56 | else if (isMediaDocument(uri)) { 57 | final String docId = DocumentsContract.getDocumentId(uri); 58 | final String[] split = docId.split(":"); 59 | final String type = split[0]; 60 | 61 | Uri contentUri = null; 62 | if ("image".equals(type)) { 63 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 64 | } else if ("video".equals(type)) { 65 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 66 | } else if ("audio".equals(type)) { 67 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 68 | } 69 | 70 | final String selection = "_id=?"; 71 | final String[] selectionArgs = new String[]{ 72 | split[1] 73 | }; 74 | 75 | return getDataColumn(context, contentUri, selection, selectionArgs); 76 | } 77 | } 78 | // MediaStore (and general) 79 | else if ("content".equalsIgnoreCase(uri.getScheme())) { 80 | return getDataColumn(context, uri, null, null); 81 | } 82 | // File 83 | else if ("file".equalsIgnoreCase(uri.getScheme())) { 84 | return uri.getPath(); 85 | } 86 | 87 | return null; 88 | } 89 | 90 | /** 91 | * Get the value of the data column for this Uri. This is useful for 92 | * MediaStore Uris, and other file-based ContentProviders. 93 | * 94 | * @param context The context. 95 | * @param uri The Uri to query. 96 | * @param selection (Optional) Filter used in the query. 97 | * @param selectionArgs (Optional) Selection arguments used in the query. 98 | * @return The value of the _data column, which is typically a file path. 99 | */ 100 | public static String getDataColumn(Context context, Uri uri, String selection, 101 | String[] selectionArgs) { 102 | 103 | Cursor cursor = null; 104 | final String column = "_data"; 105 | final String[] projection = { 106 | column 107 | }; 108 | 109 | try { 110 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, 111 | null); 112 | if (cursor != null && cursor.moveToFirst()) { 113 | final int column_index = cursor.getColumnIndexOrThrow(column); 114 | return cursor.getString(column_index); 115 | } 116 | } finally { 117 | if (cursor != null) 118 | cursor.close(); 119 | } 120 | return null; 121 | } 122 | 123 | 124 | /** 125 | * @param uri The Uri to check. 126 | * @return Whether the Uri authority is ExternalStorageProvider. 127 | */ 128 | public static boolean isExternalStorageDocument(Uri uri) { 129 | return "com.android.externalstorage.documents".equals(uri.getAuthority()); 130 | } 131 | 132 | /** 133 | * @param uri The Uri to check. 134 | * @return Whether the Uri authority is DownloadsProvider. 135 | */ 136 | public static boolean isDownloadsDocument(Uri uri) { 137 | return "com.android.providers.downloads.documents".equals(uri.getAuthority()); 138 | } 139 | 140 | /** 141 | * @param uri The Uri to check. 142 | * @return Whether the Uri authority is MediaProvider. 143 | */ 144 | public static boolean isMediaDocument(Uri uri) { 145 | return "com.android.providers.media.documents".equals(uri.getAuthority()); 146 | } 147 | 148 | } 149 | 150 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 18 | 19 | 20 | 21 | 28 | 29 | 34 | 35 | 39 | 40 | 47 | 48 | 52 | 53 | 60 | 61 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | 78 | 83 | 84 | 88 | 89 | 96 | 97 | 101 | 102 | 109 | 110 | 111 | 112 | 117 | 118 | 122 | 123 | 130 | 131 | 135 | 136 | 143 | 144 | 145 | 146 | 147 | 148 | 152 | 153 | 160 | 161 | 162 | 169 | 170 | 171 | 172 | 173 | 178 | 179 | 183 | 184 | 191 | 192 | 193 | 194 | 195 | 196 | 203 | 204 | 209 | 210 | 214 | 215 | 222 | 223 | 227 | 228 | 235 | 236 | 237 | 238 | 243 | 244 | 248 | 249 | 255 | 256 | 257 | 258 | 259 | 260 | 269 | 270 | 274 | 275 |