--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/wifi_dpp_fragment_header"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <SurfaceView
+ android:id="@+id/preview_view"
+ android:layout_width="426dp"
+ android:layout_height="320dp"
+ android:layout_gravity="center"/>
+ <com.android.settings.wifi.qrcode.QrDecorateView
+ android:id="@+id/decorate_view"
+ android:layout_width="426dp"
+ android:layout_height="320dp"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <TextView android:id="@+id/error_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+
+ <include layout="@layout/wifi_dpp_fragment_footer"
+ android:gravity="center|bottom"/>
+
+</LinearLayout>
+
android:layout_width="320dp"
android:layout_height="426dp"
android:layout_gravity="center"/>
- <ImageView
+ <com.android.settings.wifi.qrcode.QrDecorateView
android:id="@+id/decorate_view"
android:layout_width="320dp"
android:layout_height="426dp"
import android.os.Bundle;
import android.util.Log;
+import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
}
private void addQrCodeScannerFragment() {
- WifiDppQrCodeScannerFragment fragment = new WifiDppQrCodeScannerFragment();
+ final WifiDppQrCodeScannerFragment fragment = new WifiDppQrCodeScannerFragment();
mFragmentTransaction.add(R.id.fragment_container, fragment);
- mFragmentTransaction.addToBackStack(null);
+ mFragmentTransaction.addToBackStack(/* name */ null);
mFragmentTransaction.commit();
}
private void addQrCodeGeneratorFragment() {
- WifiDppQrCodeGeneratorFragment fragment = new WifiDppQrCodeGeneratorFragment();
+ final WifiDppQrCodeGeneratorFragment fragment = new WifiDppQrCodeGeneratorFragment();
mFragmentTransaction.add(R.id.fragment_container, fragment);
- mFragmentTransaction.addToBackStack(null);
+ mFragmentTransaction.addToBackStack(/* name */ null);
mFragmentTransaction.commit();
}
private void addChooseSavedWifiNetworkFragment() {
- ActionBar action = getActionBar();
+ final ActionBar action = getActionBar();
if (action != null) {
action.hide();
}
WifiDppChooseSavedWifiNetworkFragment fragment =
new WifiDppChooseSavedWifiNetworkFragment();
mFragmentTransaction.add(R.id.fragment_container, fragment);
- mFragmentTransaction.addToBackStack(null);
+ mFragmentTransaction.addToBackStack(/* name */ null);
mFragmentTransaction.commit();
}
+
+ @Override
+ protected void onStop() {
+ final Fragment fragment = mFragmentManager.findFragmentById(R.id.fragment_container);
+ if (fragment != null) {
+ // Remove it to prevent stacking multiple fragments after screen rotated.
+ mFragmentManager.beginTransaction().remove(fragment).commit();
+ }
+
+ super.onStop();
+ }
}
package com.android.settings.wifi.dpp;
+import android.annotation.Nullable;
import android.app.Activity;
import android.content.Intent;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Size;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
import com.android.settings.R;
+import com.android.settings.wifi.qrcode.QrCamera;
+import com.android.settings.wifi.qrcode.QrDecorateView;
+
+public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implements
+ SurfaceHolder.Callback,
+ QrCamera.ScannerCallback {
+ private QrCamera mCamera;
+ private SurfaceView mSurfaceView;
+ private QrDecorateView mDecorateView;
-public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment {
@Override
protected int getLayout() {
return R.layout.wifi_dpp_qrcode_scanner_fragment;
}
@Override
- public void onActivityCreated (Bundle savedInstanceState) {
- super.onActivityCreated (savedInstanceState);
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
setTitle(getString(R.string.wifi_dpp_add_device_to_network));
- String ssid = "";
- Intent intent = getActivity().getIntent();
- if (intent != null)
+ String ssid = null;
+ final Intent intent = getActivity().getIntent();
+ if (intent != null) {
ssid = intent.getStringExtra(WifiDppConfiguratorActivity.EXTRA_SSID);
- String description = getString(R.string.wifi_dpp_center_qr_code, ssid);
- setDescription(description);
+ }
+ if (TextUtils.isEmpty(ssid)) {
+ throw new IllegalArgumentException("Invalid SSID");
+ }
+ setDescription(getString(R.string.wifi_dpp_center_qr_code, ssid));
hideRightButton();
setLeftButtonText(getString(android.R.string.cancel));
setLeftButtonOnClickListener((view) -> {
- getActivity().setResult(Activity.RESULT_CANCELED);
- getActivity().finish();});
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ });
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mSurfaceView = (SurfaceView) view.findViewById(R.id.preview_view);
+ final SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
+ surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ surfaceHolder.addCallback(this);
+
+ mDecorateView = (QrDecorateView) view.findViewById(R.id.decorate_view);
+ }
+
+ @Override
+ public void surfaceCreated(final SurfaceHolder holder) {
+ initCamera(holder);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ destroyCamera();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // Do nothing
+ }
+
+ @Override
+ public Size getViewSize() {
+ return new Size(mSurfaceView.getWidth(), mSurfaceView.getHeight());
+ }
+
+ @Override
+ public Rect getFramePosition(Size previewSize, int cameraOrientation) {
+ return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
+ }
+
+ @Override
+ public void handleSuccessfulResult(String qrCode) {
+ destroyCamera();
+ mDecorateView.setFocused(true);
+ // TODO(b/120243131): Add a network by Wi-Fi Network config shared via QR code.
+ }
+
+ @Override
+ public void handleCameraFailure() {
+ destroyCamera();
+ }
+
+ private void initCamera(SurfaceHolder holder) {
+ // Check if the camera has already created.
+ if (mCamera == null) {
+ mCamera = new QrCamera(getContext(), this);
+ mCamera.start(holder);
+ }
+ }
+
+ private void destroyCamera() {
+ if (mCamera != null) {
+ mCamera.stop();
+ mCamera = null;
+ }
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import androidx.annotation.VisibleForTesting;
mReader.setHints(HINTS);
}
- void start(SurfaceHolder surfaceHolder) {
+ /**
+ * The function start camera preview and capture pictures to decode QR code continuously in a
+ * background task.
+ *
+ * @param surfaceHolder the Surface to be used for live preview, must already contain a surface
+ * when this method is called.
+ */
+ public void start(SurfaceHolder surfaceHolder) {
if (mDecodeTask == null) {
mDecodeTask = new DecodingTask(surfaceHolder);
- mDecodeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ // Execute in the separate thread pool to prevent block other AsyncTask.
+ mDecodeTask.executeOnExecutor(Executors.newSingleThreadExecutor());
}
}
- void stop() {
+ /**
+ * The function stop camera preview and background decode task. Caller call this function when
+ * the surface is being destroyed.
+ */
+ public void stop() {
removeMessages(MSG_AUTO_FOCUS);
if (mDecodeTask != null) {
mDecodeTask.cancel(true);
}
/** The scanner which includes this QrCamera class should implement this */
- interface ScannerCallback {
+ public interface ScannerCallback {
/**
* The function used to handle the decoding result of the QR code.
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
assertThat(mActivityRule.getActivityResult().getResultCode()).
isEqualTo(Activity.RESULT_CANCELED);
}
+
+ @Test
+ public void rotateScreen_shouldNotCrash() {
+ mActivityRule.getActivity().setRequestedOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ mActivityRule.getActivity().setRequestedOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
}