• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.settings.development;
18 
19 import android.app.Activity;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.debug.AdbManager;
25 import android.debug.IAdbManager;
26 import android.graphics.Matrix;
27 import android.graphics.Rect;
28 import android.graphics.SurfaceTexture;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.util.Log;
35 import android.util.Size;
36 import android.view.LayoutInflater;
37 import android.view.TextureView;
38 import android.view.TextureView.SurfaceTextureListener;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.view.accessibility.AccessibilityEvent;
42 import android.widget.TextView;
43 
44 import androidx.annotation.StringRes;
45 
46 import com.android.settings.R;
47 import com.android.settings.wifi.dpp.AdbQrCode;
48 import com.android.settings.wifi.dpp.WifiDppQrCodeBaseFragment;
49 import com.android.settings.wifi.dpp.WifiNetworkConfig;
50 import com.android.settings.wifi.qrcode.QrCamera;
51 import com.android.settings.wifi.qrcode.QrDecorateView;
52 
53 /**
54  * Fragment shown when clicking on the "Pair by QR code" preference in
55  * the Wireless Debugging fragment.
56  */
57 public class AdbQrcodeScannerFragment extends WifiDppQrCodeBaseFragment implements
58         SurfaceTextureListener,
59         QrCamera.ScannerCallback {
60     private static final String TAG = "AdbQrcodeScannerFrag";
61 
62     /** Message sent to hide error message */
63     private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
64 
65     /** Message sent to show error message */
66     private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
67 
68     private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
69     private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
70 
71     private QrCamera mCamera;
72     private TextureView mTextureView;
73     private QrDecorateView mDecorateView;
74     private View mQrCameraView;
75     private View mVerifyingView;
76     private TextView mVerifyingTextView;
77     private TextView mErrorMessage;
78 
79     /** QR code data scanned by camera */
80     private AdbQrCode mAdbQrCode;
81     private WifiNetworkConfig mAdbConfig;
82 
83     private IAdbManager mAdbManager;
84 
85     private IntentFilter mIntentFilter;
86     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
87         @Override
88         public void onReceive(Context context, Intent intent) {
89             String action = intent.getAction();
90             if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
91                 Integer res = intent.getIntExtra(
92                         AdbManager.WIRELESS_STATUS_EXTRA,
93                         AdbManager.WIRELESS_STATUS_FAIL);
94                 if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
95                     Intent i = new Intent();
96                     i.putExtra(
97                             WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
98                             WirelessDebuggingFragment.SUCCESS_ACTION);
99                     getActivity().setResult(Activity.RESULT_OK, i);
100                     getActivity().finish();
101                 } else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
102                     Intent i = new Intent();
103                     i.putExtra(
104                             WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
105                             WirelessDebuggingFragment.FAIL_ACTION);
106                     getActivity().setResult(Activity.RESULT_OK, i);
107                     getActivity().finish();
108                 } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
109                     int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
110                     Log.i(TAG, "Got Qr pairing code port=" + port);
111                 }
112             }
113         }
114     };
115 
116     private final Handler mHandler = new Handler() {
117         @Override
118         public void handleMessage(Message msg) {
119             switch (msg.what) {
120                 case MESSAGE_HIDE_ERROR_MESSAGE:
121                     mErrorMessage.setVisibility(View.INVISIBLE);
122                     break;
123 
124                 case MESSAGE_SHOW_ERROR_MESSAGE:
125                     final String errorMessage = (String) msg.obj;
126 
127                     mErrorMessage.setVisibility(View.VISIBLE);
128                     mErrorMessage.setText(errorMessage);
129                     mErrorMessage.sendAccessibilityEvent(
130                             AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
131 
132                     // Cancel any pending messages to hide error view and requeue the message so
133                     // user has time to see error
134                     removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
135                     sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
136                             SHOW_ERROR_MESSAGE_INTERVAL);
137                     break;
138 
139                 default:
140                     return;
141             }
142         }
143     };
144 
145     @Override
onCreate(Bundle savedInstanceState)146     public void onCreate(Bundle savedInstanceState) {
147         super.onCreate(savedInstanceState);
148 
149         mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
150     }
151 
152     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)153     public final View onCreateView(LayoutInflater inflater, ViewGroup container,
154             Bundle savedInstanceState) {
155         return inflater.inflate(R.layout.adb_qrcode_scanner_fragment, container, false);
156     }
157 
158     @Override
onViewCreated(View view, Bundle savedInstanceState)159     public void onViewCreated(View view, Bundle savedInstanceState) {
160         super.onViewCreated(view, savedInstanceState);
161 
162         mTextureView = (TextureView) view.findViewById(R.id.preview_view);
163         mTextureView.setSurfaceTextureListener(this);
164 
165         mDecorateView = view.findViewById(R.id.decorate_view);
166         setProgressBarShown(false);
167 
168         setHeaderIconImageResource(R.drawable.ic_scan_24dp);
169 
170         mQrCameraView = view.findViewById(R.id.camera_layout);
171         mVerifyingView = view.findViewById(R.id.verifying_layout);
172         mVerifyingTextView = view.findViewById(R.id.verifying_textview);
173 
174         setHeaderTitle(R.string.wifi_dpp_scan_qr_code);
175         mSummary.setText(R.string.adb_wireless_qrcode_pairing_description);
176 
177         mErrorMessage = view.findViewById(R.id.error_message);
178     }
179 
180     @Override
onResume()181     public void onResume() {
182         super.onResume();
183 
184         mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
185         getActivity().registerReceiver(mReceiver, mIntentFilter);
186     }
187 
188     @Override
onPause()189     public void onPause() {
190         super.onPause();
191 
192         getActivity().unregisterReceiver(mReceiver);
193         try {
194             mAdbManager.disablePairing();
195         } catch (RemoteException e) {
196             Log.e(TAG, "Unable to cancel pairing");
197         }
198         getActivity().setResult(Activity.RESULT_CANCELED);
199         getActivity().finish();
200     }
201 
202     @Override
onSurfaceTextureUpdated(SurfaceTexture surface)203     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
204         // Do nothing
205     }
206 
207     @Override
onAttach(Context context)208     public void onAttach(Context context) {
209         super.onAttach(context);
210     }
211 
212     @Override
onActivityCreated(Bundle savedInstanceState)213     public void onActivityCreated(Bundle savedInstanceState) {
214         super.onActivityCreated(savedInstanceState);
215 
216         getActivity().getActionBar().hide();
217         // setTitle for TalkBack
218         getActivity().setTitle(R.string.wifi_dpp_scan_qr_code);
219     }
220 
221     @Override
getMetricsCategory()222     public int getMetricsCategory() {
223         return 0;
224     }
225 
226     @Override
onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)227     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
228         initCamera(surface);
229     }
230 
231     @Override
onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)232     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
233         // Do nothing
234     }
235 
236     @Override
onSurfaceTextureDestroyed(SurfaceTexture surface)237     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
238         destroyCamera();
239         return true;
240     }
241 
242     @Override
getViewSize()243     public Size getViewSize() {
244         return new Size(mTextureView.getWidth(), mTextureView.getHeight());
245     }
246 
247     @Override
setTransform(Matrix transform)248     public void setTransform(Matrix transform) {
249         mTextureView.setTransform(transform);
250     }
251 
252     @Override
getFramePosition(Size previewSize, int cameraOrientation)253     public Rect getFramePosition(Size previewSize, int cameraOrientation) {
254         return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
255     }
256 
257     @Override
isValid(String qrCode)258     public boolean isValid(String qrCode) {
259         try {
260             // WIFI:T:ADB;S:myname;P:mypass;;
261             mAdbQrCode = new AdbQrCode(qrCode);
262         } catch (IllegalArgumentException e) {
263             showErrorMessage(R.string.wifi_dpp_qr_code_is_not_valid_format);
264             return false;
265         }
266 
267         mAdbConfig = mAdbQrCode.getAdbNetworkConfig();
268 
269         return true;
270     }
271 
272     @Override
handleSuccessfulResult(String qrCode)273     public void handleSuccessfulResult(String qrCode) {
274         destroyCamera();
275         mDecorateView.setFocused(true);
276         mQrCameraView.setVisibility(View.GONE);
277         mVerifyingView.setVisibility(View.VISIBLE);
278         AdbQrCode.triggerVibrationForQrCodeRecognition(getContext());
279         mVerifyingTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
280         try {
281             mAdbManager.enablePairingByQrCode(mAdbConfig.getSsid(),
282                     mAdbConfig.getPreSharedKey());
283         } catch (RemoteException e) {
284             Log.e(TAG, "Unable to enable QR code pairing");
285             getActivity().setResult(Activity.RESULT_CANCELED);
286             getActivity().finish();
287         }
288     }
289 
290     @Override
handleCameraFailure()291     public void handleCameraFailure() {
292         destroyCamera();
293     }
294 
initCamera(SurfaceTexture surface)295     private void initCamera(SurfaceTexture surface) {
296         // Check if the camera has alread been created.
297         if (mCamera == null) {
298             mCamera = new QrCamera(getContext(), this);
299             mCamera.start(surface);
300         }
301     }
302 
303     /**
304      * To resume camera decoding task after handshake fail or Wi-Fi connection fail.
305      */
restartCamera()306     private void restartCamera() {
307         if (mCamera == null) {
308             Log.d(TAG, "mCamera is not available for restarting camera");
309             return;
310         }
311 
312         if (mCamera.isDecodeTaskAlive()) {
313             mCamera.stop();
314         }
315 
316         final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
317         if (surfaceTexture == null) {
318             throw new IllegalStateException("SurfaceTexture is not ready for restarting camera");
319         }
320 
321         mCamera.start(surfaceTexture);
322     }
323 
destroyCamera()324     private void destroyCamera() {
325         if (mCamera != null) {
326             mCamera.stop();
327             mCamera = null;
328         }
329     }
330 
showErrorMessage(@tringRes int messageResId)331     private void showErrorMessage(@StringRes int messageResId) {
332         final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
333                 getString(messageResId));
334         message.sendToTarget();
335     }
336 
337     @Override
isFooterAvailable()338     protected boolean isFooterAvailable() {
339         return false;
340     }
341 }
342