Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aba22b8d authored by Wilhelm Fitzpatrick's avatar Wilhelm Fitzpatrick Committed by Gerrit Code Review
Browse files

DeskClock: add increasing volume option for alarm clocks

parent 421038c8
Loading
Loading
Loading
Loading
+18 −11
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@
            <CheckBox
                android:id="@+id/repeat_onoff"
                android:layout_width="wrap_content"
                android:layout_height="48dip"
                android:layout_height="@dimen/touch_target_min_size"
                android:layout_gravity="center_vertical|start"
                android:text="@string/alarm_repeat"
                android:textSize="16sp"
@@ -84,7 +84,7 @@
            <LinearLayout
                android:id="@+id/repeat_days"
                android:layout_width="match_parent"
                android:layout_height="48dip"
                android:layout_height="@dimen/touch_target_min_size"
                android:layout_gravity="top"
                android:orientation="horizontal"
                android:visibility="gone">
@@ -95,18 +95,16 @@
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:orientation="horizontal" >
                android:orientation="vertical" >

                <TextView
                    android:id="@+id/choose_ringtone"
                    android:layout_width="0dip"
                    android:layout_height="48dip"
                    android:layout_weight="1"
                    android:layout_alignParentStart="true"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/touch_target_min_size"
                    android:background="?android:attr/selectableItemBackground"
                    android:clickable="true"
                    android:paddingStart="4dip"
                    android:drawablePadding="16dp"
                    android:drawablePadding="20dp"
                    android:drawableStart="@drawable/ic_ringtone"
                    android:ellipsize="marquee"
                    android:gravity="center_vertical"
@@ -118,12 +116,21 @@
                    android:textColor="@color/clock_white"
                    />

                <CheckBox
                    android:id="@+id/increasing_volume_onoff"
                    android:layout_width="wrap_content"
                    android:layout_height="@dimen/touch_target_min_size"
                    android:includeFontPadding="false"
                    android:text="@string/alarm_increasing_volume"
                    android:paddingStart="16dip"
                    android:textSize="16sp"
                    android:textColor="@color/white"
                    style="@style/body" />

                <CheckBox
                    android:id="@+id/vibrate_onoff"
                    android:layout_width="wrap_content"
                    android:layout_height="48dip"
                    android:layout_alignParentEnd="true"
                    android:layout_centerVertical="true"
                    android:layout_height="@dimen/touch_target_min_size"
                    android:includeFontPadding="false"
                    android:text="@string/alarm_vibrate"
                    android:paddingStart="16dip"
+3 −0
Original line number Diff line number Diff line
@@ -20,4 +20,7 @@

    <!-- Setting summary for showing/hiding the alarm statusbar icon -->
    <string name="show_status_bar_icon_summary">Show an icon in the status bar when an alarm is set</string>

    <!-- Setting labels on Set alarm screen: Increasing volume on or off -->
    <string name="alarm_increasing_volume">Increasing volume</string>
</resources>
+13 −0
Original line number Diff line number Diff line
@@ -573,6 +573,7 @@ public abstract class AlarmClockFragment extends DeskClockFragment implements
            LinearLayout repeatDays;
            CompoundButton[] dayButtons = new CompoundButton[7];
            CheckBox vibrate;
            CheckBox increasingVolume;
            TextView ringtone;
            View hairLine;
            View arrow;
@@ -713,6 +714,7 @@ public abstract class AlarmClockFragment extends DeskClockFragment implements
                holder.dayButtons[i] = dayButton;
            }
            holder.vibrate = (CheckBox) view.findViewById(R.id.vibrate_onoff);
            holder.increasingVolume = (CheckBox) view.findViewById(R.id.increasing_volume_onoff);
            holder.ringtone = (TextView) view.findViewById(R.id.choose_ringtone);

            view.setTag(holder);
@@ -1052,6 +1054,17 @@ public abstract class AlarmClockFragment extends DeskClockFragment implements
                    launchRingTonePicker(alarm);
                }
            });

            itemHolder.increasingVolume.setVisibility(View.VISIBLE);
            itemHolder.increasingVolume.setChecked(alarm.increasingVolume);
            itemHolder.increasingVolume.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    final boolean checked = ((CheckBox) v).isChecked();
                    alarm.increasingVolume = checked;
                    asyncUpdateAlarm(alarm, false);
                }
            });
        }

        // Sets the alpha of the digital time display. This gives a visual effect
+104 −18
Original line number Diff line number Diff line
@@ -46,9 +46,15 @@ public class AsyncRingtonePlayer {
    // Volume suggested by media team for in-call alarms.
    private static final float IN_CALL_VOLUME = 0.125f;

    // Constants for increasing volume alarms
    private static final long INCREASING_VOLUME_DELAY = 4000;
    private static final float INCREASING_VOLUME_START = 0.05f;
    private static final float INCREASING_VOLUME_DELTA = 0.05f;

    // Message codes used with the ringtone thread.
    private static final int EVENT_PLAY = 1;
    private static final int EVENT_STOP = 2;
    private static final int EVENT_INCREASE_VOLUME = 3;
    private static final String RINGTONE_URI_KEY = "RINGTONE_URI_KEY";

    /** Handler running on the ringtone thread. */
@@ -65,15 +71,15 @@ public class AsyncRingtonePlayer {
    }

    /** Plays the ringtone. */
    public void play(Uri ringtoneUri) {
    public void play(Uri ringtoneUri, boolean increasingVolume) {
        LogUtils.d(TAG, "Posting play.");
        postMessage(EVENT_PLAY, ringtoneUri);
        postMessage(EVENT_PLAY, ringtoneUri, increasingVolume);
    }

    /** Stops playing the ringtone. */
    public void stop() {
        LogUtils.d(TAG, "Posting stop.");
        postMessage(EVENT_STOP, null);
        postMessage(EVENT_STOP, null, false);
    }

    /**
@@ -81,7 +87,7 @@ public class AsyncRingtonePlayer {
     *
     * @param messageCode The message to post.
     */
    private void postMessage(int messageCode, Uri ringtoneUri) {
    private void postMessage(int messageCode, Uri ringtoneUri, boolean increasingVolume) {
        synchronized (this) {
            if (mHandler == null) {
                mHandler = getNewHandler();
@@ -93,10 +99,17 @@ public class AsyncRingtonePlayer {
                bundle.putParcelable(RINGTONE_URI_KEY, ringtoneUri);
                message.setData(bundle);
            }
            message.arg1 = increasingVolume ? 1 : 0;
            message.sendToTarget();
        }
    }

    private void delayedMessage(int messageCode, long delay) {
        synchronized (this) {
            mHandler.sendEmptyMessageDelayed(messageCode, delay);
        }
    }

    /**
     * Creates a new ringtone Handler running in its own thread.
     */
@@ -110,18 +123,29 @@ public class AsyncRingtonePlayer {
                switch (msg.what) {
                    case EVENT_PLAY:
                        final Uri ringtoneUri = msg.getData().getParcelable(RINGTONE_URI_KEY);
                        getPlaybackDelegate().play(mContext, ringtoneUri);
                        final boolean increasingVolume = msg.arg1 == 1;
                        getPlaybackDelegate().play(mContext, ringtoneUri, increasingVolume);
                        if (increasingVolume) {
                            delayedMessage(EVENT_INCREASE_VOLUME, INCREASING_VOLUME_DELAY);
                        }
                        break;
                    case EVENT_STOP:
                        removeMessages(EVENT_INCREASE_VOLUME);
                        getPlaybackDelegate().stop(mContext);
                        break;
                    case EVENT_INCREASE_VOLUME:
                        PlaybackDelegate pd = getPlaybackDelegate();
                        if (pd.isPlaying() && pd.increaseVolume()) {
                            delayedMessage(EVENT_INCREASE_VOLUME, INCREASING_VOLUME_DELAY);
                        }
                        break;
                }
            }
        };
    }

    /**
     * @return <code>true</code> iff the device is currently in a telephone call
     * @return <code>true</code> if the device is currently in a telephone call
     */
    private static boolean isInTelephoneCall(Context context) {
        final TelephonyManager tm = (TelephonyManager)
@@ -169,8 +193,10 @@ public class AsyncRingtonePlayer {
     * vs {@link MediaPlayer}.
     */
    private interface PlaybackDelegate {
        void play(Context context, Uri ringtoneUri);
        void play(Context context, Uri ringtoneUri, boolean increasingVolume);
        void stop(Context context);
        boolean isPlaying();
        boolean increaseVolume();
    }

    /**
@@ -184,11 +210,14 @@ public class AsyncRingtonePlayer {
        /** Non-{@code null} while playing a ringtone; {@code null} otherwise. */
        private MediaPlayer mMediaPlayer;

        private float mMaxVolume;
        private float mCurrentVolume;

        /**
         * Starts the actual playback of the ringtone. Executes on ringtone-thread.
         */
        @Override
        public void play(final Context context, Uri ringtoneUri) {
        public void play(final Context context, Uri ringtoneUri, boolean increasingVolume) {
            if (Looper.getMainLooper() == Looper.myLooper()) {
                LogUtils.e(TAG, "Must not be on the main thread!", new IllegalStateException());
            }
@@ -217,11 +246,12 @@ public class AsyncRingtonePlayer {
            });

            try {
                mMaxVolume = 1f;
                // Check if we are in a call. If we are, use the in-call alarm resource at a
                // low volume to not disrupt the call.
                if (isInTelephoneCall(context)) {
                    LogUtils.v("Using the in-call alarm");
                    mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
                    mMaxVolume = IN_CALL_VOLUME;
                    alarmNoise = getInCallRingtoneUri(context);
                }

@@ -230,7 +260,7 @@ public class AsyncRingtonePlayer {
                // installation time. M+, this permission can be revoked by the user any time.
                mMediaPlayer.setDataSource(context, alarmNoise);

                startAlarm(mMediaPlayer);
                startAlarm(mMediaPlayer, increasingVolume);
            } catch (Throwable t) {
                LogUtils.e("Use the fallback ringtone, original was " + alarmNoise, t);
                // The alarmNoise may be on the sd card which could be busy right now.
@@ -239,7 +269,7 @@ public class AsyncRingtonePlayer {
                    // Must reset the media player to clear the error state.
                    mMediaPlayer.reset();
                    mMediaPlayer.setDataSource(context, getFallbackRingtoneUri(context));
                    startAlarm(mMediaPlayer);
                    startAlarm(mMediaPlayer, increasingVolume);
                } catch (Throwable t2) {
                    // At this point we just don't play anything.
                    LogUtils.e("Failed to play fallback ringtone", t2);
@@ -250,7 +280,7 @@ public class AsyncRingtonePlayer {
        /**
         * Do the common stuff when starting the alarm.
         */
        private void startAlarm(MediaPlayer player) throws IOException {
        private void startAlarm(MediaPlayer player, boolean increasingVolume) throws IOException {
            // do not play alarms if stream volume is 0 (typically because ringer mode is silent).
            if (mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
                if (Utils.isLOrLater()) {
@@ -260,6 +290,8 @@ public class AsyncRingtonePlayer {
                            .build());
                }

                mCurrentVolume = increasingVolume ? INCREASING_VOLUME_START : mMaxVolume;
                player.setVolume(mCurrentVolume, mCurrentVolume);
                player.setAudioStreamType(AudioManager.STREAM_ALARM);
                player.setLooping(true);
                player.prepare();
@@ -288,6 +320,27 @@ public class AsyncRingtonePlayer {
                mMediaPlayer = null;
            }
        }

        @Override
        public boolean isPlaying() {
            return mMediaPlayer != null && mMediaPlayer.isPlaying();
        }

        /**
         * @return <code>true</code> if volume should continue to be increased
         */
        @Override
        public boolean increaseVolume() {
            if (mMediaPlayer != null) {
                mCurrentVolume += INCREASING_VOLUME_DELTA;
                if (mCurrentVolume > mMaxVolume) {
                    mCurrentVolume = mMaxVolume;
                }
                mMediaPlayer.setVolume(mCurrentVolume, mCurrentVolume);
                return mCurrentVolume < mMaxVolume;
            }
            return false;
        }
    }

    /**
@@ -307,6 +360,9 @@ public class AsyncRingtonePlayer {
        /** The method to adjust playback looping; cannot be null. */
        private Method mSetLoopingMethod;

        private float mMaxVolume;
        private float mCurrentVolume;

        private RingtonePlaybackDelegate() {
            try {
                mSetVolumeMethod = Ringtone.class.getDeclaredMethod("setVolume", float.class);
@@ -325,7 +381,7 @@ public class AsyncRingtonePlayer {
         * Starts the actual playback of the ringtone. Executes on ringtone-thread.
         */
        @Override
        public void play(Context context, Uri ringtoneUri) {
        public void play(Context context, Uri ringtoneUri, boolean increasingVolume) {
            if (Looper.getMainLooper() == Looper.myLooper()) {
                LogUtils.e(TAG, "Must not be on the main thread!", new IllegalStateException());
            }
@@ -374,21 +430,29 @@ public class AsyncRingtonePlayer {
                        .build());
            }

            mMaxVolume = 1f;
            // Attempt to adjust the ringtone volume if the user is in a telephone call.
            if (inTelephoneCall) {
                LogUtils.v("Using the in-call alarm");
                try {
                    mSetVolumeMethod.invoke(mRingtone, IN_CALL_VOLUME);
                } catch (Exception e) {
                    LogUtils.e(TAG, "Unable to set in-call volume for android.media.Ringtone", e);
                }
                mMaxVolume = IN_CALL_VOLUME;
            }

            mCurrentVolume = increasingVolume ? INCREASING_VOLUME_START : mMaxVolume;

            mAudioManager.requestAudioFocus(null, AudioManager.STREAM_ALARM,
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            setVolume(mCurrentVolume);
            mRingtone.play();
        }

        private void setVolume(float volume) {
            try {
                mSetVolumeMethod.invoke(mRingtone, volume);
            } catch (Exception e) {
                LogUtils.e(TAG, "Unable to set in-call volume for android.media.Ringtone", e);
            }
        }

        /**
         * Stops the playback of the ringtone. Executes on the ringtone-thread.
         */
@@ -403,12 +467,34 @@ public class AsyncRingtonePlayer {
            if (mRingtone != null && mRingtone.isPlaying()) {
                LogUtils.d(TAG, "Ringtone.stop() invoked.");
                mRingtone.stop();
                mRingtone = null;
            }

            if (mAudioManager != null) {
                mAudioManager.abandonAudioFocus(null);
            }
        }

        @Override
        public boolean isPlaying() {
            return mRingtone != null && mRingtone.isPlaying();
        }

        /**
         * @return <code>true</code> if volume should continue to be increased
         */
        @Override
        public boolean increaseVolume() {
            if (mRingtone != null && mSetVolumeMethod != null) {
                mCurrentVolume += INCREASING_VOLUME_DELTA;
                if (mCurrentVolume > mMaxVolume) {
                    mCurrentVolume = mMaxVolume;
                }
                setVolume(mCurrentVolume);
                return mCurrentVolume < mMaxVolume;
            }
            return false;
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ public final class AlarmKlaxon {
        stop(context);

        if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) {
            getAsyncRingtonePlayer(context).play(instance.mRingtone);
            getAsyncRingtonePlayer(context).play(instance.mRingtone, instance.mIncreasingVolume);
        }

        if (instance.mVibrate) {