1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.soundpicker; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.media.RingtoneManager; 22 import android.net.Uri; 23 import android.os.AsyncTask; 24 import android.os.Environment; 25 import android.os.FileUtils; 26 import android.os.IBinder; 27 import android.provider.MediaStore; 28 import android.provider.Settings.System; 29 import android.util.Log; 30 31 import androidx.annotation.IdRes; 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 35 import java.io.File; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 40 /** 41 * Service to copy and set customization of default sounds 42 */ 43 public class RingtoneOverlayService extends Service { 44 private static final String TAG = "RingtoneOverlayService"; 45 private static final boolean DEBUG = false; 46 47 @Override onStartCommand(@ullable final Intent intent, final int flags, final int startId)48 public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) { 49 AsyncTask.execute(() -> { 50 updateRingtones(); 51 stopSelf(); 52 }); 53 54 // Try again later if we are killed before we finish. 55 return Service.START_REDELIVER_INTENT; 56 } 57 58 @Override onBind(@ullable final Intent intent)59 public IBinder onBind(@Nullable final Intent intent) { 60 return null; 61 } 62 updateRingtones()63 private void updateRingtones() { 64 copyResourceAndSetAsSound(R.raw.default_ringtone, 65 System.RINGTONE, Environment.DIRECTORY_RINGTONES); 66 copyResourceAndSetAsSound(R.raw.default_notification_sound, 67 System.NOTIFICATION_SOUND, Environment.DIRECTORY_NOTIFICATIONS); 68 copyResourceAndSetAsSound(R.raw.default_alarm_alert, 69 System.ALARM_ALERT, Environment.DIRECTORY_ALARMS); 70 } 71 72 /* If the resource contains any data, copy a resource to the file system, scan it, and set the 73 * file URI as the default for a sound. */ copyResourceAndSetAsSound(@dRes final int id, @NonNull final String name, @NonNull final String subPath)74 private void copyResourceAndSetAsSound(@IdRes final int id, @NonNull final String name, 75 @NonNull final String subPath) { 76 final File destDir = Environment.getExternalStoragePublicDirectory(subPath); 77 if (!destDir.exists() && !destDir.mkdirs()) { 78 Log.e(TAG, "can't create " + destDir.getAbsolutePath()); 79 return; 80 } 81 82 final File dest = new File(destDir, "default_" + name + ".ogg"); 83 try ( 84 InputStream is = getResources().openRawResource(id); 85 FileOutputStream os = new FileOutputStream(dest); 86 ) { 87 if (is.available() > 0) { 88 FileUtils.copy(is, os); 89 final Uri uri = scanFile(dest); 90 if (uri != null) { 91 set(name, uri); 92 } 93 } else { 94 // TODO Shall we remove any former copied resource in this case and unset 95 // the defaults if we use this event a second time to clear the data? 96 if (DEBUG) Log.d(TAG, "Resource for " + name + " has no overlay"); 97 } 98 } catch (IOException e) { 99 Log.e(TAG, "Unable to open resource for " + name + ": " + e); 100 } 101 } 102 scanFile(@onNull final File file)103 private Uri scanFile(@NonNull final File file) { 104 return MediaStore.scanFile(getContentResolver(), file); 105 } 106 set(@onNull final String name, @NonNull final Uri uri)107 private void set(@NonNull final String name, @NonNull final Uri uri) { 108 final Uri settingUri = System.getUriFor(name); 109 RingtoneManager.setActualDefaultRingtoneUri(this, 110 RingtoneManager.getDefaultType(settingUri), uri); 111 System.putInt(getContentResolver(), name + "_set", 1); 112 } 113 } 114