1/*
2 * Copyright (C) 2014 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 android.telecom;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.media.ToneGenerator;
22import android.text.TextUtils;
23
24import java.util.Objects;
25
26/**
27 * Describes the cause of a disconnected call. This always includes a code describing the generic
28 * cause of the disconnect. Optionally, it may include a label and/or description to display to the
29 * user. It is the responsibility of the {@link ConnectionService} to provide localized versions of
30 * the label and description. It also may contain a reason for the disconnect, which is intended for
31 * logging and not for display to the user.
32 */
33public final class DisconnectCause implements Parcelable {
34
35    /** Disconnected because of an unknown or unspecified reason. */
36    public static final int UNKNOWN = 0;
37    /** Disconnected because there was an error, such as a problem with the network. */
38    public static final int ERROR = 1;
39    /** Disconnected because of a local user-initiated action, such as hanging up. */
40    public static final int LOCAL = 2;
41    /**
42     * Disconnected because of a remote user-initiated action, such as the other party hanging up
43     * up.
44     */
45    public static final int REMOTE = 3;
46    /** Disconnected because it has been canceled. */
47    public static final int CANCELED = 4;
48    /** Disconnected because there was no response to an incoming call. */
49    public static final int MISSED = 5;
50    /** Disconnected because the user rejected an incoming call. */
51    public static final int REJECTED = 6;
52    /** Disconnected because the other party was busy. */
53    public static final int BUSY = 7;
54    /**
55     * Disconnected because of a restriction on placing the call, such as dialing in airplane
56     * mode.
57     */
58    public static final int RESTRICTED = 8;
59    /** Disconnected for reason not described by other disconnect codes. */
60    public static final int OTHER = 9;
61    /**
62     * Disconnected because the connection manager did not support the call. The call will be tried
63     * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
64     */
65    public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
66
67    /**
68     * Disconnected because the user did not locally answer the incoming call, but it was answered
69     * on another device where the call was ringing.
70     * @hide
71     */
72    public static final int ANSWERED_ELSEWHERE = 11;
73
74    /**
75     * Disconnected because the call was pulled from the current device to another device.
76     * @hide
77     */
78    public static final int CALL_PULLED = 12;
79
80    private int mDisconnectCode;
81    private CharSequence mDisconnectLabel;
82    private CharSequence mDisconnectDescription;
83    private String mDisconnectReason;
84    private int mToneToPlay;
85
86    /**
87     * Creates a new DisconnectCause.
88     *
89     * @param code The code for the disconnect cause.
90     */
91    public DisconnectCause(int code) {
92        this(code, null, null, null, ToneGenerator.TONE_UNKNOWN);
93    }
94
95    /**
96     * Creates a new DisconnectCause.
97     *
98     * @param code The code for the disconnect cause.
99     * @param reason The reason for the disconnect.
100     */
101    public DisconnectCause(int code, String reason) {
102        this(code, null, null, reason, ToneGenerator.TONE_UNKNOWN);
103    }
104
105    /**
106     * Creates a new DisconnectCause.
107     *
108     * @param code The code for the disconnect cause.
109     * @param label The localized label to show to the user to explain the disconnect.
110     * @param description The localized description to show to the user to explain the disconnect.
111     * @param reason The reason for the disconnect.
112     */
113    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason) {
114        this(code, label, description, reason, ToneGenerator.TONE_UNKNOWN);
115    }
116
117    /**
118     * Creates a new DisconnectCause.
119     *
120     * @param code The code for the disconnect cause.
121     * @param label The localized label to show to the user to explain the disconnect.
122     * @param description The localized description to show to the user to explain the disconnect.
123     * @param reason The reason for the disconnect.
124     * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
125     */
126    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
127            int toneToPlay) {
128        mDisconnectCode = code;
129        mDisconnectLabel = label;
130        mDisconnectDescription = description;
131        mDisconnectReason = reason;
132        mToneToPlay = toneToPlay;
133    }
134
135    /**
136     * Returns the code for the reason for this disconnect.
137     *
138     * @return The disconnect code.
139     */
140    public int getCode() {
141        return mDisconnectCode;
142    }
143
144    /**
145     * Returns a short label which explains the reason for the disconnect cause and is for display
146     * in the user interface. If not null, it is expected that the In-Call UI should display this
147     * text where it would normally display the call state ("Dialing", "Disconnected") and is
148     * therefore expected to be relatively small. The {@link ConnectionService } is responsible for
149     * providing and localizing this label. If there is no string provided, returns null.
150     *
151     * @return The disconnect label.
152     */
153    public CharSequence getLabel() {
154        return mDisconnectLabel;
155    }
156
157    /**
158     * Returns a description which explains the reason for the disconnect cause and is for display
159     * in the user interface. This optional text is generally a longer and more descriptive version
160     * of {@link #getLabel}, however it can exist even if {@link #getLabel} is empty. The In-Call UI
161     * should display this relatively prominently; the traditional implementation displays this as
162     * an alert dialog. The {@link ConnectionService} is responsible for providing and localizing
163     * this message. If there is no string provided, returns null.
164     *
165     * @return The disconnect description.
166     */
167    public CharSequence getDescription() {
168        return mDisconnectDescription;
169    }
170
171    /**
172     * Returns an explanation of the reason for the disconnect. This is not intended for display to
173     * the user and is used mainly for logging.
174     *
175     * @return The disconnect reason.
176     */
177    public String getReason() {
178        return mDisconnectReason;
179    }
180
181    /**
182     * Returns the tone to play when disconnected.
183     *
184     * @return the tone as defined in {@link ToneGenerator} to play when disconnected.
185     */
186    public int getTone() {
187        return mToneToPlay;
188    }
189
190    public static final Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
191        @Override
192        public DisconnectCause createFromParcel(Parcel source) {
193            int code = source.readInt();
194            CharSequence label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
195            CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
196            String reason = source.readString();
197            int tone = source.readInt();
198            return new DisconnectCause(code, label, description, reason, tone);
199        }
200
201        @Override
202        public DisconnectCause[] newArray(int size) {
203            return new DisconnectCause[size];
204        }
205    };
206
207    @Override
208    public void writeToParcel(Parcel destination, int flags) {
209        destination.writeInt(mDisconnectCode);
210        TextUtils.writeToParcel(mDisconnectLabel, destination, flags);
211        TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
212        destination.writeString(mDisconnectReason);
213        destination.writeInt(mToneToPlay);
214    }
215
216    @Override
217    public int describeContents() {
218        return 0;
219    }
220
221    @Override
222    public int hashCode() {
223        return Objects.hashCode(mDisconnectCode)
224                + Objects.hashCode(mDisconnectLabel)
225                + Objects.hashCode(mDisconnectDescription)
226                + Objects.hashCode(mDisconnectReason)
227                + Objects.hashCode(mToneToPlay);
228    }
229
230    @Override
231    public boolean equals(Object o) {
232        if (o instanceof DisconnectCause) {
233            DisconnectCause d = (DisconnectCause) o;
234            return Objects.equals(mDisconnectCode, d.getCode())
235                    && Objects.equals(mDisconnectLabel, d.getLabel())
236                    && Objects.equals(mDisconnectDescription, d.getDescription())
237                    && Objects.equals(mDisconnectReason, d.getReason())
238                    && Objects.equals(mToneToPlay, d.getTone());
239        }
240        return false;
241    }
242
243    @Override
244    public String toString() {
245        String code = "";
246        switch (mDisconnectCode) {
247            case UNKNOWN:
248                code = "UNKNOWN";
249                break;
250            case ERROR:
251                code = "ERROR";
252                break;
253            case LOCAL:
254                code = "LOCAL";
255                break;
256            case REMOTE:
257                code = "REMOTE";
258                break;
259            case CANCELED:
260                code = "CANCELED";
261                break;
262            case MISSED:
263                code = "MISSED";
264                break;
265            case REJECTED:
266                code = "REJECTED";
267                break;
268            case BUSY:
269                code = "BUSY";
270                break;
271            case RESTRICTED:
272                code = "RESTRICTED";
273                break;
274            case OTHER:
275                code = "OTHER";
276                break;
277            case CONNECTION_MANAGER_NOT_SUPPORTED:
278                code = "CONNECTION_MANAGER_NOT_SUPPORTED";
279                break;
280            default:
281                code = "invalid code: " + mDisconnectCode;
282                break;
283        }
284        String label = mDisconnectLabel == null ? "" : mDisconnectLabel.toString();
285        String description = mDisconnectDescription == null
286                ? "" : mDisconnectDescription.toString();
287        String reason = mDisconnectReason == null ? "" : mDisconnectReason;
288        return "DisconnectCause [ Code: (" + code + ")"
289                + " Label: (" + label + ")"
290                + " Description: (" + description + ")"
291                + " Reason: (" + reason + ")"
292                + " Tone: (" + mToneToPlay + ") ]";
293    }
294}
295