1/*
2 * Copyright (C) 2007 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
17package com.android.settings;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.TypedArray;
22import android.media.AudioAttributes;
23import android.media.RingtoneManager;
24import android.net.Uri;
25import android.provider.Settings.System;
26import android.support.v7.preference.Preference;
27import android.support.v7.preference.PreferenceManager;
28import android.text.TextUtils;
29import android.util.AttributeSet;
30
31/**
32 * A {@link Preference} that allows the user to choose a ringtone from those on the device.
33 * The chosen ringtone's URI will be persisted as a string.
34 * <p>
35 * If the user chooses the "Default" item, the saved string will be one of
36 * {@link System#DEFAULT_RINGTONE_URI},
37 * {@link System#DEFAULT_NOTIFICATION_URI}, or
38 * {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
39 * item, the saved string will be an empty string.
40 *
41 * @attr ref android.R.styleable#RingtonePreference_ringtoneType
42 * @attr ref android.R.styleable#RingtonePreference_showDefault
43 * @attr ref android.R.styleable#RingtonePreference_showSilent
44 *
45 * Based of frameworks/base/core/java/android/preference/RingtonePreference.java
46 * but extends android.support.v7.preference.Preference instead.
47 */
48public class RingtonePreference extends Preference {
49
50    private static final String TAG = "RingtonePreference";
51
52    private int mRingtoneType;
53    private boolean mShowDefault;
54    private boolean mShowSilent;
55
56    private int mRequestCode;
57
58    public RingtonePreference(Context context, AttributeSet attrs) {
59        super(context, attrs);
60
61        final TypedArray a = context.obtainStyledAttributes(attrs,
62                com.android.internal.R.styleable.RingtonePreference, 0, 0);
63        mRingtoneType = a.getInt(com.android.internal.R.styleable.RingtonePreference_ringtoneType,
64                RingtoneManager.TYPE_RINGTONE);
65        mShowDefault = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showDefault,
66                true);
67        mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent,
68                true);
69        setIntent(new Intent(RingtoneManager.ACTION_RINGTONE_PICKER));
70        a.recycle();
71    }
72
73    /**
74     * Returns the sound type(s) that are shown in the picker.
75     *
76     * @return The sound type(s) that are shown in the picker.
77     * @see #setRingtoneType(int)
78     */
79    public int getRingtoneType() {
80        return mRingtoneType;
81    }
82
83    /**
84     * Sets the sound type(s) that are shown in the picker.
85     *
86     * @param type The sound type(s) that are shown in the picker.
87     * @see RingtoneManager#EXTRA_RINGTONE_TYPE
88     */
89    public void setRingtoneType(int type) {
90        mRingtoneType = type;
91    }
92
93    /**
94     * Returns whether to a show an item for the default sound/ringtone.
95     *
96     * @return Whether to show an item for the default sound/ringtone.
97     */
98    public boolean getShowDefault() {
99        return mShowDefault;
100    }
101
102    /**
103     * Sets whether to show an item for the default sound/ringtone. The default
104     * to use will be deduced from the sound type(s) being shown.
105     *
106     * @param showDefault Whether to show the default or not.
107     * @see RingtoneManager#EXTRA_RINGTONE_SHOW_DEFAULT
108     */
109    public void setShowDefault(boolean showDefault) {
110        mShowDefault = showDefault;
111    }
112
113    /**
114     * Returns whether to a show an item for 'Silent'.
115     *
116     * @return Whether to show an item for 'Silent'.
117     */
118    public boolean getShowSilent() {
119        return mShowSilent;
120    }
121
122    /**
123     * Sets whether to show an item for 'Silent'.
124     *
125     * @param showSilent Whether to show 'Silent'.
126     * @see RingtoneManager#EXTRA_RINGTONE_SHOW_SILENT
127     */
128    public void setShowSilent(boolean showSilent) {
129        mShowSilent = showSilent;
130    }
131
132    public int getRequestCode() {
133        return mRequestCode;
134    }
135
136    /**
137     * Prepares the intent to launch the ringtone picker. This can be modified
138     * to adjust the parameters of the ringtone picker.
139     *
140     * @param ringtonePickerIntent The ringtone picker intent that can be
141     *            modified by putting extras.
142     */
143    public void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
144
145        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
146                onRestoreRingtone());
147
148        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault);
149        if (mShowDefault) {
150            ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
151                    RingtoneManager.getDefaultUri(getRingtoneType()));
152        }
153
154        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent);
155        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, mRingtoneType);
156        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getTitle());
157        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
158                AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
159    }
160
161    /**
162     * Called when a ringtone is chosen.
163     * <p>
164     * By default, this saves the ringtone URI to the persistent storage as a
165     * string.
166     *
167     * @param ringtoneUri The chosen ringtone's {@link Uri}. Can be null.
168     */
169    protected void onSaveRingtone(Uri ringtoneUri) {
170        persistString(ringtoneUri != null ? ringtoneUri.toString() : "");
171    }
172
173    /**
174     * Called when the chooser is about to be shown and the current ringtone
175     * should be marked. Can return null to not mark any ringtone.
176     * <p>
177     * By default, this restores the previous ringtone URI from the persistent
178     * storage.
179     *
180     * @return The ringtone to be marked as the current ringtone.
181     */
182    protected Uri onRestoreRingtone() {
183        final String uriString = getPersistedString(null);
184        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
185    }
186
187    @Override
188    protected Object onGetDefaultValue(TypedArray a, int index) {
189        return a.getString(index);
190    }
191
192    @Override
193    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValueObj) {
194        String defaultValue = (String) defaultValueObj;
195
196        /*
197         * This method is normally to make sure the internal state and UI
198         * matches either the persisted value or the default value. Since we
199         * don't show the current value in the UI (until the dialog is opened)
200         * and we don't keep local state, if we are restoring the persisted
201         * value we don't need to do anything.
202         */
203        if (restorePersistedValue) {
204            return;
205        }
206
207        // If we are setting to the default value, we should persist it.
208        if (!TextUtils.isEmpty(defaultValue)) {
209            onSaveRingtone(Uri.parse(defaultValue));
210        }
211    }
212    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
213        super.onAttachedToHierarchy(preferenceManager);
214    }
215
216    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
217        if (data != null) {
218            Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
219
220            if (callChangeListener(uri != null ? uri.toString() : "")) {
221                onSaveRingtone(uri);
222            }
223        }
224
225        return true;
226    }
227
228}
229