diff options
author | Michael W <baddaemon87@gmail.com> | 2022-12-29 16:27:29 +0100 |
---|---|---|
committer | Michael W <baddaemon87@gmail.com> | 2023-01-21 15:04:12 +0100 |
commit | 5f28f280623b24f4732b89132bc884da2dba358f (patch) | |
tree | 3c7958cdbac14e53e5316c53288eb153e0f531f5 | |
parent | f747fa4fe618194b97c3efae4b821527dc71a0f0 (diff) |
DeskClock: Convert AsyncTask(Loader)
* Deprecated and these are easy to convert
Change-Id: I839160aa54d28bf23dbb1c2e8e1b70e77927d631
6 files changed, 302 insertions, 348 deletions
diff --git a/src/com/android/deskclock/AlarmSelectionActivity.java b/src/com/android/deskclock/AlarmSelectionActivity.java index ab15e69f8..8cee59b8b 100644 --- a/src/com/android/deskclock/AlarmSelectionActivity.java +++ b/src/com/android/deskclock/AlarmSelectionActivity.java @@ -15,10 +15,8 @@ */ package com.android.deskclock; -import android.app.Activity; import android.app.ListActivity; import android.content.Intent; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.view.View; @@ -28,10 +26,12 @@ import android.widget.ListView; import com.android.deskclock.provider.Alarm; import com.android.deskclock.widget.selector.AlarmSelectionAdapter; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + public class AlarmSelectionActivity extends ListActivity { /** Used by default when an invalid action provided. */ @@ -84,33 +84,21 @@ public class AlarmSelectionActivity extends ListActivity { // id corresponds to mSelections id because the view adapter used mSelections final Alarm alarm = mSelections.get((int) id); if (alarm != null) { - new ProcessAlarmActionAsync(alarm, this, mAction).execute(); + processAlarmActionAsync(alarm); } finish(); } - private static class ProcessAlarmActionAsync extends AsyncTask<Void, Void, Void> { - - private final Alarm mAlarm; - private final WeakReference<Activity> mActivity; - private final int mAction; - - public ProcessAlarmActionAsync(Alarm alarm, Activity activity, int action) { - mAlarm = alarm; - mActivity = new WeakReference<>(activity); - mAction = action; - } - - @Override - protected Void doInBackground(Void... parameters) { + void processAlarmActionAsync(Alarm alarm) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(() -> { switch (mAction) { case ACTION_DISMISS: - HandleApiCalls.dismissAlarm(mAlarm, mActivity.get()); + HandleApiCalls.dismissAlarm(alarm, this); break; case ACTION_INVALID: LogUtils.i("Invalid action"); } - return null; - } + }); } } diff --git a/src/com/android/deskclock/HandleApiCalls.java b/src/com/android/deskclock/HandleApiCalls.java index 28e09a717..5b59d33d4 100644 --- a/src/com/android/deskclock/HandleApiCalls.java +++ b/src/com/android/deskclock/HandleApiCalls.java @@ -31,7 +31,6 @@ import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.AlarmClock; @@ -55,6 +54,8 @@ import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * This activity is never visible. It processes all public intents defined by {@link AlarmClock} @@ -165,7 +166,7 @@ public class HandleApiCalls extends Activity { Events.sendAlarmEvent(R.string.action_dismiss, R.string.label_intent); } - private static class DismissAlarmAsync extends AsyncTask<Void, Void, Void> { + private static class DismissAlarmAsync { private final Context mContext; private final Intent mIntent; @@ -177,66 +178,69 @@ public class HandleApiCalls extends Activity { mActivity = activity; } - @Override - protected Void doInBackground(Void... parameters) { - final ContentResolver cr = mContext.getContentResolver(); - final List<Alarm> alarms = getEnabledAlarms(mContext); - if (alarms.isEmpty()) { - final String reason = mContext.getString(R.string.no_scheduled_alarms); - Controller.getController().notifyVoiceFailure(mActivity, reason); - LOGGER.i("No scheduled alarms"); - return null; - } + protected void execute() { + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(() -> { + final ContentResolver cr = mContext.getContentResolver(); + final List<Alarm> alarms = getEnabledAlarms(mContext); + if (alarms.isEmpty()) { + final String reason = mContext.getString(R.string.no_scheduled_alarms); + Controller.getController().notifyVoiceFailure(mActivity, reason); + LOGGER.i("No scheduled alarms"); + return; + } - // remove Alarms in MISSED, DISMISSED, and PREDISMISSED states - for (Iterator<Alarm> i = alarms.iterator(); i.hasNext();) { - final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( - cr, i.next().id); - if (instance == null || instance.mAlarmState > FIRED_STATE) { - i.remove(); + // remove Alarms in MISSED, DISMISSED, and PREDISMISSED states + for (Iterator<Alarm> i = alarms.iterator(); i.hasNext();) { + final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( + cr, i.next().id); + if (instance == null || instance.mAlarmState > FIRED_STATE) { + i.remove(); + } } - } - final String searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE); - if (searchMode == null && alarms.size() > 1) { - // shows the UI where user picks which alarm they want to DISMISS - final Intent pickSelectionIntent = new Intent(mContext, - AlarmSelectionActivity.class) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(EXTRA_ACTION, ACTION_DISMISS) - .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[0])); - mContext.startActivity(pickSelectionIntent); - final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss); - Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage); - return null; - } + final String searchMode = mIntent.getStringExtra( + AlarmClock.EXTRA_ALARM_SEARCH_MODE); + if (searchMode == null && alarms.size() > 1) { + // shows the UI where user picks which alarm they want to DISMISS + final Intent pickSelectionIntent = new Intent(mContext, + AlarmSelectionActivity.class) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra(EXTRA_ACTION, ACTION_DISMISS) + .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[0])); + mContext.startActivity(pickSelectionIntent); + final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss); + Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage); + return; + } - // fetch the alarms that are specified by the intent - final FetchMatchingAlarmsAction fmaa = - new FetchMatchingAlarmsAction(mContext, alarms, mIntent, mActivity); - fmaa.run(); - final List<Alarm> matchingAlarms = fmaa.getMatchingAlarms(); - - // If there are multiple matching alarms and it wasn't expected - // disambiguate what the user meant - if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && matchingAlarms.size() > 1) { - final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(EXTRA_ACTION, ACTION_DISMISS) - .putExtra(EXTRA_ALARMS, - matchingAlarms.toArray(new Parcelable[0])); - mContext.startActivity(pickSelectionIntent); - final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss); - Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage); - return null; - } + // fetch the alarms that are specified by the intent + final FetchMatchingAlarmsAction fmaa = + new FetchMatchingAlarmsAction(mContext, alarms, mIntent, mActivity); + fmaa.run(); + final List<Alarm> matchingAlarms = fmaa.getMatchingAlarms(); + + // If there are multiple matching alarms and it wasn't expected + // disambiguate what the user meant + if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && + matchingAlarms.size() > 1) { + final Intent pickSelectionIntent = new Intent(mContext, + AlarmSelectionActivity.class) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra(EXTRA_ACTION, ACTION_DISMISS) + .putExtra(EXTRA_ALARMS, matchingAlarms.toArray(new Parcelable[0])); + mContext.startActivity(pickSelectionIntent); + final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss); + Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage); + return; + } - // Apply the action to the matching alarms - for (Alarm alarm : matchingAlarms) { - dismissAlarm(alarm, mActivity); - LOGGER.i("Alarm dismissed: " + alarm); - } - return null; + // Apply the action to the matching alarms + for (Alarm alarm : matchingAlarms) { + dismissAlarm(alarm, mActivity); + LOGGER.i("Alarm dismissed: " + alarm); + } + }); } private static List<Alarm> getEnabledAlarms(Context context) { @@ -247,36 +251,23 @@ public class HandleApiCalls extends Activity { } private void handleSnoozeAlarm() { - new SnoozeAlarmAsync(this).execute(); - } - - private static class SnoozeAlarmAsync extends AsyncTask<Void, Void, Void> { - - private final Context mContext; - private final Activity mActivity; - - public SnoozeAlarmAsync(Activity activity) { - mContext = activity.getApplicationContext(); - mActivity = activity; - } - - @Override - protected Void doInBackground(Void... parameters) { - final ContentResolver cr = mContext.getContentResolver(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(() -> { + final Context context = getApplicationContext(); + final ContentResolver cr = context.getContentResolver(); final List<AlarmInstance> alarmInstances = AlarmInstance.getInstancesByState( cr, FIRED_STATE); if (alarmInstances.isEmpty()) { - final String reason = mContext.getString(R.string.no_firing_alarms); - Controller.getController().notifyVoiceFailure(mActivity, reason); + final String reason = context.getString(R.string.no_firing_alarms); + Controller.getController().notifyVoiceFailure(this, reason); LOGGER.i("No firing alarms"); - return null; + return; } for (AlarmInstance firingAlarmInstance : alarmInstances) { - snoozeAlarm(firingAlarmInstance, mContext, mActivity); + snoozeAlarm(firingAlarmInstance, context, this); } - return null; - } + }); } static void snoozeAlarm(AlarmInstance alarmInstance, Context context, Activity activity) { diff --git a/src/com/android/deskclock/alarms/AlarmUpdateHandler.java b/src/com/android/deskclock/alarms/AlarmUpdateHandler.java index f085016a5..1e46f6ee7 100644 --- a/src/com/android/deskclock/alarms/AlarmUpdateHandler.java +++ b/src/com/android/deskclock/alarms/AlarmUpdateHandler.java @@ -18,7 +18,8 @@ package com.android.deskclock.alarms; import android.content.ContentResolver; import android.content.Context; -import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; import android.text.format.DateFormat; import android.view.View; import android.view.ViewGroup; @@ -33,6 +34,8 @@ import com.google.android.material.snackbar.Snackbar; import java.util.Calendar; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * API for asynchronously mutating a single alarm. @@ -59,37 +62,34 @@ public final class AlarmUpdateHandler { * @param alarm The alarm to be added. */ public void asyncAddAlarm(final Alarm alarm) { - final AsyncTask<Void, Void, AlarmInstance> updateTask = - new AsyncTask<Void, Void, AlarmInstance>() { - @Override - protected AlarmInstance doInBackground(Void... parameters) { - if (alarm != null) { - Events.sendAlarmEvent(R.string.action_create, R.string.label_deskclock); - ContentResolver cr = mAppContext.getContentResolver(); - - // Add alarm to db - Alarm newAlarm = Alarm.addAlarm(cr, alarm); - - // Be ready to scroll to this alarm on UI later. - mScrollHandler.setSmoothScrollStableId(newAlarm.id); - - // Create and add instance to db - if (newAlarm.enabled) { - return setupAlarmInstance(newAlarm); - } - } - return null; - } - - @Override - protected void onPostExecute(AlarmInstance instance) { - if (instance != null) { - AlarmUtils.popAlarmSetSnackbar( - mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis()); - } - } - }; - updateTask.execute(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler handler = new Handler(Looper.getMainLooper()); + executor.execute(() -> { + AlarmInstance instance = null; + if (alarm != null) { + Events.sendAlarmEvent(R.string.action_create, R.string.label_deskclock); + ContentResolver cr = mAppContext.getContentResolver(); + + // Add alarm to db + Alarm newAlarm = Alarm.addAlarm(cr, alarm); + + // Be ready to scroll to this alarm on UI later. + mScrollHandler.setSmoothScrollStableId(newAlarm.id); + + // Create and add instance to db + if (newAlarm.enabled) { + instance = setupAlarmInstance(newAlarm); + } + } + + final AlarmInstance finalInstance = instance; + handler.post(() -> { + if (finalInstance != null) { + AlarmUtils.popAlarmSetSnackbar( + mSnackbarAnchor, finalInstance.getAlarmTime().getTimeInMillis()); + } + }); + }); } /** @@ -101,51 +101,47 @@ public final class AlarmUpdateHandler { */ public void asyncUpdateAlarm(final Alarm alarm, final boolean popToast, final boolean minorUpdate) { - final AsyncTask<Void, Void, AlarmInstance> updateTask = - new AsyncTask<Void, Void, AlarmInstance>() { - @Override - protected AlarmInstance doInBackground(Void... parameters) { - ContentResolver cr = mAppContext.getContentResolver(); - - // Update alarm - Alarm.updateAlarm(cr, alarm); - - if (minorUpdate) { - // just update the instance in the database and update notifications. - final List<AlarmInstance> instanceList = - AlarmInstance.getInstancesByAlarmId(cr, alarm.id); - for (AlarmInstance instance : instanceList) { - // Make a copy of the existing instance - final AlarmInstance newInstance = new AlarmInstance(instance); - // Copy over minor change data to the instance; we don't know - // exactly which minor field changed, so just copy them all. - newInstance.mVibrate = alarm.vibrate; - newInstance.mRingtone = alarm.alert; - newInstance.mLabel = alarm.label; - // Since we copied the mId of the old instance and the mId is used - // as the primary key in the AlarmInstance table, this will replace - // the existing instance. - AlarmInstance.updateInstance(cr, newInstance); - // Update the notification for this instance. - AlarmNotifications.updateNotification(mAppContext, newInstance); - } - return null; - } - // Otherwise, this is a major update and we're going to re-create the alarm - AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); - - return alarm.enabled ? setupAlarmInstance(alarm) : null; - } - - @Override - protected void onPostExecute(AlarmInstance instance) { - if (popToast && instance != null) { - AlarmUtils.popAlarmSetSnackbar( - mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis()); - } - } - }; - updateTask.execute(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler handler = new Handler(Looper.getMainLooper()); + executor.execute(() -> { + ContentResolver cr = mAppContext.getContentResolver(); + + // Update alarm + Alarm.updateAlarm(cr, alarm); + + if (minorUpdate) { + // just update the instance in the database and update notifications. + final List<AlarmInstance> instanceList = + AlarmInstance.getInstancesByAlarmId(cr, alarm.id); + for (AlarmInstance instance : instanceList) { + // Make a copy of the existing instance + final AlarmInstance newInstance = new AlarmInstance(instance); + // Copy over minor change data to the instance; we don't know + // exactly which minor field changed, so just copy them all. + newInstance.mVibrate = alarm.vibrate; + newInstance.mRingtone = alarm.alert; + newInstance.mLabel = alarm.label; + // Since we copied the mId of the old instance and the mId is used + // as the primary key in the AlarmInstance table, this will replace + // the existing instance. + AlarmInstance.updateInstance(cr, newInstance); + // Update the notification for this instance. + AlarmNotifications.updateNotification(mAppContext, newInstance); + } + return; + } + // Otherwise, this is a major update and we're going to re-create the alarm + AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); + + final AlarmInstance finalInstance = alarm.enabled ? setupAlarmInstance(alarm) : null; + + handler.post(() -> { + if (popToast && finalInstance != null) { + AlarmUtils.popAlarmSetSnackbar( + mSnackbarAnchor, finalInstance.getAlarmTime().getTimeInMillis()); + } + }); + }); } /** @@ -154,27 +150,24 @@ public final class AlarmUpdateHandler { * @param alarm The alarm to be deleted. */ public void asyncDeleteAlarm(final Alarm alarm) { - final AsyncTask<Void, Void, Boolean> deleteTask = new AsyncTask<Void, Void, Boolean>() { - @Override - protected Boolean doInBackground(Void... parameters) { - // Activity may be closed at this point , make sure data is still valid - if (alarm == null) { - // Nothing to do here, just return. - return false; - } - AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); - return Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler handler = new Handler(Looper.getMainLooper()); + executor.execute(() -> { + // Activity may be closed at this point , make sure data is still valid + if (alarm == null) { + // Nothing to do here, just return. + return; } + AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); + final boolean deleted = Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id); - @Override - protected void onPostExecute(Boolean deleted) { + handler.post(() -> { if (deleted) { mDeletedAlarm = alarm; showUndoBar(); } - } - }; - deleteTask.execute(); + }); + }); } /** diff --git a/src/com/android/deskclock/data/SilentSettingsModel.java b/src/com/android/deskclock/data/SilentSettingsModel.java index 79b52f995..10169517d 100644 --- a/src/com/android/deskclock/data/SilentSettingsModel.java +++ b/src/com/android/deskclock/data/SilentSettingsModel.java @@ -35,7 +35,6 @@ import android.database.ContentObserver; import android.media.AudioManager; import android.media.RingtoneManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; @@ -45,6 +44,8 @@ import com.android.deskclock.data.DataModel.SilentSetting; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * This model fetches and stores reasons that alarms may be suppressed or silenced by system @@ -112,7 +113,7 @@ final class SilentSettingsModel { void updateSilentState() { // Cancel any task in flight, the result is no longer relevant. if (mCheckSilenceSettingsTask != null) { - mCheckSilenceSettingsTask.cancel(true); + mCheckSilenceSettingsTask.cancel(); mCheckSilenceSettingsTask = null; } @@ -143,35 +144,36 @@ final class SilentSettingsModel { * associated ringtone from playing. If any of them would prevent an alarm from firing or * making noise, a description of the setting is reported to this model on the main thread. */ - private final class CheckSilenceSettingsTask extends AsyncTask<Void, Void, SilentSetting> { - @Override - protected SilentSetting doInBackground(Void... parameters) { - if (!isCancelled() && isDoNotDisturbBlockingAlarms()) { - return SilentSetting.DO_NOT_DISTURB; - } else if (!isCancelled() && isAlarmStreamMuted()) { - return SilentSetting.MUTED_VOLUME; - } else if (!isCancelled() && isSystemAlarmRingtoneSilent()) { - return SilentSetting.SILENT_RINGTONE; - } else if (!isCancelled() && isAppNotificationBlocked()) { - return SilentSetting.BLOCKED_NOTIFICATIONS; - } - return null; - } - - @Override - protected void onCancelled() { - super.onCancelled(); - if (mCheckSilenceSettingsTask == this) { - mCheckSilenceSettingsTask = null; - } + private final class CheckSilenceSettingsTask { + ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + Handler mHandler = new Handler(Looper.getMainLooper()); + + private void execute() { + mExecutor.execute(() -> { + final SilentSetting silentSetting; + if (isDoNotDisturbBlockingAlarms()) { + silentSetting = SilentSetting.DO_NOT_DISTURB; + } else if (isAlarmStreamMuted()) { + silentSetting = SilentSetting.MUTED_VOLUME; + } else if (isSystemAlarmRingtoneSilent()) { + silentSetting = SilentSetting.SILENT_RINGTONE; + } else if (isAppNotificationBlocked()) { + silentSetting = SilentSetting.BLOCKED_NOTIFICATIONS; + } else { + silentSetting = null; + } + + mHandler.post(() -> { + if (mCheckSilenceSettingsTask == this) { + mCheckSilenceSettingsTask = null; + setSilentState(silentSetting); + } + }); + }); } - @Override - protected void onPostExecute(SilentSetting silentSetting) { - if (mCheckSilenceSettingsTask == this) { - mCheckSilenceSettingsTask = null; - setSilentState(silentSetting); - } + private void cancel() { + mExecutor.shutdownNow(); } private boolean isDoNotDisturbBlockingAlarms() { diff --git a/src/com/android/deskclock/ringtone/RingtoneLoader.java b/src/com/android/deskclock/ringtone/RingtoneLoader.java index 3f5d6d200..515eb915c 100644 --- a/src/com/android/deskclock/ringtone/RingtoneLoader.java +++ b/src/com/android/deskclock/ringtone/RingtoneLoader.java @@ -19,13 +19,14 @@ package com.android.deskclock.ringtone; import static android.media.AudioManager.STREAM_ALARM; import static com.android.deskclock.Utils.RINGTONE_SILENT; -import android.content.AsyncTaskLoader; import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.media.RingtoneManager; import android.net.Uri; +import androidx.loader.content.AsyncTaskLoader; + import com.android.deskclock.ItemAdapter; import com.android.deskclock.LogUtils; import com.android.deskclock.R; diff --git a/src/com/android/deskclock/ringtone/RingtonePickerActivity.java b/src/com/android/deskclock/ringtone/RingtonePickerActivity.java index ba18c85e0..8d2d9de83 100644 --- a/src/com/android/deskclock/ringtone/RingtonePickerActivity.java +++ b/src/com/android/deskclock/ringtone/RingtonePickerActivity.java @@ -28,18 +28,17 @@ import static com.android.deskclock.ringtone.RingtoneViewHolder.VIEW_TYPE_SYSTEM import android.app.Dialog; import android.app.DialogFragment; import android.app.FragmentManager; -import android.app.LoaderManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.Loader; import android.database.Cursor; import android.media.AudioManager; import android.media.RingtoneManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.provider.MediaStore; import android.view.LayoutInflater; import android.view.MenuItem; @@ -47,6 +46,8 @@ import android.view.MenuItem; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; +import androidx.loader.content.Loader; +import androidx.loader.app.LoaderManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -61,6 +62,8 @@ import com.android.deskclock.provider.Alarm; import com.android.deskclock.widget.CollapsingToolbarBaseActivity; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * This activity presents a set of ringtones from which the user may select one. The set includes: @@ -190,7 +193,8 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity final int titleResourceId = intent.getIntExtra(EXTRA_TITLE, 0); setTitle(context.getString(titleResourceId)); - getLoaderManager().initLoader(0 /* id */, null /* args */, this /* callback */); + LoaderManager.getInstance(this).initLoader(0 /* id */, null /* args */, + this /* callback */); registerForContextMenu(recyclerView); } @@ -203,26 +207,22 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity final ContentResolver cr = getContentResolver(); // Start a background task to fetch the alarm whose ringtone must be updated. - new AsyncTask<Void, Void, Alarm>() { - @Override - protected Alarm doInBackground(Void... parameters) { - final Alarm alarm = Alarm.getAlarm(cr, mAlarmId); - if (alarm != null) { - alarm.alert = mSelectedRingtoneUri; - } - return alarm; - } - - @Override - protected void onPostExecute(Alarm alarm) { - // Update the default ringtone for future new alarms. - DataModel.getDataModel().setDefaultAlarmRingtoneUri(alarm.alert); - - // Start a second background task to persist the updated alarm. - new AlarmUpdateHandler(context, null, null) - .asyncUpdateAlarm(alarm, false, true); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler handler = new Handler(Looper.getMainLooper()); + executor.execute(() -> { + final Alarm alarm = Alarm.getAlarm(cr, mAlarmId); + if (alarm != null) { + alarm.alert = mSelectedRingtoneUri; + + handler.post(() -> { + DataModel.getDataModel().setDefaultAlarmRingtoneUri(alarm.alert); + + // Start a second background task to persist the updated alarm. + new AlarmUpdateHandler(context, null, null) + .asyncUpdateAlarm(alarm, false, true); + }); } - }.execute(); + }); } else { DataModel.getDataModel().setTimerRingtoneUri(mSelectedRingtoneUri); } @@ -255,8 +255,8 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity } @Override - public void onLoadFinished(Loader<List<ItemAdapter.ItemHolder<Uri>>> loader, - List<ItemAdapter.ItemHolder<Uri>> itemHolders) { + public void onLoadFinished(@NonNull Loader<List<ItemAdapter.ItemHolder<Uri>>> loader, + List<ItemAdapter.ItemHolder<Uri>> itemHolders) { // Update the adapter with fresh data. mRingtoneAdapter.setItems(itemHolders); @@ -301,7 +301,7 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity } // Start a task to fetch the display name of the audio content and add the custom ringtone. - new AddCustomRingtoneTask(uri).execute(); + addCustomRingtoneAsync(uri); } @Override @@ -377,15 +377,6 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity } /** - * Proceeds with removing the custom ringtone with the given uri. - * - * @param toRemove identifies the custom ringtone to be removed - */ - private void removeCustomRingtone(Uri toRemove) { - new RemoveCustomRingtoneTask(toRemove).execute(); - } - - /** * This DialogFragment informs the user of the side-effects of removing a custom ringtone while * it is in use by alarms and/or timers and prompts them to confirm the removal. */ @@ -415,7 +406,7 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity final Uri toRemove = arguments.getParcelable(ARG_RINGTONE_URI_TO_REMOVE); final DialogInterface.OnClickListener okListener = (dialog, which) -> - ((RingtonePickerActivity) getActivity()).removeCustomRingtone(toRemove); + ((RingtonePickerActivity) getActivity()).removeCustomRingtoneAsync(toRemove); if (arguments.getBoolean(ARG_RINGTONE_HAS_PERMISSIONS)) { return new AlertDialog.Builder(getActivity()) @@ -482,64 +473,61 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity * This task locates a displayable string in the background that is fit for use as the title of * the audio content. It adds a custom ringtone using the uri and title on the main thread. */ - private final class AddCustomRingtoneTask extends AsyncTask<Void, Void, String> { + private void addCustomRingtoneAsync(Uri uri) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler handler = new Handler(Looper.getMainLooper()); - private final Uri mUri; - private final Context mContext; - - private AddCustomRingtoneTask(Uri uri) { - mUri = uri; - mContext = getApplicationContext(); - } - - @Override - protected String doInBackground(Void... voids) { - final ContentResolver contentResolver = mContext.getContentResolver(); + executor.execute(() -> { + final Context context = getApplicationContext(); + final ContentResolver contentResolver = context.getContentResolver(); + String name = null; // Take the long-term permission to read (playback) the audio at the uri. - contentResolver.takePersistableUriPermission(mUri, FLAG_GRANT_READ_URI_PERMISSION); + contentResolver.takePersistableUriPermission(uri, FLAG_GRANT_READ_URI_PERMISSION); - try (Cursor cursor = contentResolver.query(mUri, null, null, null, null)) { + try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { // If the file was a media file, return its title. final int titleIndex = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE); if (titleIndex != -1) { - return cursor.getString(titleIndex); - } - - // If the file was a simple openable, return its display name. - final int displayNameIndex = cursor.getColumnIndex(DISPLAY_NAME); - if (displayNameIndex != -1) { - String title = cursor.getString(displayNameIndex); - final int dotIndex = title.lastIndexOf("."); - if (dotIndex > 0) { - title = title.substring(0, dotIndex); + name = cursor.getString(titleIndex); + } else { + // If the file was a simple openable, return its display name. + final int displayNameIndex = cursor.getColumnIndex(DISPLAY_NAME); + if (displayNameIndex != -1) { + String displayName = cursor.getString(displayNameIndex); + final int dotIndex = displayName.lastIndexOf("."); + if (dotIndex > 0) { + displayName = displayName.substring(0, dotIndex); + } + name = displayName; } - return title; } } else { - LogUtils.e("No ringtone for uri: %s", mUri); + LogUtils.e("No ringtone for uri: %s", uri); } } catch (Exception e) { - LogUtils.e("Unable to locate title for custom ringtone: " + mUri, e); + LogUtils.e("Unable to locate title for custom ringtone: " + uri, e); } - return mContext.getString(R.string.unknown_ringtone_title); - } + if (name == null) { + name = context.getString(R.string.unknown_ringtone_title); + } - @Override - protected void onPostExecute(String title) { - // Add the new custom ringtone to the data model. - DataModel.getDataModel().addCustomRingtone(mUri, title); + final String title = name; + handler.post(() -> { + // Add the new custom ringtone to the data model. + DataModel.getDataModel().addCustomRingtone(uri, title); - // When the loader completes, it must play the new ringtone. - mSelectedRingtoneUri = mUri; - mIsPlaying = true; + // When the loader completes, it must play the new ringtone. + mSelectedRingtoneUri = uri; + mIsPlaying = true; - // Reload the data to reflect the change in the UI. - getLoaderManager().restartLoader(0 /* id */, null /* args */, - RingtonePickerActivity.this /* callback */); - } + // Reload the data to reflect the change in the UI. + LoaderManager.getInstance(this).restartLoader(0 /* id */, null /* args */, + RingtonePickerActivity.this /* callback */); + }); + }); } /** @@ -549,25 +537,18 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity * Android system default alarm ringtone. If the application's timer ringtone is being removed, * it is reset to the application's default timer ringtone. */ - private final class RemoveCustomRingtoneTask extends AsyncTask<Void, Void, Void> { - - private final Uri mRemoveUri; - private Uri mSystemDefaultRingtoneUri; - - private RemoveCustomRingtoneTask(Uri removeUri) { - mRemoveUri = removeUri; - } - - @Override - protected Void doInBackground(Void... voids) { - mSystemDefaultRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); - + private void removeCustomRingtoneAsync(Uri removeUri) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler handler = new Handler(Looper.getMainLooper()); + executor.execute(() -> { + final Uri systemDefaultRingtoneUri = + RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); // Update all alarms that use the custom ringtone to use the system default. final ContentResolver cr = getContentResolver(); final List<Alarm> alarms = Alarm.getAlarms(cr, null); for (Alarm alarm : alarms) { - if (mRemoveUri.equals(alarm.alert)) { - alarm.alert = mSystemDefaultRingtoneUri; + if (removeUri.equals(alarm.alert)) { + alarm.alert = systemDefaultRingtoneUri; // Start a second background task to persist the updated alarm. new AlarmUpdateHandler(RingtonePickerActivity.this, null, null) .asyncUpdateAlarm(alarm, false, true); @@ -576,51 +557,49 @@ public class RingtonePickerActivity extends CollapsingToolbarBaseActivity try { // Release the permission to read (playback) the audio at the uri. - cr.releasePersistableUriPermission(mRemoveUri, FLAG_GRANT_READ_URI_PERMISSION); + cr.releasePersistableUriPermission(removeUri, FLAG_GRANT_READ_URI_PERMISSION); } catch (SecurityException ignore) { // If the file was already deleted from the file system, a SecurityException is // thrown indicating this app did not hold the read permission being released. - LogUtils.w("SecurityException while releasing read permission for " + mRemoveUri); + LogUtils.w("SecurityException while releasing read permission for " + removeUri); } - return null; - } - - @Override - protected void onPostExecute(Void v) { - // Reset the default alarm ringtone if it was just removed. - if (mRemoveUri.equals(DataModel.getDataModel().getDefaultAlarmRingtoneUri())) { - DataModel.getDataModel().setDefaultAlarmRingtoneUri(mSystemDefaultRingtoneUri); - } + handler.post(() -> { + // Reset the default alarm ringtone if it was just removed. + if (removeUri.equals(DataModel.getDataModel().getDefaultAlarmRingtoneUri())) { + DataModel.getDataModel().setDefaultAlarmRingtoneUri(systemDefaultRingtoneUri); + } - // Reset the timer ringtone if it was just removed. - if (mRemoveUri.equals(DataModel.getDataModel().getTimerRingtoneUri())) { - final Uri timerRingtoneUri = DataModel.getDataModel().getDefaultTimerRingtoneUri(); - DataModel.getDataModel().setTimerRingtoneUri(timerRingtoneUri); - } + // Reset the timer ringtone if it was just removed. + if (removeUri.equals(DataModel.getDataModel().getTimerRingtoneUri())) { + final Uri timerRingtoneUri = DataModel.getDataModel() + .getDefaultTimerRingtoneUri(); + DataModel.getDataModel().setTimerRingtoneUri(timerRingtoneUri); + } - // Remove the corresponding custom ringtone. - DataModel.getDataModel().removeCustomRingtone(mRemoveUri); + // Remove the corresponding custom ringtone. + DataModel.getDataModel().removeCustomRingtone(removeUri); - // Find the ringtone to be removed from the adapter. - final RingtoneHolder toRemove = getRingtoneHolder(mRemoveUri); - if (toRemove == null) { - return; - } + // Find the ringtone to be removed from the adapter. + final RingtoneHolder toRemove = getRingtoneHolder(removeUri); + if (toRemove == null) { + return; + } - // If the ringtone to remove is also the selected ringtone, adjust the selection. - if (toRemove.isSelected()) { - stopPlayingRingtone(toRemove, false); - final RingtoneHolder defaultRingtone = getRingtoneHolder(mDefaultRingtoneUri); - if (defaultRingtone != null) { - defaultRingtone.setSelected(true); - mSelectedRingtoneUri = defaultRingtone.getUri(); - defaultRingtone.notifyItemChanged(); + // If the ringtone to remove is also the selected ringtone, adjust the selection. + if (toRemove.isSelected()) { + stopPlayingRingtone(toRemove, false); + final RingtoneHolder defaultRingtone = getRingtoneHolder(mDefaultRingtoneUri); + if (defaultRingtone != null) { + defaultRingtone.setSelected(true); + mSelectedRingtoneUri = defaultRingtone.getUri(); + defaultRingtone.notifyItemChanged(); + } } - } - // Remove the ringtone from the adapter. - mRingtoneAdapter.removeItem(toRemove); - } + // Remove the ringtone from the adapter. + mRingtoneAdapter.removeItem(toRemove); + }); + }); } } |