1 /*
2  * Copyright (c) 2008-2009, Motorola, Inc.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * - Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * - Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * - Neither the name of the Motorola, Inc. nor the names of its contributors
17  * may be used to endorse or promote products derived from this software
18  * without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.android.bluetooth.opp;
34 
35 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
36 
37 import android.bluetooth.AlertActivity;
38 import android.bluetooth.BluetoothProfile;
39 import android.bluetooth.BluetoothProtoEnums;
40 import android.content.BroadcastReceiver;
41 import android.content.ContentValues;
42 import android.content.Context;
43 import android.content.DialogInterface;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.net.Uri;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.Message;
50 import android.text.format.Formatter;
51 import android.util.Log;
52 import android.view.KeyEvent;
53 import android.view.View;
54 import android.widget.TextView;
55 import android.widget.Toast;
56 
57 import com.android.bluetooth.BluetoothMethodProxy;
58 import com.android.bluetooth.BluetoothStatsLog;
59 import com.android.bluetooth.R;
60 import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils;
61 import com.android.internal.annotations.VisibleForTesting;
62 
63 /** This class is designed to ask user to confirm if accept incoming file; */
64 // Next tag value for ContentProfileErrorReportUtils.report(): 1
65 public class BluetoothOppIncomingFileConfirmActivity extends AlertActivity {
66     private static final String TAG = "BluetoothIncomingFileConfirmActivity";
67 
68     @VisibleForTesting static final int DISMISS_TIMEOUT_DIALOG = 0;
69 
70     @VisibleForTesting static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
71 
72     private static final String PREFERENCE_USER_TIMEOUT = "user_timeout";
73 
74     private BluetoothOppTransferInfo mTransInfo;
75 
76     private Uri mUri;
77 
78     private ContentValues mUpdateValues;
79 
80     private boolean mTimeout = false;
81 
82     private BroadcastReceiver mReceiver = null;
83 
84     @Override
onCreate(Bundle savedInstanceState)85     protected void onCreate(Bundle savedInstanceState) {
86         setTheme(R.style.Theme_Material_Settings_Floating);
87         Log.v(TAG, "onCreate(): action = " + getIntent().getAction());
88         super.onCreate(savedInstanceState);
89 
90         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
91         Intent intent = getIntent();
92         mUri = intent.getData();
93         mTransInfo = new BluetoothOppTransferInfo();
94         mTransInfo = BluetoothOppUtility.queryRecord(this, mUri);
95         if (mTransInfo == null) {
96             Log.w(TAG, "Error: Can not get data from db");
97             ContentProfileErrorReportUtils.report(
98                     BluetoothProfile.OPP,
99                     BluetoothProtoEnums.BLUETOOTH_OPP_INCOMING_FILE_CONFIRM_ACTIVITY,
100                     BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR,
101                     0);
102             finish();
103             return;
104         }
105 
106         mAlertBuilder.setTitle(getString(R.string.incoming_file_confirm_content));
107         mAlertBuilder.setView(createView());
108         mAlertBuilder.setPositiveButton(
109                 R.string.incoming_file_confirm_ok, (dialog, which) -> onIncomingFileConfirmOk());
110         mAlertBuilder.setNegativeButton(
111                 R.string.incoming_file_confirm_cancel,
112                 (dialog, which) -> onIncomingFileConfirmCancel());
113 
114         setupAlert();
115         Log.v(TAG, "mTimeout: " + mTimeout);
116         if (mTimeout) {
117             onTimeout();
118         }
119 
120         Log.v(TAG, "BluetoothIncomingFileConfirmActivity: Got uri:" + mUri);
121 
122         mReceiver =
123                 new BroadcastReceiver() {
124                     @Override
125                     public void onReceive(Context context, Intent intent) {
126                         if (!BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION.equals(
127                                 intent.getAction())) {
128                             return;
129                         }
130                         onTimeout();
131                     }
132                 };
133         IntentFilter filter = new IntentFilter(BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION);
134         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
135         registerReceiver(mReceiver, filter);
136     }
137 
createView()138     private View createView() {
139         View view = getLayoutInflater().inflate(R.layout.incoming_dialog, null);
140 
141         ((TextView) view.findViewById(R.id.from_content)).setText(mTransInfo.mDeviceName);
142         String fileName = mTransInfo.mFileName;
143         if (fileName != null) {
144             fileName = fileName.replace('\t', ' ').replace('\n', ' ').replace('\r', ' ');
145         }
146         ((TextView) view.findViewById(R.id.filename_content)).setText(fileName);
147         ((TextView) view.findViewById(R.id.size_content))
148                 .setText(Formatter.formatFileSize(this, mTransInfo.mTotalBytes));
149 
150         return view;
151     }
152 
onIncomingFileConfirmOk()153     private void onIncomingFileConfirmOk() {
154         if (!mTimeout) {
155             // Update database
156             mUpdateValues = new ContentValues();
157             mUpdateValues.put(
158                     BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_CONFIRMED);
159             BluetoothMethodProxy.getInstance()
160                     .contentResolverUpdate(
161                             this.getContentResolver(), mUri, mUpdateValues, null, null);
162 
163             Toast.makeText(this, getString(R.string.bt_toast_1), Toast.LENGTH_SHORT).show();
164         }
165     }
166 
onIncomingFileConfirmCancel()167     private void onIncomingFileConfirmCancel() {
168         // Update database
169         mUpdateValues = new ContentValues();
170         mUpdateValues.put(
171                 BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_DENIED);
172         BluetoothMethodProxy.getInstance()
173                 .contentResolverUpdate(this.getContentResolver(), mUri, mUpdateValues, null, null);
174     }
175 
176     @Override
onKeyDown(int keyCode, KeyEvent event)177     public boolean onKeyDown(int keyCode, KeyEvent event) {
178         if (keyCode == KeyEvent.KEYCODE_BACK) {
179             Log.d(TAG, "onKeyDown() called; Key: back key");
180             finish();
181             return true;
182         }
183         return false;
184     }
185 
186     @Override
onDestroy()187     protected void onDestroy() {
188         super.onDestroy();
189         if (mReceiver != null) {
190             unregisterReceiver(mReceiver);
191         }
192     }
193 
194     @Override
onRestoreInstanceState(Bundle savedInstanceState)195     protected void onRestoreInstanceState(Bundle savedInstanceState) {
196         super.onRestoreInstanceState(savedInstanceState);
197         mTimeout = savedInstanceState.getBoolean(PREFERENCE_USER_TIMEOUT);
198         Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
199         if (mTimeout) {
200             onTimeout();
201         }
202     }
203 
204     @Override
onSaveInstanceState(Bundle outState)205     protected void onSaveInstanceState(Bundle outState) {
206         super.onSaveInstanceState(outState);
207         Log.v(TAG, "onSaveInstanceState() mTimeout: " + mTimeout);
208         outState.putBoolean(PREFERENCE_USER_TIMEOUT, mTimeout);
209     }
210 
onTimeout()211     private void onTimeout() {
212         mTimeout = true;
213 
214         changeTitle(
215                 getString(R.string.incoming_file_confirm_timeout_content, mTransInfo.mDeviceName));
216         changeButtonVisibility(DialogInterface.BUTTON_NEGATIVE, View.GONE);
217         changeButtonText(
218                 DialogInterface.BUTTON_POSITIVE,
219                 getString(R.string.incoming_file_confirm_timeout_ok));
220 
221         BluetoothMethodProxy.getInstance()
222                 .handlerSendMessageDelayed(
223                         mTimeoutHandler, DISMISS_TIMEOUT_DIALOG, DISMISS_TIMEOUT_DIALOG_VALUE);
224     }
225 
226     private final Handler mTimeoutHandler =
227             new Handler() {
228                 @Override
229                 public void handleMessage(Message msg) {
230                     switch (msg.what) {
231                         case DISMISS_TIMEOUT_DIALOG:
232                             Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
233                             finish();
234                             break;
235                         default:
236                             break;
237                     }
238                 }
239             };
240 }
241