diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..23a89bb --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a5f05cd --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d5d35ec --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f6012c9 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "com.example.ringtone" + minSdkVersion 18 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + dataBinding { + enabled = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/release/app-release.apk b/app/release/app-release.apk new file mode 100644 index 0000000..112e40b Binary files /dev/null and b/app/release/app-release.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..1c77cd5 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.example.ringtone", + "variantName": "processReleaseResources", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-release.apk" + } + ] +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/ringtone/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/ringtone/ExampleInstrumentedTest.java new file mode 100644 index 0000000..3cfde7f --- /dev/null +++ b/app/src/androidTest/java/com/example/ringtone/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.ringtone; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.ringtone", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f1c420f --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/ringtone/FileUriPathUtils.java b/app/src/main/java/com/example/ringtone/FileUriPathUtils.java new file mode 100644 index 0000000..f8dd98f --- /dev/null +++ b/app/src/main/java/com/example/ringtone/FileUriPathUtils.java @@ -0,0 +1,82 @@ +package com.example.ringtone; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * Android之uri、file、path相互转化 + */ +public final class FileUriPathUtils { + private FileUriPathUtils() { + throw new UnsupportedOperationException("FileUriPathUtils can't instantiate me..."); + } + + public final String getUriToPath(Context context, Uri uri) { + String path = null; + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); + if (cursor == null) { + return null; + } + if (cursor.moveToFirst()) { + try { + path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); + } catch (Exception e) { + e.printStackTrace(); + } + } + cursor.close(); + return path; + } + + /** + * uri转file: * * @param uri + */ + public final File getUriToFile(Uri uri) { + File file = null; + try { + file = new File(new URI(uri.toString())); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return file; + } + + /** + * file转uri: * * @param file + */ + public final URI getFileToUri(File file) { + URI uri = file.toURI(); + return uri; + } + + /** + * file转path: * * @param file + */ + public final String getFileToPath(File file) { + String path = file.getPath(); + return path; + } + + /** + * path转uri: * * @param path + */ + public final Uri getPathToUri(String path) { + Uri uri = Uri.parse(path); + return uri; + } + + /** + * path转file: * * @param path + */ + public final File getPathToFile(String path) { + File file = new File(path); + return file; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ringtone/MainActivity.java b/app/src/main/java/com/example/ringtone/MainActivity.java new file mode 100644 index 0000000..b3a59a4 --- /dev/null +++ b/app/src/main/java/com/example/ringtone/MainActivity.java @@ -0,0 +1,279 @@ +package com.example.ringtone; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.FileProvider; +import androidx.databinding.DataBindingUtil; + +import android.Manifest; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.MediaStore; +import android.provider.Settings; +import android.util.Log; +import android.widget.Toast; + +import com.example.ringtone.databinding.ActivityMainBinding; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class MainActivity extends AppCompatActivity { + + //Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE, + String [] perms = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.WRITE_SETTINGS,Manifest.permission.MODIFY_AUDIO_SETTINGS}; + + ActivityMainBinding binding; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this,R.layout.activity_main); + binding.setAct(this); + checkFilePermission(); + } + + /** + * WRITE_SETTINGS权限申请 + */ + public void requestWriteSettings(){ + Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS); + intent.setData(Uri.parse("package:" + getPackageName())); + //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //开启一个新activity + startActivity(intent); + } + + /** + * 设置来电铃声 + */ + public void setRingtone(){ + Context context = getApplicationContext(); + String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "abc.mp3";//getExternalCacheDir().getAbsolutePath() + File.separator + "abc.mp3"; + File sdfile = new File(path); + boolean flag = sdfile.exists(); + Log.d("test","exists = " + flag + " path = "+path + " --- " + sdfile.getName()); + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!Settings.System.canWrite(MainActivity.this)) { + requestWriteSettings(); + return; + } + + } + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ + setRingtoneAndroidQ(sdfile); + }else { + setRingtone(this,RingtoneManager.TYPE_RINGTONE,path,"wtete"); + } + + } + + /** + * 适配android q以上版本 android 9 + * @param ringtoneFile + */ + private void setRingtoneAndroidQ(File ringtoneFile){ + Log.d("test","---setRingtoneAndroidQ---"); + ContentValues values = new ContentValues(); + values.put(MediaStore.Audio.Media.DISPLAY_NAME, "ring_demo.mp3"); + values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3"); + values.put(MediaStore.Audio.Media.TITLE, "ring_demo.mp3"); + values.put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/test"); + values.put(MediaStore.Audio.Media.IS_RINGTONE, true); + values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false); + values.put(MediaStore.Audio.Media.IS_ALARM, false); + values.put(MediaStore.Audio.Media.IS_MUSIC, false); + + Uri external = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;//MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + ContentResolver resolver = getContentResolver(); + + Uri insertUri = resolver.insert(external, values); + + Log.d("test","insertUri: " + insertUri); + + String uriPaht = getFilePathFromContentUri(insertUri,getContentResolver()); + Log.d("test","setRingtoneAndroidQ uriPaht: " + uriPaht); + + + OutputStream os = null; + FileInputStream inputStream = null; + + if(insertUri != null){ + try { + os = resolver.openOutputStream(insertUri); + if (os != null) { + inputStream = new FileInputStream(ringtoneFile); + byte[] bytes = new byte[1024]; + int len = 0; + while ((len=inputStream.read(bytes)) != -1) { + os.write(bytes,0,len); + } + inputStream.close(); + os.close(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + RingtoneManager.setActualDefaultRingtoneUri(this, RingtoneManager.TYPE_RINGTONE, insertUri); + Log.d("test","123 insertUri: " + insertUri); + Toast.makeText(this,"铃声设置完成",Toast.LENGTH_SHORT).show(); + } + /** + * + * 设置铃声 + * + * type RingtoneManager.TYPE_RINGTONE 来电铃声 + * RingtoneManager.TYPE_NOTIFICATION 通知铃声 + * RingtoneManager.TYPE_ALARM 闹钟铃声 + * + * path 下载下来的mp3全路径 + * title 铃声的名字 + */ + public void setRingtone(Context context, int type, String path, String title) { + + Uri oldRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE); //系统当前 通知铃声 + Uri oldNotification = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_NOTIFICATION); //系统当前 通知铃声 + Uri oldAlarm = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM); //系统当前 闹钟铃声 + + File sdfile = new File(path); + ContentValues values = new ContentValues(); + values.put(MediaStore.MediaColumns.DATA, sdfile.getAbsolutePath()); + values.put(MediaStore.MediaColumns.TITLE, title); + values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3"); + values.put(MediaStore.Audio.Media.IS_RINGTONE, true); + values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true); + values.put(MediaStore.Audio.Media.IS_ALARM, true); + values.put(MediaStore.Audio.Media.IS_MUSIC, true); + + Uri uri = MediaStore.Audio.Media.getContentUriForPath(sdfile.getAbsolutePath()); + Uri newUri = null; + String deleteId = ""; + try { + Cursor cursor = context.getContentResolver().query(uri, null, MediaStore.MediaColumns.DATA + "=?", new String[] { path },null); + if (cursor.moveToFirst()) { + deleteId = cursor.getString(cursor.getColumnIndex("_id")); + } + //LogTool.e("AGameRing", "deleteId:" + deleteId); + Log.d("test","checkFilePermission"); + context.getContentResolver().delete(uri, + MediaStore.MediaColumns.DATA + "=\"" + sdfile.getAbsolutePath() + "\"", null); + newUri = context.getContentResolver().insert(uri, values); + } catch (Exception e) { + e.printStackTrace(); + } + String uriPaht = getFilePathFromContentUri(newUri,getContentResolver()); + Log.d("test","setRingtone uriPaht: " + uriPaht); + if (newUri != null) { + + String ringStoneId = ""; + String notificationId = ""; + String alarmId = ""; + if (null != oldRingtoneUri) { + ringStoneId = oldRingtoneUri.getLastPathSegment(); + } + + if (null != oldNotification) { + notificationId = oldNotification.getLastPathSegment(); + } + + if (null != oldAlarm) { + alarmId = oldAlarm.getLastPathSegment(); + } + + Uri setRingStoneUri; + Uri setNotificationUri; + Uri setAlarmUri; + + if (type == RingtoneManager.TYPE_RINGTONE || ringStoneId.equals(deleteId)) { + setRingStoneUri = newUri; + } else { + setRingStoneUri = oldRingtoneUri; + } + + if (type == RingtoneManager.TYPE_NOTIFICATION || notificationId.equals(deleteId)) { + setNotificationUri = newUri; + } else { + setNotificationUri = oldNotification; + } + + if (type == RingtoneManager.TYPE_ALARM || alarmId.equals(deleteId)) { + setAlarmUri = newUri; + } else { + setAlarmUri = oldAlarm; + } + + RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE, setRingStoneUri); + RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_NOTIFICATION, setNotificationUri); + RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM, setAlarmUri); + + switch (type) { + case RingtoneManager.TYPE_RINGTONE: + Toast.makeText(context.getApplicationContext(), "设置来电铃声成功!", Toast.LENGTH_SHORT).show(); + break; + case RingtoneManager.TYPE_NOTIFICATION: + Toast.makeText(context.getApplicationContext(), "设置通知铃声成功!", Toast.LENGTH_SHORT).show(); + break; + case RingtoneManager.TYPE_ALARM: + Toast.makeText(context.getApplicationContext(), "设置闹钟铃声成功!", Toast.LENGTH_SHORT).show(); + break; + } + } + } + private String getFilePathFromContentUri(Uri selectedVideoUri, ContentResolver contentResolver) { + String filePath; + + String[] filePathColumn = {MediaStore.MediaColumns.DATA}; + + Cursor cursor = contentResolver.query(selectedVideoUri, filePathColumn, null, null, null); + + cursor.moveToFirst(); + + int columnIndex = cursor.getColumnIndex(filePathColumn[0]); + + filePath = cursor.getString(columnIndex); + + cursor.close(); + + return filePath; + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + } + + private void checkFilePermission() { + if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ + ActivityCompat.requestPermissions(this, perms, 123); + } + Log.d("test","checkFilePermission"); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + Log.d("test","onRequestPermissionsResult permissions = " + Arrays.toString(permissions) + " grantResults = " + Arrays.toString(grantResults)); + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..27771c4 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,41 @@ + + + + + + + + + + +