Merge branch 'afWarningQrCodeScan' into 'master'

* jif-afWarningQrCodeScan:
  do not include english string in translations
  Correct check style errors
  Add style for the poor QR code scanning autofocus capability warning
  Add 'poor QR code scanning capability' translations
  Call to the camera autofocus checker in the view
  Add camera characteristics checker

fdroid/fdroidclient!649
closes #260
This commit is contained in:
Hans-Christoph Steiner 2018-03-06 16:35:28 +01:00
commit 6ad3604a0e
55 changed files with 258 additions and 50 deletions

View File

@ -29,6 +29,7 @@ import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.net.WifiStateChangeService;
import org.fdroid.fdroid.views.swap.device.camera.CameraCharacteristicsChecker;
import java.net.URI;
import java.util.List;
@ -63,6 +64,7 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
protected void onFinishInflate() {
super.onFinishInflate();
setUIFromWifi();
setUpWarningMessageQrScan();
ImageView qrImage = (ImageView) findViewById(R.id.wifi_qr_code);
@ -81,6 +83,14 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
onWifiStateChanged, new IntentFilter(WifiStateChangeService.BROADCAST));
}
private void setUpWarningMessageQrScan() {
final View qrWarnningMessage = findViewById(R.id.warning_qr_scanner);
final boolean hasAutofocus = CameraCharacteristicsChecker.getInstance(getContext()).hasAutofocus();
final int visiblity = hasAutofocus ? GONE : VISIBLE;
qrWarnningMessage.setVisibility(visiblity);
}
/**
* Remove relevant listeners/receivers/etc so that they do not receive and process events
* when this view is not in use.

View File

@ -0,0 +1,21 @@
package org.fdroid.fdroid.views.swap.device.camera;
import android.content.Context;
public abstract class CameraCharacteristicsChecker {
public static CameraCharacteristicsChecker getInstance(final Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
return new CameraCharacteristicsMinApiLevel21(context);
} else {
return new CameraCharacteristicsMaxApiLevel20();
}
}
public abstract boolean hasAutofocus();
class FDroidDeviceException extends Exception {
FDroidDeviceException(final String message, final Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,50 @@
package org.fdroid.fdroid.views.swap.device.camera;
import android.hardware.Camera;
import android.util.Log;
import java.util.List;
public class CameraCharacteristicsMaxApiLevel20 extends CameraCharacteristicsChecker {
private static final String TAG = "CameraCharMaxApiLevel20";
protected CameraCharacteristicsMaxApiLevel20() {
}
@Override
public boolean hasAutofocus() {
boolean hasAutofocus = false;
try {
hasAutofocus = hasDeviceAutofocusCapability();
} catch (FDroidDeviceException e) {
Log.e(TAG, e.getMessage(), e);
}
return hasAutofocus;
}
private boolean hasDeviceAutofocusCapability() throws FDroidDeviceException {
try {
final int numberOfCameras = Camera.getNumberOfCameras();
if (numberOfCameras == 0) {
Log.i(TAG, "No camera on device");
return false;
}
boolean hasAutofocus = false;
for (int cameraId = 0; cameraId < numberOfCameras; cameraId++) {
Camera camera = Camera.open(cameraId);
Camera.Parameters parameters = camera.getParameters();
List<String> availableAFModes = parameters.getSupportedFocusModes();
hasAutofocus = availableAFModes.contains(Camera.Parameters.FOCUS_MODE_AUTO);
}
return hasAutofocus;
} catch (Exception e) {
String msg = "Exception accessing device camera";
Log.e(TAG, msg, e);
throw new FDroidDeviceException(msg, e);
}
}
}

View File

@ -0,0 +1,111 @@
package org.fdroid.fdroid.views.swap.device.camera;
import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.Log;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class CameraCharacteristicsMinApiLevel21 extends CameraCharacteristicsChecker {
private static final String TAG = "CameraCharMinApiLevel21";
private final CameraManager cameraManager;
protected CameraCharacteristicsMinApiLevel21(final Context context) {
this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
@Override
public boolean hasAutofocus() {
boolean hasAutofocus = false;
try {
hasAutofocus = hasDeviceAutofocus();
} catch (FDroidDeviceException e) {
Log.e(TAG, e.getMessage(), e);
}
return hasAutofocus;
}
private boolean hasDeviceAutofocus() throws FDroidDeviceException {
try {
boolean deviceHasAutofocus = false;
final String[] cameraIdList = getCameraIdList();
for (final String cameraId : cameraIdList) {
if (isLensFacingBack(cameraId)) {
deviceHasAutofocus = testAutofocusModeForCamera(cameraId);
break;
}
}
return deviceHasAutofocus;
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
throw new FDroidDeviceException("Exception accessing the camera list", e);
}
}
@NonNull
private String[] getCameraIdList() throws FDroidDeviceException {
try {
return cameraManager.getCameraIdList();
} catch (CameraAccessException e) {
Log.e(TAG, e.getMessage(), e);
throw new FDroidDeviceException("Exception accessing the camera list", e);
}
}
private boolean isLensFacingBack(final String cameraId) throws FDroidDeviceException {
final Integer lensFacing = getCameraCharacteristics(cameraId).get(CameraCharacteristics.LENS_FACING);
return lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK;
}
@NonNull
private CameraCharacteristics getCameraCharacteristics(final String cameraId) throws FDroidDeviceException {
try {
return cameraManager.getCameraCharacteristics(cameraId);
} catch (CameraAccessException e) {
Log.e(TAG, e.getMessage(), e);
throw new FDroidDeviceException("Exception accessing the camera id = " + cameraId, e);
}
}
private boolean testAutofocusModeForCamera(final String cameraId) throws FDroidDeviceException {
try {
boolean hasAutofocusMode = false;
final int[] autoFocusModes = getAvailableAFModes(cameraId);
if (autoFocusModes != null) {
hasAutofocusMode = testAvailableMode(autoFocusModes);
}
return hasAutofocusMode;
} catch (FDroidDeviceException e) {
Log.e(TAG, e.getMessage(), e);
throw new FDroidDeviceException("Exception accessing the camera id = " + cameraId, e);
}
}
private int[] getAvailableAFModes(final String cameraId) throws FDroidDeviceException {
return getCameraCharacteristics(cameraId).get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
}
private boolean testAvailableMode(final int[] autoFocusModes) {
boolean hasAutofocusMode = false;
for (final int mode : autoFocusModes) {
boolean afMode = isAutofocus(mode);
hasAutofocusMode |= afMode;
}
return hasAutofocusMode;
}
private boolean isAutofocus(final int mode) {
return mode != android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_OFF;
}
}

View File

@ -44,6 +44,14 @@
android:layout_gravity="center"
android:id="@+id/btn_qr_scanner"/>
<TextView
android:id="@+id/warning_qr_scanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/warning_scaning_qr_code"
android:visibility="gone"
style="@style/SwapTheme.Wizard.QRScanWarningText"/>
</LinearLayout>
</org.fdroid.fdroid.views.swap.WifiQrView>

View File

@ -563,6 +563,7 @@ This often occurs with apps installed via Google Play or other sources, if they
</plurals>
<string name="details_last_updated_today">Updated today</string>
<string name="warning_scaning_qr_code">Your camera doesn\'t seem to have an autofocus. It might be difficult to scan the code.</string>
<plurals name="details_last_update_days">
<item quantity="one">Updated %1$d day ago</item>
<item quantity="other">Updated %1$d days ago</item>

View File

@ -228,12 +228,19 @@
<style name="SwapTheme.Wizard.MainTextBase" parent="@style/SwapTheme.Wizard.Text">
<item name="android:paddingLeft">40dp</item>
<item name="android:paddingRight">40dp</item>
<item name="android:paddingTop">20dp</item>
<item name="android:paddingBottom">15dp</item>
<item name="android:paddingTop">10dp</item>
<item name="android:paddingBottom">10dp</item>
</style>
<style name="SwapTheme.Wizard.MainText" parent="SwapTheme.Wizard.MainTextBase" />
<style name="SwapTheme.Wizard.QRScanWarningText" parent="@style/SwapTheme.Wizard.MainTextBase" >
<item name="android:paddingLeft">40dp</item>
<item name="android:paddingRight">40dp</item>
<item name="android:paddingTop">0dp</item>
<item name="android:paddingBottom">5dp</item>
</style>
<style name="SwapTheme.Wizard.LocalIpAddress" parent="@style/SwapTheme.Wizard.Text">
<item name="android:textSize">20sp</item>
<item name="android:paddingLeft">40dp</item>