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.stk;
18
19import com.android.internal.telephony.cat.CatLog;
20import com.android.internal.telephony.cat.TextMessage;
21import com.android.internal.telephony.cat.CatLog;
22
23import android.app.Activity;
24import android.app.AlarmManager;
25import android.app.PendingIntent;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.graphics.drawable.BitmapDrawable;
31import android.os.Bundle;
32import android.os.SystemClock;
33import android.view.KeyEvent;
34import android.view.View;
35import android.view.Window;
36import android.widget.Button;
37import android.widget.TextView;
38
39/**
40 * AlertDialog used for DISPLAY TEXT commands.
41 *
42 */
43public class StkDialogActivity extends Activity implements View.OnClickListener {
44    // members
45    private static final String className = new Object(){}.getClass().getEnclosingClass().getName();
46    private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1);
47    TextMessage mTextMsg = null;
48    private int mSlotId = -1;
49    private StkAppService appService = StkAppService.getInstance();
50    // Determines whether Terminal Response (TR) has been sent
51    private boolean mIsResponseSent = false;
52    private Context mContext;
53    // Utilize AlarmManager for real-time countdown
54    private PendingIntent mTimeoutIntent;
55    private AlarmManager mAlarmManager;
56    private final static String ALARM_TIMEOUT = "com.android.stk.DIALOG_ALARM_TIMEOUT";
57
58    //keys) for saving the state of the dialog in the icicle
59    private static final String TEXT = "text";
60
61    // message id for time out
62    private static final int MSG_ID_TIMEOUT = 1;
63
64    // buttons id
65    public static final int OK_BUTTON = R.id.button_ok;
66    public static final int CANCEL_BUTTON = R.id.button_cancel;
67
68    @Override
69    protected void onCreate(Bundle icicle) {
70        super.onCreate(icicle);
71
72        CatLog.d(LOG_TAG, "onCreate, sim id: " + mSlotId);
73        // New Dialog is created - set to no response sent
74        mIsResponseSent = false;
75
76        requestWindowFeature(Window.FEATURE_LEFT_ICON);
77
78        setContentView(R.layout.stk_msg_dialog);
79
80        Button okButton = (Button) findViewById(R.id.button_ok);
81        Button cancelButton = (Button) findViewById(R.id.button_cancel);
82
83        okButton.setOnClickListener(this);
84        cancelButton.setOnClickListener(this);
85
86        mContext = getBaseContext();
87        IntentFilter intentFilter = new IntentFilter();
88        intentFilter.addAction(ALARM_TIMEOUT);
89        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
90        mAlarmManager =(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
91
92    }
93
94    public void onClick(View v) {
95        String input = null;
96        switch (v.getId()) {
97        case OK_BUTTON:
98            CatLog.d(LOG_TAG, "OK Clicked!, mSlotId: " + mSlotId);
99            cancelTimeOut();
100            sendResponse(StkAppService.RES_ID_CONFIRM, true);
101            break;
102        case CANCEL_BUTTON:
103            CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId);
104            cancelTimeOut();
105            sendResponse(StkAppService.RES_ID_CONFIRM, false);
106            break;
107        }
108        finish();
109    }
110
111    @Override
112    public boolean onKeyDown(int keyCode, KeyEvent event) {
113        switch (keyCode) {
114        case KeyEvent.KEYCODE_BACK:
115            CatLog.d(LOG_TAG, "onKeyDown - KEYCODE_BACK");
116            cancelTimeOut();
117            sendResponse(StkAppService.RES_ID_BACKWARD);
118            finish();
119            break;
120        }
121        return false;
122    }
123
124    @Override
125    public void onResume() {
126        super.onResume();
127        CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent +
128                "], sim id: " + mSlotId);
129
130        initFromIntent(getIntent());
131        if (mTextMsg == null) {
132            finish();
133            return;
134        }
135
136        Window window = getWindow();
137
138        TextView mMessageView = (TextView) window
139                .findViewById(R.id.dialog_message);
140
141        setTitle(mTextMsg.title);
142
143        if (!(mTextMsg.iconSelfExplanatory && mTextMsg.icon != null)) {
144            mMessageView.setText(mTextMsg.text);
145        }
146
147        if (mTextMsg.icon == null) {
148            window.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
149                    com.android.internal.R.drawable.stat_notify_sim_toolkit);
150        } else {
151            window.setFeatureDrawable(Window.FEATURE_LEFT_ICON,
152                    new BitmapDrawable(mTextMsg.icon));
153        }
154
155        /*
156         * If the userClear flag is set and dialogduration is set to 0, the display Text
157         * should be displayed to user forever until some high priority event occurs
158         * (incoming call, MMI code execution etc as mentioned under section
159         * ETSI 102.223, 6.4.1)
160         */
161        if (StkApp.calculateDurationInMilis(mTextMsg.duration) == 0 &&
162            !mTextMsg.responseNeeded && mTextMsg.userClear) {
163            CatLog.d(LOG_TAG, "User should clear text..showing message forever");
164            return;
165        }
166
167        appService.setDisplayTextDlgVisibility(true, mSlotId);
168
169        /*
170         * When another activity takes the foreground, we do not want the Terminal
171         * Response timer to be restarted when our activity resumes. Hence we will
172         * check if there is an existing timer, and resume it. In this way we will
173         * inform the SIM in correct time when there is no response from the User
174         * to a dialog.
175         */
176        if (mTimeoutIntent != null) {
177            CatLog.d(LOG_TAG, "Pending Alarm! Let it finish counting down...");
178        }
179        else {
180            CatLog.d(LOG_TAG, "No Pending Alarm! OK to start timer...");
181            startTimeOut(mTextMsg.userClear);
182        }
183    }
184
185    @Override
186    public void onPause() {
187        super.onPause();
188        CatLog.d(LOG_TAG, "onPause, sim id: " + mSlotId);
189        appService.setDisplayTextDlgVisibility(false, mSlotId);
190
191        /*
192         * do not cancel the timer here cancelTimeOut(). If any higher/lower
193         * priority events such as incoming call, new sms, screen off intent,
194         * notification alerts, user actions such as 'User moving to another activtiy'
195         * etc.. occur during Display Text ongoing session,
196         * this activity would receive 'onPause()' event resulting in
197         * cancellation of the timer. As a result no terminal response is
198         * sent to the card.
199         */
200    }
201
202    @Override
203    protected void onStart() {
204        CatLog.d(LOG_TAG, "onStart, sim id: " + mSlotId);
205        super.onStart();
206    }
207
208    @Override
209    public void onStop() {
210        super.onStop();
211        CatLog.d(LOG_TAG, "onStop - before Send CONFIRM false mIsResponseSent[" +
212                mIsResponseSent + "], sim id: " + mSlotId);
213        if (!mIsResponseSent) {
214            appService.getStkContext(mSlotId).setPendingDialogInstance(this);
215        } else {
216            CatLog.d(LOG_TAG, "finish.");
217            appService.getStkContext(mSlotId).setPendingDialogInstance(null);
218            cancelTimeOut();
219            finish();
220        }
221    }
222
223    @Override
224    public void onDestroy() {
225        super.onDestroy();
226        CatLog.d(LOG_TAG, "onDestroy - mIsResponseSent[" + mIsResponseSent +
227                "], sim id: " + mSlotId);
228        // if dialog activity is finished by stkappservice
229        // when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here
230        // , since the dialog cmd is waiting user to process.
231        if (!mIsResponseSent && !appService.isDialogPending(mSlotId)) {
232            sendResponse(StkAppService.RES_ID_CONFIRM, false);
233        }
234        cancelTimeOut();
235        // Cleanup broadcast receivers to avoid leaks
236        if (mBroadcastReceiver != null) {
237            unregisterReceiver(mBroadcastReceiver);
238        }
239    }
240
241    @Override
242    public void onSaveInstanceState(Bundle outState) {
243        CatLog.d(LOG_TAG, "onSaveInstanceState");
244
245        super.onSaveInstanceState(outState);
246
247        outState.putParcelable(TEXT, mTextMsg);
248    }
249
250    @Override
251    public void onRestoreInstanceState(Bundle savedInstanceState) {
252        super.onRestoreInstanceState(savedInstanceState);
253
254        mTextMsg = savedInstanceState.getParcelable(TEXT);
255        CatLog.d(LOG_TAG, "onRestoreInstanceState - [" + mTextMsg + "]");
256    }
257
258    @Override
259    protected void onNewIntent(Intent intent) {
260        CatLog.d(LOG_TAG, "onNewIntent - updating the same Dialog box");
261        setIntent(intent);
262    }
263
264    private void sendResponse(int resId, boolean confirmed) {
265        if (mSlotId == -1) {
266            CatLog.d(LOG_TAG, "sim id is invalid");
267            return;
268        }
269
270        if (StkAppService.getInstance() == null) {
271            CatLog.d(LOG_TAG, "Ignore response: id is " + resId);
272            return;
273        }
274
275        CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] confirmed[" + confirmed + "]");
276
277        if (mTextMsg.responseNeeded) {
278            Bundle args = new Bundle();
279            args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
280            args.putInt(StkAppService.SLOT_ID, mSlotId);
281            args.putInt(StkAppService.RES_ID, resId);
282            args.putBoolean(StkAppService.CONFIRMATION, confirmed);
283            startService(new Intent(this, StkAppService.class).putExtras(args));
284            mIsResponseSent = true;
285        }
286    }
287
288    private void sendResponse(int resId) {
289        sendResponse(resId, true);
290    }
291
292    private void initFromIntent(Intent intent) {
293
294        if (intent != null) {
295            mTextMsg = intent.getParcelableExtra("TEXT");
296            mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1);
297        } else {
298            finish();
299        }
300
301        CatLog.d(LOG_TAG, "initFromIntent - [" + mTextMsg + "], sim id: " + mSlotId);
302    }
303
304    private void cancelTimeOut() {
305        CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId);
306        if (mTimeoutIntent != null) {
307            mAlarmManager.cancel(mTimeoutIntent);
308            mTimeoutIntent = null;
309        }
310    }
311
312    private void startTimeOut(boolean waitForUserToClear) {
313
314        // Reset timeout.
315        cancelTimeOut();
316        int dialogDuration = StkApp.calculateDurationInMilis(mTextMsg.duration);
317        // If duration is specified, this has priority. If not, set timeout
318        // according to condition given by the card.
319        if (mTextMsg.userClear == true && mTextMsg.responseNeeded == false) {
320            return;
321        } else {
322            // userClear = false. will disappear after a while.
323            if (dialogDuration == 0) {
324                if (waitForUserToClear) {
325                    dialogDuration = StkApp.DISP_TEXT_WAIT_FOR_USER_TIMEOUT;
326                } else {
327                    dialogDuration = StkApp.DISP_TEXT_CLEAR_AFTER_DELAY_TIMEOUT;
328                }
329            }
330            CatLog.d(LOG_TAG, "startTimeOut: " + mSlotId);
331            Intent mAlarmIntent = new Intent(ALARM_TIMEOUT);
332            mAlarmIntent.putExtra(StkAppService.SLOT_ID, mSlotId);
333            mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
334
335            // Try to use a more stringent timer not affected by system sleep.
336            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
337                mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
338                    SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent);
339            }
340            else {
341                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
342                SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent);
343            }
344        }
345    }
346
347    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
348        @Override public void onReceive(Context context, Intent intent) {
349            String action = intent.getAction();
350            int slotID = intent.getIntExtra(StkAppService.SLOT_ID, 0);
351
352            if (action == null || slotID != mSlotId) return;
353            CatLog.d(LOG_TAG, "onReceive, action=" + action + ", sim id: " + slotID);
354            if (action.equals(ALARM_TIMEOUT)) {
355                CatLog.d(LOG_TAG, "ALARM_TIMEOUT rcvd");
356                mTimeoutIntent = null;
357                sendResponse(StkAppService.RES_ID_TIMEOUT);
358                finish();
359            }
360        }
361    };
362}
363