From 1972ea8c3047edf11b800f4028bf49f1ce1bb1b3 Mon Sep 17 00:00:00 2001 From: Vibhor Srivastava <69432426+vibhorSrv@users.noreply.github.com> Date: Tue, 23 Feb 2021 12:27:41 +0530 Subject: [PATCH] Restructured code, Added CameraDump option --- LICENSE | 21 ++ app/build.gradle | 7 +- .../java/com/vibhorsrv/cameraids/Camera.java | 159 ------------ .../vibhorsrv/cameraids/CamerasFinder.java | 226 ------------------ .../com/vibhorsrv/cameraids/MainActivity.java | 209 +++++++++++++--- .../cameraids/api/CameraFinderAbstract.java | 93 +++++++ .../vibhorsrv/cameraids/api/CameraIDs.java | 34 +++ .../api/CameraIdentifierAbstract.java | 19 ++ .../cameraids/api/ReflectionApi.java | 10 + .../cameraids/finder/CameraFinder.java | 73 ++++++ .../cameraids/finder/CameraIdentifier.java | 108 +++++++++ .../cameraids/model/Camera2ApiProperties.java | 141 +++++++++++ .../cameraids/model/CameraModel.java | 97 ++++++++ .../vibhorsrv/cameraids/model/CameraType.java | 11 + .../cameraids/model/DerivedProperties.java | 59 +++++ .../reflection/ReflectionProvider.java | 31 +++ .../com/vibhorsrv/cameraids/saver/Saver.java | 29 +++ .../com/vibhorsrv/cameraids/share/Share.java | 18 ++ .../com/vibhorsrv/cameraids/util/AppUtil.java | 19 ++ .../cameraids/util/CameraDumpUtil.java | 30 +++ .../vibhorsrv/cameraids/util/CameraUtil.java | 21 ++ app/src/main/res/layout/activity_main.xml | 9 + app/src/main/res/menu/options_menu.xml | 13 +- app/src/main/res/values/strings.xml | 11 +- build.gradle | 2 +- checkroot/.gitignore | 1 + checkroot/build.gradle | 27 +++ checkroot/consumer-rules.pro | 0 checkroot/proguard-rules.pro | 21 ++ checkroot/src/main/AndroidManifest.xml | 5 + .../cameraids/checkroot/CheckRoot.java | 61 +++++ gradle/wrapper/gradle-wrapper.properties | 6 +- settings.gradle | 1 + 33 files changed, 1147 insertions(+), 425 deletions(-) create mode 100644 LICENSE delete mode 100644 app/src/main/java/com/vibhorsrv/cameraids/Camera.java delete mode 100644 app/src/main/java/com/vibhorsrv/cameraids/CamerasFinder.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/api/CameraFinderAbstract.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/api/CameraIDs.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/api/CameraIdentifierAbstract.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/api/ReflectionApi.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/finder/CameraFinder.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/finder/CameraIdentifier.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/model/Camera2ApiProperties.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/model/CameraModel.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/model/CameraType.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/model/DerivedProperties.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/reflection/ReflectionProvider.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/saver/Saver.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/share/Share.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/util/AppUtil.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/util/CameraDumpUtil.java create mode 100644 app/src/main/java/com/vibhorsrv/cameraids/util/CameraUtil.java create mode 100644 checkroot/.gitignore create mode 100644 checkroot/build.gradle create mode 100644 checkroot/consumer-rules.pro create mode 100644 checkroot/proguard-rules.pro create mode 100644 checkroot/src/main/AndroidManifest.xml create mode 100644 checkroot/src/main/java/com/vibhorsrv/cameraids/checkroot/CheckRoot.java diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6535dd1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Vibhor Srivastava + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/build.gradle b/app/build.gradle index e73bdc4..c9aab44 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,10 +6,10 @@ android { defaultConfig { applicationId "com.vibhorsrv.cameraids" - minSdkVersion 28 + minSdkVersion 26 targetSdkVersion 30 - versionCode 11 - versionName "1.1" + versionCode 12 + versionName "1.2" } buildTypes { @@ -28,4 +28,5 @@ dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' + implementation project(':checkroot') } \ No newline at end of file diff --git a/app/src/main/java/com/vibhorsrv/cameraids/Camera.java b/app/src/main/java/com/vibhorsrv/cameraids/Camera.java deleted file mode 100644 index f5f74e2..0000000 --- a/app/src/main/java/com/vibhorsrv/cameraids/Camera.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.vibhorsrv.cameraids; - -import android.util.Size; -import android.util.SizeF; -import androidx.annotation.NonNull; - -import java.util.Arrays; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; - -/** - * Created by Vibhor Srivastava on 28/09/2020 - */ -public class Camera { - private final String id; - private final boolean isFront; - private final float focalLength; - private final float aperture; - private final int[] aeModes; - private final Size[] rawSizes; - private final SizeF sensorSize; - private final Double angleOfView; - private final boolean flashSupported; - private final String supportedHardwareLevel; - private final Set physicalIds; - private String type = ""; - private String name = ""; - - public Camera(String id, boolean isFront, float focalLength, float aperture, SizeF sensorSize, double angleOfView, int[] aeModes, boolean flashSupported, Size[] rawSizes, String supportedHardwareLevel, Set physicalIds) { - this.id = id; - this.focalLength = focalLength; - this.aperture = aperture; - this.sensorSize = sensorSize; - this.angleOfView = angleOfView; - this.aeModes = aeModes; - this.flashSupported = flashSupported; - this.rawSizes = rawSizes; - this.supportedHardwareLevel = supportedHardwareLevel; - this.isFront = isFront; - this.physicalIds = physicalIds; - if (!physicalIds.isEmpty()) - type = "(Logical)"; - } - - public Set getPhysicalIds() { - return physicalIds; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - setType("\u2713"); - } - - public String getId() { - return id; - } - - public boolean isFront() { - return isFront; - } - - public float getFocalLength() { - return focalLength; - } - - public float getAperture() { - return aperture; - } - - public int[] getAeModes() { - return aeModes; - } - - public boolean isFlashSupported() { - return flashSupported; - } - - public String getSupportedHardwareLevel() { - return supportedHardwareLevel; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Size[] getRawSizes() { - return rawSizes; - } - - public SizeF getSensorSize() { - return sensorSize; - } - - public double getAngleOfView() { - return angleOfView; - } - - public boolean isNameNotSet() { - return name.equals(""); - } - - public boolean isTypeNotSet() { - return type.equals(""); - } - - @NonNull - public String toString() { - return "" + type + (isFront ? "FRONT" : "BACK") + " " + "ID" + '[' + id + "] " + name + (physicalIds.isEmpty() ? "" : " = ID" + physicalIds.toString().replace(", ", " + ")) + - "\n\t\t\t" + - "FocalLength = " + focalLength + - "\n\t\t\t" + - "Aperture = " + aperture + - "\n\t\t\t" + - "SensorSize = " + sensorSize + - "\n\t\t\t" + - "AngleOfView(Diagonal) = " + Math.round(angleOfView) + "\u00b0" + - "\n\t\t\t" + - "AEModes = " + Arrays.toString(aeModes) + - "\n\t\t\t" + - "FlashSupported = " + flashSupported + - "\n\t\t\t" + - "RAW_SENSOR sizes = " + Arrays.toString(rawSizes) + - "\n\t\t\t" + - "SupportedHardwareLevel = " + supportedHardwareLevel + - "\n\n" - ; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - Camera camera = (Camera) o; - return isFront() == camera.isFront() && - Float.compare(camera.getFocalLength(), getFocalLength()) == 0 && - Float.compare(camera.getAperture(), getAperture()) == 0 && - isFlashSupported() == camera.isFlashSupported() && - Arrays.equals(getAeModes(), camera.getAeModes()) && - getSensorSize().equals(camera.getSensorSize()); - } - - @Override - public int hashCode() { - int result = Objects.hash(isFront(), getFocalLength(), getAperture(), getSensorSize(), isFlashSupported()); - result = 31 * result + Arrays.hashCode(getAeModes()); - return result; - } -} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/CamerasFinder.java b/app/src/main/java/com/vibhorsrv/cameraids/CamerasFinder.java deleted file mode 100644 index 55e53e7..0000000 --- a/app/src/main/java/com/vibhorsrv/cameraids/CamerasFinder.java +++ /dev/null @@ -1,226 +0,0 @@ -package com.vibhorsrv.cameraids; - -import android.graphics.ImageFormat; -import android.hardware.camera2.CameraAccessException; -import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraManager; -import android.hardware.camera2.params.StreamConfigurationMap; -import android.os.Build; -import android.util.Size; -import android.util.SizeF; - -import java.util.*; - -/** - * Created by Vibhor on 23/09/2020 - */ -public class CamerasFinder { - private final Map map = new LinkedHashMap<>(); - private final CameraManager mCameraManager; - private String mFileName; - - public CamerasFinder(CameraManager mCameraManager) { - this.mCameraManager = mCameraManager; - } - - private void scanAllCameras(CameraManager cameraManager) { - for (int id = 0; id < 512; id++) { - CameraCharacteristics cameraCharacteristics; - try { - cameraCharacteristics = cameraManager.getCameraCharacteristics(String.valueOf(id)); - float[] focalLength = cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS); - float[] aperture = cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES); - if (focalLength != null && aperture != null) { - Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); - Camera camera = new Camera( - String.valueOf(id), - facing == 0, - focalLength[0], - aperture[0], - cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE), - calculateAngleOfView(cameraCharacteristics), - cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES), - cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE), - getRawSizes(cameraCharacteristics), - getSupportedHWlevel(cameraCharacteristics), - cameraCharacteristics.getPhysicalCameraIds() - ); - if (camera.isTypeNotSet() && map.containsValue(camera)) { - camera.setType("(Repeat)"); - } - map.put(String.valueOf(id), camera); - } - } catch (IllegalArgumentException ignored) { - } catch (Exception e) { - e.printStackTrace(); - } - } - updateMap(); - } - - private void updateMap() { -// TreeSet backAperturesSorted = new TreeSet<>(); -// TreeSet frontAperturesSorted = new TreeSet<>(); - TreeSet frontAnglesOfViewSorted = new TreeSet<>(); - TreeSet backAnglesOfViewSorted = new TreeSet<>(); -// Comparator sizeFComparator = (o1, o2) -> Float.compare(o1.getWidth() * o1.getHeight(), o2.getWidth() * o2.getHeight()); -// TreeSet backSensorSizeSorted = new TreeSet<>(sizeFComparator); -// TreeSet frontSensorSizeSorted = new TreeSet<>(sizeFComparator); - - //Filling tree sets for later use/comparisons - for (Camera currentCamera : map.values()) { - if (currentCamera.isTypeNotSet()) - if (currentCamera.isFront()) { - frontAnglesOfViewSorted.add(currentCamera.getAngleOfView()); -// frontAperturesSorted.add(currentCamera.getAperture()); -// frontSensorSizeSorted.add(currentCamera.getSensorSize()); - } else { - backAnglesOfViewSorted.add(currentCamera.getAngleOfView()); -// backAperturesSorted.add(currentCamera.getAperture()); -// backSensorSizeSorted.add(currentCamera.getSensorSize()); - } - } - - //Stores the Value of Main Back and Main Front (initialise with ID 0 and 1 respectively) - Camera mainBackCam = map.get("0"); - Camera mainFrontCam = map.get("1"); - - //Finding Main Back camera and updating the map - for (Map.Entry cameraEntry : map.entrySet()) { - Camera currentCam = cameraEntry.getValue(); - if (currentCam.isNameNotSet() && currentCam.isTypeNotSet() && currentCam.getAeModes() != null) { - if (!currentCam.isFront()) { - currentCam.setName("(Main)"); - cameraEntry.setValue(currentCam); - mainBackCam = currentCam; - break; - } - } - } - //Finding Main Front camera and updating the map - for (Map.Entry cameraEntry : map.entrySet()) { - Camera currentCam = cameraEntry.getValue(); - if (currentCam.isNameNotSet() && currentCam.isTypeNotSet() && currentCam.getAeModes() != null) { - if (currentCam.isFront()) { - currentCam.setName("(Main)"); - cameraEntry.setValue(currentCam); - mainFrontCam = currentCam; - break; - } - } - } - - //Naming the Cameras - for (Map.Entry cameraEntry : map.entrySet()) { - Camera currentCam = cameraEntry.getValue(); - if (mainBackCam != null && mainFrontCam != null) { - if (currentCam.isTypeNotSet() && currentCam.isNameNotSet()) { - if (currentCam.getAeModes() == null) { - currentCam.setName("(Other)"); //Sensors such as ToF sensors - cameraEntry.setValue(currentCam); - } else if (currentCam.getAeModes().length > 2) { - if (!currentCam.isFront()) { - nameCameras(cameraEntry, mainBackCam, backAnglesOfViewSorted); - } else { - nameCameras(cameraEntry, mainFrontCam, frontAnglesOfViewSorted); - } - } else if (currentCam.getAeModes().length <= 2) { - if (currentCam.isFront() && currentCam.getSensorSize().getWidth() > mainFrontCam.getSensorSize().getWidth() && currentCam.getAngleOfView() > mainFrontCam.getAngleOfView()) { - currentCam.setName("(Wide)"); //Added this logic keeping in mind Samsung S20 - } else { - currentCam.setName("(Depth/Portrait)"); - } - cameraEntry.setValue(currentCam); - } - } - } - } - } - - //Set Names for Wide/Macro/Tele cameras - private void nameCameras(Map.Entry cameraEntry, Camera mainCam, TreeSet sortedListOfAngles) { - Camera currentCam = cameraEntry.getValue(); - if (currentCam.getAngleOfView() > mainCam.getAngleOfView()) { - if (currentCam.getAngleOfView() == sortedListOfAngles.last()) { //largest angle of view - currentCam.setName("(Wide)"); - } else { - currentCam.setName("(Macro)"); //TODO improve logic - } - cameraEntry.setValue(currentCam); - } else if (currentCam.getAngleOfView() < mainCam.getAngleOfView()) { - currentCam.setName("(Tele)"); //angle of view less than Main Camera - cameraEntry.setValue(currentCam); - } - } - - - private Double calculateAngleOfView(CameraCharacteristics cc) { - float focalLength = cc.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; - double sensorDiagonal = Math.sqrt(Math.pow(cc.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE).getWidth(), 2) - + Math.pow(cc.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE).getHeight(), 2) - ); - return Math.toDegrees(2 * Math.atan(sensorDiagonal / (2 * focalLength))); - } - - private Size[] getRawSizes(CameraCharacteristics cameraCharacteristics) { - StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - return streamConfigurationMap.getOutputSizes(ImageFormat.RAW_SENSOR); - - } - - private String getSupportedHWlevel(CameraCharacteristics cameraCharacteristics) { - return hwLevelName(cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)); - } - - private String hwLevelName(int level) { - return level == 0 ? "LIMITED" - : level == 1 ? "FULL" - : level == 2 ? "LEGACY" - : level == 3 ? "3" - : level == 4 ? "EXTERNAL" - : ""; - } - - /** - * @return filename based on the Device's brand and model name - */ - public String getFileName() { - return mFileName; - } - - private void setFileName(String mFileName) { - this.mFileName = mFileName; - } - - /** - * Scans all IDs and returns usable string of data - * - * @return the formatted displayable string of data - */ - public String getResultString() { - StringBuilder sb = new StringBuilder(); - sb.append(Build.BRAND).append(", ").append(Build.MODEL).append(", ").append(Build.MANUFACTURER).append(", ").append(Build.DEVICE); - setFileName("CameraIDs-".concat(sb.toString().replace(", ", "-"))); - sb.append("\n\n"); - sb.append("Android ").append(Build.VERSION.RELEASE).append(" - ").append(System.getProperty("os.version")); - sb.append("\n"); - - scanAllCameras(mCameraManager); - try { - sb.append("\n===============\n"); - sb.append("\nCamera IDs visible to Apps = "); - sb.append(Arrays.toString(mCameraManager.getCameraIdList())); - sb.append("\n\n===============\n"); - sb.append("All Cameras IDs = ").append(map.keySet()).append("\n"); - sb.append("\n"); - for (Camera camera : map.values()) - sb.append(camera); - sb.append("===============\n"); - - } catch (CameraAccessException e) { - e.printStackTrace(); - } - return sb.toString(); - } -} - diff --git a/app/src/main/java/com/vibhorsrv/cameraids/MainActivity.java b/app/src/main/java/com/vibhorsrv/cameraids/MainActivity.java index 11e1864..bdb6035 100644 --- a/app/src/main/java/com/vibhorsrv/cameraids/MainActivity.java +++ b/app/src/main/java/com/vibhorsrv/cameraids/MainActivity.java @@ -1,77 +1,179 @@ package com.vibhorsrv.cameraids; import android.content.Context; -import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; import android.text.Html; import android.text.method.LinkMovementMethod; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; +import android.widget.Toast; + import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.FileProvider; +import androidx.core.widget.ContentLoadingProgressBar; + import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.vibhorsrv.cameraids.api.CameraIDs; +import com.vibhorsrv.cameraids.checkroot.CheckRoot; +import com.vibhorsrv.cameraids.finder.CameraFinder; +import com.vibhorsrv.cameraids.finder.CameraIdentifier; +import com.vibhorsrv.cameraids.model.CameraModel; +import com.vibhorsrv.cameraids.saver.Saver; +import com.vibhorsrv.cameraids.share.Share; +import com.vibhorsrv.cameraids.util.AppUtil; +import com.vibhorsrv.cameraids.util.CameraDumpUtil; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Locale; /** * Created by Vibhor on 23/09/2020 */ public class MainActivity extends AppCompatActivity { + private static final String CAMERA_DUMP = "CameraDump"; + private static final String CAMERA_IDS = "CameraIDs"; + private final Saver saver = new Saver(); + private final TextUtil textUtil = new TextUtil(); + private StringBuilder stringBuilder; private TextView mTextView; + private FloatingActionButton floatingActionButton; + private CameraManager cameraManager; + private CameraIDs.Finder> cameraFinder; + private CameraIDs.Identifier> cameraIdentifier; + private String bufferText = ""; + private String cameraDumpText = ""; + private ContentLoadingProgressBar contentLoadingProgressBar; + private boolean cameraDumpMode = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - mTextView = findViewById(R.id.textView); - FloatingActionButton floatingActionButton = findViewById(R.id.share); + floatingActionButton = findViewById(R.id.share); + contentLoadingProgressBar = findViewById(R.id.progress_circular); - CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); - CamerasFinder camerasFinder = new CamerasFinder(cameraManager); + cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); - mTextView.setText(camerasFinder.getResultString()); - floatingActionButton.setOnClickListener(v -> writeToFileAndShare(camerasFinder.getFileName(), mTextView.getText().toString())); + cameraFinder = new CameraFinder(cameraManager); + cameraFinder.init(); + cameraIdentifier = new CameraIdentifier(cameraFinder.getCameraModels()); + cameraIdentifier.init(); + + showCameraIDsInfo(); } - private void writeToFileAndShare(String filename, String textToWrite) { - File newFile = new File(getExternalFilesDir(null).getAbsolutePath(), filename + ".txt"); - try { - FileOutputStream fos = new FileOutputStream(newFile); - fos.write(textToWrite.getBytes()); - fos.close(); - } catch (IOException e) { - e.printStackTrace(); + @Override + public void onBackPressed() { + if (cameraDumpMode) { + showCameraIDsInfo(); + } else { + super.onBackPressed(); + } + } + + private void showCameraIDsInfo() { + mTextView.setText(bufferText = ""); + cameraDumpMode = false; + setTitle(CAMERA_IDS); + + File newFile = new File(getExternalFilesDir(null).getAbsolutePath(), Saver.generateFileName(CAMERA_IDS, "txt")); + Uri fileUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", newFile); + + if (bufferText.equals("")) { + contentLoadingProgressBar.show(); + + stringBuilder = new StringBuilder(); + textUtil.addTimeStamp(stringBuilder); + textUtil.addDeviceInfo(stringBuilder); + textUtil.addBasicInfo(stringBuilder); + cameraFinder.getCameraModels().forEach(cameraModel -> + stringBuilder + .append(cameraModel) + .append("\n============================\n")); + bufferText = stringBuilder.toString(); + + saver.saveText(newFile.getAbsolutePath(), bufferText); + mTextView.setText(bufferText); + contentLoadingProgressBar.hide(); } - Intent intent = new Intent(Intent.ACTION_SEND); - Uri fileUri = FileProvider.getUriForFile(MainActivity.this, getApplicationContext().getPackageName() + ".provider", newFile); - intent.putExtra(Intent.EXTRA_STREAM, fileUri); - intent.setType("text/txt"); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - startActivity(intent); + floatingActionButton.setOnClickListener(v -> Share.shareUri(this, fileUri, textUtil.generateExtraShareText(CAMERA_IDS))); + } + + + private void showCameraDump() { + mTextView.setText(bufferText = ""); + cameraDumpMode = true; + setTitle(CAMERA_DUMP); + + File newFile = new File(getExternalFilesDir(null).getAbsolutePath(), Saver.generateFileName(CAMERA_DUMP, "txt")); + Uri fileUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", newFile); + + HandlerThread t = new HandlerThread("CameraDumpThread"); + t.start(); + Handler h = new Handler(t.getLooper()); + h.post(() -> { + if (bufferText.equals("")) { + contentLoadingProgressBar.show(); + + stringBuilder = new StringBuilder(); + textUtil.addTimeStamp(stringBuilder); + textUtil.addDeviceInfo(stringBuilder); + cameraDumpText = String.join("\n", CameraDumpUtil.getCameraDump()); + stringBuilder.append(cameraDumpText); + bufferText = stringBuilder.toString(); + + mTextView.post(() -> { + if (!cameraDumpText.equals("")) { + if (mTextView.getText().toString().equals("")) { + mTextView.setText(bufferText); + saver.saveText(newFile.getAbsolutePath(), bufferText); + } + floatingActionButton.setOnClickListener(v -> Share.shareUri(this, fileUri, textUtil.generateExtraShareText(CAMERA_DUMP))); + } else { + Toast.makeText(this, R.string.root_data_na_warn, Toast.LENGTH_LONG).show(); + } + }); + + contentLoadingProgressBar.post(contentLoadingProgressBar::hide); + } + }); + t.quitSafely(); } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.options_menu, menu); + MenuItem cameraDumpOption = menu.findItem(R.id.action_camera_dump); + if (cameraDumpOption != null) { + cameraDumpOption.setVisible(CheckRoot.isRooted()); + } return true; } + //Menu click listeners + public void onInfoClicked(MenuItem item) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.action_info); String br = getString(R.string.line_break); String html = - getString(R.string.info_repeat,br) + - getString(R.string.info_logical,br) + -// getString(R.string.info_profile,br) + - getString(R.string.github_link,br); + getString(R.string.app_info) + + br + + getString(R.string.app_version, AppUtil.getVersionName(this)) + + br + + getString(R.string.github_link); builder.setMessage(Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)); AlertDialog dialog = builder.create(); dialog.show(); @@ -81,4 +183,57 @@ public void onInfoClicked(MenuItem item) { messageTextView.setMovementMethod(LinkMovementMethod.getInstance()); } } + + public void onCameraDumpClicked(MenuItem item) { + if (CheckRoot.hasRootPermission()) { + showCameraDump(); + } else { + Toast.makeText(this, R.string.root_na_warn, Toast.LENGTH_SHORT).show(); + } + } + + private class TextUtil { + private String generateExtraShareText(String prefix) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder + .append(prefix).append("\n") + .append("\n============================\n"); + addDeviceInfo(stringBuilder); + return stringBuilder.toString(); + } + + private void addTimeStamp(StringBuilder stringBuilder) { + stringBuilder + .append(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.ROOT).format(new Date())) + .append("\n") + .append("\n============================\n"); + } + + private void addDeviceInfo(StringBuilder stringBuilder) { + stringBuilder + .append("\n") + .append("Device : ").append(Build.BRAND).append(" ").append(Build.MODEL).append(" (").append(Build.DEVICE).append(')') + .append("\n") + .append("Manufacturer : ").append(Build.MANUFACTURER) + .append("\n") + .append("Android : ").append(Build.VERSION.RELEASE) + .append("\n") + .append("Fingerprint : ").append(Build.FINGERPRINT) + .append("\n") + .append("\n============================\n"); + } + + private void addBasicInfo(StringBuilder stringBuilder) { + stringBuilder + .append("\n") + .append("Camera IDs Visible to Apps = ").append(cameraFinder.getApiCameraIdList()) + .append("\n") + .append("\n============================\n") + .append("\n") + .append("All Camera IDs = ").append(cameraFinder.getAllCameraIdList()) + .append("\n") + .append("\n============================\n"); + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/vibhorsrv/cameraids/api/CameraFinderAbstract.java b/app/src/main/java/com/vibhorsrv/cameraids/api/CameraFinderAbstract.java new file mode 100644 index 0000000..81d315c --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/api/CameraFinderAbstract.java @@ -0,0 +1,93 @@ +package com.vibhorsrv.cameraids.api; + +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; + +import com.vibhorsrv.cameraids.model.Camera2ApiProperties; +import com.vibhorsrv.cameraids.model.CameraModel; +import com.vibhorsrv.cameraids.model.DerivedProperties; +import com.vibhorsrv.cameraids.reflection.ReflectionProvider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Base class for camera finder mechanism + *

+ * Created by Vibhor Srivastava + * + * @param Iterable implementation for storing CameraModel objects + */ +public abstract class CameraFinderAbstract> implements CameraIDs.Finder { + protected final List validCameraIds = new ArrayList<>(); + protected final ReflectionProvider reflectionProvider = new ReflectionProvider(); + protected final CameraManager cameraManager; + protected T cameraModels; + + public CameraFinderAbstract(CameraManager cameraManager) { + this.cameraManager = cameraManager; + } + + public void init() { + scanCameras(cameraManager); + createModels(); + } + + protected abstract void createModels(); + + protected abstract Camera2ApiProperties findProperties(int cameraId, CameraCharacteristics characteristics); + + protected abstract DerivedProperties deriveProperties(int cameraId, Camera2ApiProperties camera2ApiProperties); + + private void scanCameras(CameraManager cameraManager) { + if (cameraManager != null) { + for (int id = 0; id < 512; id++) { + try { + cameraManager.getCameraCharacteristics(String.valueOf(id)); + validCameraIds.add(String.valueOf(id)); + } catch (IllegalArgumentException ignored) { + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + @Override + public CameraCharacteristics getCameraCharacteristics(int cameraId) { + try { + if (cameraManager != null) { + return cameraManager.getCameraCharacteristics(String.valueOf(cameraId)); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public T getCameraModels() { + return cameraModels; + } + + + @Override + public List getApiCameraIdList() { + try { + if (cameraManager != null) { + return Arrays.stream(cameraManager.getCameraIdList()).collect(Collectors.toList()); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + return new ArrayList<>(); + } + + @Override + public List getAllCameraIdList() { + return validCameraIds; + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/api/CameraIDs.java b/app/src/main/java/com/vibhorsrv/cameraids/api/CameraIDs.java new file mode 100644 index 0000000..1b864a5 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/api/CameraIDs.java @@ -0,0 +1,34 @@ +package com.vibhorsrv.cameraids.api; + +import android.hardware.camera2.CameraCharacteristics; + +import com.vibhorsrv.cameraids.model.CameraModel; + +import java.util.List; + +/** + * Created by Vibhor Srivastava + */ +public interface CameraIDs { + interface Finder> { + void init(); + + CameraCharacteristics getCameraCharacteristics(int cameraId); + + T getCameraModels(); + + List getApiCameraIdList(); + + List getAllCameraIdList(); + } + + interface Identifier> { + void init(); + + void identifyCamera(T cameraModels); + } + + interface Saver { + void saveText(String path, String text); + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/api/CameraIdentifierAbstract.java b/app/src/main/java/com/vibhorsrv/cameraids/api/CameraIdentifierAbstract.java new file mode 100644 index 0000000..39a4bdf --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/api/CameraIdentifierAbstract.java @@ -0,0 +1,19 @@ +package com.vibhorsrv.cameraids.api; + +import com.vibhorsrv.cameraids.model.CameraModel; + +/** + * Base class for camera identifier mechanism + *

+ * Created by Vibhor Srivastava + * + * @param Iterable implementation for storing CameraModel objects + */ +public abstract class CameraIdentifierAbstract> implements CameraIDs.Identifier { + protected T cameraModels; + + public CameraIdentifierAbstract(T cameraModels) { + this.cameraModels = cameraModels; + } + +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/api/ReflectionApi.java b/app/src/main/java/com/vibhorsrv/cameraids/api/ReflectionApi.java new file mode 100644 index 0000000..51d5bee --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/api/ReflectionApi.java @@ -0,0 +1,10 @@ +package com.vibhorsrv.cameraids.api; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public interface ReflectionApi { + Field[] getFields(Class aClass); + + Method[] getMethods(Class aClass); +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/finder/CameraFinder.java b/app/src/main/java/com/vibhorsrv/cameraids/finder/CameraFinder.java new file mode 100644 index 0000000..8da3349 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/finder/CameraFinder.java @@ -0,0 +1,73 @@ +package com.vibhorsrv.cameraids.finder; + +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.os.Build; +import android.util.Log; +import android.util.Size; + +import com.vibhorsrv.cameraids.api.CameraFinderAbstract; +import com.vibhorsrv.cameraids.model.Camera2ApiProperties; +import com.vibhorsrv.cameraids.model.CameraModel; +import com.vibhorsrv.cameraids.model.DerivedProperties; +import com.vibhorsrv.cameraids.util.CameraUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; + +public class CameraFinder extends CameraFinderAbstract> { + + public CameraFinder(CameraManager cameraManager) { + super(cameraManager); + cameraModels = new ArrayList<>(); + } + + @Override + public void createModels() { + validCameraIds.forEach(id -> cameraModels.add(new CameraModel(Integer.parseInt(id)))); + cameraModels.forEach(cameraModel -> cameraModel.setCamera2ApiProperties(findProperties(cameraModel.getId(), getCameraCharacteristics(cameraModel.getId())))); + cameraModels.forEach(cameraModel -> cameraModel.setDerivedProperties(deriveProperties(cameraModel.getId(), cameraModel.getCamera2ApiProperties()))); + } + + + @Override + public Camera2ApiProperties findProperties(int cameraId, CameraCharacteristics characteristics) { + Camera2ApiProperties properties = new Camera2ApiProperties(cameraId); + properties.setFacing(characteristics.get(CameraCharacteristics.LENS_FACING)); + properties.setFocalLength(characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]); + properties.setAperture(characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)[0]); + properties.setAeModes(characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)); + properties.setFlashSupported(characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)); + properties.setRawSensorSizes(getRawSizes(characteristics, ImageFormat.RAW_SENSOR)); + properties.setSensorSize(characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)); + properties.setPixelArraySize(characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)); + properties.setSupportedHardwareLevel(characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)); + properties.setSupportedHardwareLevelString(reflectionProvider.getResultFieldName(CameraMetadata.class, "INFO_SUPPORTED_HARDWARE_LEVEL_", properties.getSupportedHardwareLevel())); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + properties.setPhysicalIds(characteristics.getPhysicalCameraIds()); + } else + properties.setPhysicalIds(Collections.EMPTY_SET); + return properties; + } + + private Size[] getRawSizes(CameraCharacteristics cameraCharacteristics, int imageFormat) { + StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return streamConfigurationMap.getOutputSizes(imageFormat); + + } + + @Override + public DerivedProperties deriveProperties(int cameraId, Camera2ApiProperties properties) { + DerivedProperties derivedProperties = new DerivedProperties(cameraId); + derivedProperties.setFacing(reflectionProvider.getResultFieldName(CameraMetadata.class, "LENS_FACING_", properties.getFacing())); + derivedProperties.setLogical(!properties.getPhysicalIds().isEmpty()); + derivedProperties.setAngleOfView(CameraUtil.calculateAngleOfView(properties.getFocalLength(), properties.getSensorSize(), properties.getPixelArraySize())); + derivedProperties.setPixelSize(CameraUtil.calculatePixelSize(properties.getPixelArraySize().getWidth(), properties.getSensorSize().getWidth())); + derivedProperties.setMm35FocalLength(CameraUtil.calculate35mmeqv(properties.getFocalLength(), properties.getSensorSize())); + return derivedProperties; + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/finder/CameraIdentifier.java b/app/src/main/java/com/vibhorsrv/cameraids/finder/CameraIdentifier.java new file mode 100644 index 0000000..6fde37e --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/finder/CameraIdentifier.java @@ -0,0 +1,108 @@ +package com.vibhorsrv.cameraids.finder; + +import android.hardware.camera2.CameraCharacteristics; + +import androidx.annotation.NonNull; + +import com.vibhorsrv.cameraids.api.CameraIdentifierAbstract; +import com.vibhorsrv.cameraids.model.Camera2ApiProperties; +import com.vibhorsrv.cameraids.model.CameraModel; +import com.vibhorsrv.cameraids.model.CameraType; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.TreeSet; + +public class CameraIdentifier extends CameraIdentifierAbstract> { + private static final Comparator SORT_BY_ANGLE_OF_VIEW = Comparator.comparingDouble(cameraModel -> cameraModel.getDerivedProperties().getAngleOfView()); + private static final Comparator SORT_BY_35MM_FOCAL = Comparator.comparingDouble(cameraModel -> cameraModel.getDerivedProperties().getMm35FocalLength()); + private final ArrayList frontCameraModels = new ArrayList<>(); + private final ArrayList backCameraModels = new ArrayList<>(); + private final TreeSet widerThanMain = new TreeSet<>(SORT_BY_ANGLE_OF_VIEW); + private final ArrayList narrowerThanMain = new ArrayList<>(); + private final ArrayList propertiesArrayList = new ArrayList<>(); + + + public CameraIdentifier(ArrayList cameraModels) { + super(cameraModels); + } + + public void init() { + cameraModels.forEach(cameraModel -> propertiesArrayList.add(cameraModel.getCamera2ApiProperties())); + frontOrBack(cameraModels); + identifyCamera(frontCameraModels); + identifyCamera(backCameraModels); + } + + private void frontOrBack(@NonNull Collection cameraModels) { + for (CameraModel model : cameraModels) { + if (model.getCamera2ApiProperties().getFacing() == CameraCharacteristics.LENS_FACING_BACK) { + backCameraModels.add(model); + } + if (model.getCamera2ApiProperties().getFacing() == CameraCharacteristics.LENS_FACING_FRONT) { + frontCameraModels.add(model); + } + } + } + + @Override + public void identifyCamera(@NonNull ArrayList cameraModels) { + if (!cameraModels.isEmpty()) { + CameraModel main = cameraModels.get(0); + main.setCameraType(CameraType.MAIN); + main.setZoomFactor(1f); + cameraModels.remove(0); + + //Determine whether camera is logical + cameraModels.forEach(model -> { + if (model.getDerivedProperties().isLogical() || getBit(6, model.getId())) { + model.setCameraType(CameraType.LOGICAL); + } + propertiesArrayList.forEach(camera2ApiProperties -> { + if (model.getId() != camera2ApiProperties.getId() && model.getCamera2ApiProperties().equals(camera2ApiProperties)) { + model.setCameraType(CameraType.LOGICAL); + } + }); + }); + cameraModels.removeIf(CameraModel::isTypeSet); + + cameraModels.sort(SORT_BY_ANGLE_OF_VIEW); + + cameraModels.forEach(model -> { + float zoom = model.getDerivedProperties().getMm35FocalLength() / main.getDerivedProperties().getMm35FocalLength(); + model.setZoomFactor(zoom); + + //Determine whether camera is Depth or Other + if (model.getCamera2ApiProperties().getAeModes() == null) { + model.setCameraType(CameraType.OTHER); + } + if (!model.getCamera2ApiProperties().isFlashSupported()) { + model.setCameraType(CameraType.DEPTH); + } else { + if (model.getDerivedProperties().getAngleOfView() > main.getDerivedProperties().getAngleOfView()) { + widerThanMain.add(model); + } else { + narrowerThanMain.add(model); + } + } + }); + + //Determine whether camera is Ultrawide or Macro + widerThanMain.forEach(cameraModel -> { + if (cameraModel.getDerivedProperties().getAngleOfView() == widerThanMain.last().getDerivedProperties().getAngleOfView()) { + cameraModel.setCameraType(CameraType.ULTRAWIDE); + } else { + cameraModel.setCameraType(CameraType.MACRO); + } + }); + + //Determine whether camera is Tele + narrowerThanMain.forEach(cameraModel -> cameraModel.setCameraType(CameraType.TELE)); + } + } + + private boolean getBit(int num, int val) { + return ((val >> (num - 1)) & 1) == 1; + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/model/Camera2ApiProperties.java b/app/src/main/java/com/vibhorsrv/cameraids/model/Camera2ApiProperties.java new file mode 100644 index 0000000..051c8cb --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/model/Camera2ApiProperties.java @@ -0,0 +1,141 @@ +package com.vibhorsrv.cameraids.model; + +import android.util.Size; +import android.util.SizeF; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; + +public class Camera2ApiProperties { + private final int id; + private int facing; + private float focalLength; + private float aperture; + private int[] aeModes; + private Size[] rawSensorSizes; + private SizeF sensorSize; + private Size pixelArraySize; + + public Size getPixelArraySize() { + return pixelArraySize; + } + + public void setPixelArraySize(Size pixelArraySize) { + this.pixelArraySize = pixelArraySize; + } + + private boolean flashSupported; + private int supportedHardwareLevel; + private String supportedHardwareLevelString; + private Set physicalIds; + public Camera2ApiProperties(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Camera2ApiProperties that = (Camera2ApiProperties) o; + return + facing == that.facing && + Float.compare(that.focalLength, focalLength) == 0 && + Float.compare(that.aperture, aperture) == 0 && + flashSupported == that.flashSupported && + Arrays.equals(aeModes, that.aeModes) && + sensorSize.equals(that.sensorSize); + } + + @Override + public int hashCode() { + int result = Objects.hash(facing, focalLength, aperture, sensorSize, flashSupported); + result = 31 * result + Arrays.hashCode(aeModes); + return result; + } + + public int getFacing() { + return facing; + } + + public void setFacing(int facing) { + this.facing = facing; + } + + public float getFocalLength() { + return focalLength; + } + + public void setFocalLength(float focalLength) { + this.focalLength = focalLength; + } + + public float getAperture() { + return aperture; + } + + public void setAperture(float aperture) { + this.aperture = aperture; + } + + public int[] getAeModes() { + return aeModes; + } + + public void setAeModes(int[] aeModes) { + this.aeModes = aeModes; + } + + public Size[] getRawSensorSizes() { + return rawSensorSizes; + } + + public void setRawSensorSizes(Size[] rawSensorSizes) { + this.rawSensorSizes = rawSensorSizes; + } + + public SizeF getSensorSize() { + return sensorSize; + } + + public void setSensorSize(SizeF sensorSize) { + this.sensorSize = sensorSize; + } + + public boolean isFlashSupported() { + return flashSupported; + } + + public void setFlashSupported(boolean flashSupported) { + this.flashSupported = flashSupported; + } + + public int getSupportedHardwareLevel() { + return supportedHardwareLevel; + } + + public void setSupportedHardwareLevel(int supportedHardwareLevel) { + this.supportedHardwareLevel = supportedHardwareLevel; + } + + public String getSupportedHardwareLevelString() { + return supportedHardwareLevelString; + } + + public void setSupportedHardwareLevelString(String supportedHardwareLevelString) { + this.supportedHardwareLevelString = supportedHardwareLevelString; + } + + public Set getPhysicalIds() { + return physicalIds; + } + + public void setPhysicalIds(Set physicalIds) { + this.physicalIds = physicalIds; + } + +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/model/CameraModel.java b/app/src/main/java/com/vibhorsrv/cameraids/model/CameraModel.java new file mode 100644 index 0000000..4460757 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/model/CameraModel.java @@ -0,0 +1,97 @@ +package com.vibhorsrv.cameraids.model; + +import androidx.annotation.NonNull; + +import java.util.Arrays; +import java.util.Locale; + +public class CameraModel { + private final int id; + private CameraType cameraType; + private float zoomFactor; + private Camera2ApiProperties camera2ApiProperties; + private DerivedProperties derivedProperties; + + public CameraModel(int id) { + this.id = id; + } + + public float getZoomFactor() { + return zoomFactor; + } + + public void setZoomFactor(float zoomFactor) { + this.zoomFactor = zoomFactor; + } + + public CameraType getCameraType() { + return cameraType; + } + + public void setCameraType(CameraType cameraType) { + if (this.cameraType == null) { + this.cameraType = cameraType; + } + } + + public int getId() { + return id; + } + + public Camera2ApiProperties getCamera2ApiProperties() { + return camera2ApiProperties; + } + + public void setCamera2ApiProperties(Camera2ApiProperties camera2ApiProperties) { + this.camera2ApiProperties = camera2ApiProperties; + } + + public DerivedProperties getDerivedProperties() { + return derivedProperties; + } + + public void setDerivedProperties(DerivedProperties derivedProperties) { + this.derivedProperties = derivedProperties; + } + + public boolean isTypeSet() { + return cameraType != null; + } + + @Override + @NonNull + public String toString() { + return "\n" + + "CameraID = [" + id + ']' + (!cameraType.equals(CameraType.LOGICAL) ? " \u2605" : (!camera2ApiProperties.getPhysicalIds().isEmpty() ? " = " + camera2ApiProperties.getPhysicalIds().toString().replace(", ", "+") : "")) + + "\n" + + "Facing = " + derivedProperties.getFacing() + + "\n" + + (!cameraType.equals(CameraType.LOGICAL) ? + "Zoom = " + String.format(Locale.ROOT, "%.2fx", zoomFactor).replace(".00", "") + + "\n" : "") + + "Type = " + cameraType + + "\n" + + "FocalLength = " + String.format(Locale.ROOT, "%.2fmm", camera2ApiProperties.getFocalLength()) + + "\n" + + "35mm eqv FocalLength = " + String.format(Locale.ROOT, "%.2fmm", derivedProperties.getMm35FocalLength()) + + "\n" + + "Aperture = " + camera2ApiProperties.getAperture() + + "\n" + + "SensorSize = " + camera2ApiProperties.getSensorSize().toString() + + "\n" + + "PixelArray = " + camera2ApiProperties.getPixelArraySize().toString() + + "\n" + + "PixelSize = " + String.format(Locale.ROOT, "%.2f", derivedProperties.getPixelSize()) + "µm" + + "\n" + + "AngleOfView(Diagonal) = " + Math.round(derivedProperties.getAngleOfView()) + "\u00b0" + + "\n" + + "AEModes = " + Arrays.toString(camera2ApiProperties.getAeModes()) + + "\n" + + "FlashSupported = " + camera2ApiProperties.isFlashSupported() + + "\n" + + "RAW_SENSOR sizes = " + Arrays.deepToString(camera2ApiProperties.getRawSensorSizes()) + + "\n" + + "SupportedHardwareLevel = " + camera2ApiProperties.getSupportedHardwareLevelString() + + "\n"; + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/model/CameraType.java b/app/src/main/java/com/vibhorsrv/cameraids/model/CameraType.java new file mode 100644 index 0000000..04025c1 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/model/CameraType.java @@ -0,0 +1,11 @@ +package com.vibhorsrv.cameraids.model; + +public enum CameraType { + MAIN, + ULTRAWIDE, + TELE, + MACRO, + DEPTH, + LOGICAL, + OTHER +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/model/DerivedProperties.java b/app/src/main/java/com/vibhorsrv/cameraids/model/DerivedProperties.java new file mode 100644 index 0000000..cff63b6 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/model/DerivedProperties.java @@ -0,0 +1,59 @@ +package com.vibhorsrv.cameraids.model; + +public class DerivedProperties { + private final int id; + private boolean isLogical; + private String facing; + private double angleOfView; + private float mm35FocalLength; + private float pixelSize; + + public float getPixelSize() { + return pixelSize; + } + + public void setPixelSize(float pixelSize) { + this.pixelSize = pixelSize; + } + + public DerivedProperties(int id) { + this.id = id; + } + + public boolean isLogical() { + return isLogical; + } + + public void setLogical(boolean logical) { + isLogical = logical; + } + + public int getId() { + return id; + } + + public String getFacing() { + return facing; + } + + public void setFacing(String facing) { + this.facing = facing; + } + + public double getAngleOfView() { + return angleOfView; + } + + public void setAngleOfView(double angleOfView) { + this.angleOfView = angleOfView; + } + + public float getMm35FocalLength() { + return mm35FocalLength; + } + + public void setMm35FocalLength(float mm35FocalLength) { + this.mm35FocalLength = mm35FocalLength; + } + +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/reflection/ReflectionProvider.java b/app/src/main/java/com/vibhorsrv/cameraids/reflection/ReflectionProvider.java new file mode 100644 index 0000000..64bda71 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/reflection/ReflectionProvider.java @@ -0,0 +1,31 @@ +package com.vibhorsrv.cameraids.reflection; + +import com.vibhorsrv.cameraids.api.ReflectionApi; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ReflectionProvider implements ReflectionApi { + @Override + public Field[] getFields(Class aClass) { + return aClass.getDeclaredFields(); + } + + @Override + public Method[] getMethods(Class aClass) { + return aClass.getDeclaredMethods(); + } + + public String getResultFieldName(Class aClass, String prefix, Integer value) { + for (Field f : getFields(aClass)) + if (f.getName().startsWith(prefix)) { + try { + if (f.getInt(f) == value) + return f.getName().replace(prefix, ""); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + return ""; + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/saver/Saver.java b/app/src/main/java/com/vibhorsrv/cameraids/saver/Saver.java new file mode 100644 index 0000000..cae282d --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/saver/Saver.java @@ -0,0 +1,29 @@ +package com.vibhorsrv.cameraids.saver; + +import android.os.Build; + +import com.vibhorsrv.cameraids.api.CameraIDs; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class Saver implements CameraIDs.Saver { + + public static String generateFileName(String prefix, String ext) { + return prefix + '-' + Build.BRAND + "-" + Build.MODEL + "-" + Build.MANUFACTURER + "-" + Build.DEVICE + '.' + ext; + } + + @Override + public void saveText(String path, String text) { + writeToFile(path, text.getBytes()); + } + + private void writeToFile(String path, byte[] dataToWrite) { + try { + Files.write(Paths.get(path), dataToWrite); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/share/Share.java b/app/src/main/java/com/vibhorsrv/cameraids/share/Share.java new file mode 100644 index 0000000..51cf84d --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/share/Share.java @@ -0,0 +1,18 @@ +package com.vibhorsrv.cameraids.share; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import java.net.URLConnection; + +public class Share { + public static void shareUri(Context context, Uri uri, String extraText) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, uri); + intent.putExtra(Intent.EXTRA_TEXT, extraText); + intent.setType(URLConnection.guessContentTypeFromName(uri.toString())); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + context.startActivity(Intent.createChooser(intent, null)); + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/util/AppUtil.java b/app/src/main/java/com/vibhorsrv/cameraids/util/AppUtil.java new file mode 100644 index 0000000..3a64211 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/util/AppUtil.java @@ -0,0 +1,19 @@ +package com.vibhorsrv.cameraids.util; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +public class AppUtil { + + public static String getVersionName(Context context) { + try { + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + return packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return ""; + + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/util/CameraDumpUtil.java b/app/src/main/java/com/vibhorsrv/cameraids/util/CameraDumpUtil.java new file mode 100644 index 0000000..b2ee303 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/util/CameraDumpUtil.java @@ -0,0 +1,30 @@ +package com.vibhorsrv.cameraids.util; + +import android.util.Log; + +import com.vibhorsrv.cameraids.checkroot.CheckRoot; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class CameraDumpUtil { + /** + * @return list of lines as displayed on executing the dumpsys command + */ + public static List getCameraDump() { + if (CheckRoot.hasRootPermission()) { + try { + Process process = Runtime.getRuntime().exec("su -c dumpsys media.camera"); + return new BufferedReader(new InputStreamReader(process.getInputStream())).lines().collect(Collectors.toList()); + } catch (IOException e) { + e.printStackTrace(); + } + } + Log.d("CameraDumpUtil", "getCameraDump(): Root Not Available"); + return new ArrayList<>(); + } +} diff --git a/app/src/main/java/com/vibhorsrv/cameraids/util/CameraUtil.java b/app/src/main/java/com/vibhorsrv/cameraids/util/CameraUtil.java new file mode 100644 index 0000000..85d0602 --- /dev/null +++ b/app/src/main/java/com/vibhorsrv/cameraids/util/CameraUtil.java @@ -0,0 +1,21 @@ +package com.vibhorsrv.cameraids.util; + +import android.util.Size; +import android.util.SizeF; + +public class CameraUtil { + public static float calculatePixelSize(int pixelArrayWidth, float sensorWidth) { + return (sensorWidth / ((float) pixelArrayWidth)) * 1000.0f; + } + + public static Double calculateAngleOfView(float focalLength, SizeF sensorSize, Size pixelArraySize) { + float pixelSize = CameraUtil.calculatePixelSize(pixelArraySize.getWidth(), sensorSize.getWidth()); + return Math.toDegrees(Math.atan(Math.sqrt(Math.pow(sensorSize.getWidth() * pixelSize, 2.0d) + + Math.pow(sensorSize.getHeight() * pixelSize, 2.0d)) / ((double) (2.0f * focalLength))) * 2.0d); + } + + public static float calculate35mmeqv(float focalLength, SizeF sensorSize) { + return (36.0f / sensorSize.getWidth()) * focalLength; + } + +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fd3c85a..3208d30 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -18,6 +18,7 @@ android:id="@+id/textView" android:layout_marginTop="15dp" android:textSize="15sp" + android:textIsSelectable="true" android:textColor="@android:color/black" android:padding="10dp" android:layout_gravity="fill" @@ -26,6 +27,14 @@ tools:text="TextView" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 93a5bea..2c6595f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,9 +1,12 @@ CameraIDs Info - (Repeat) = Focal length, aperture and AE modes match for two or more camera ids.]]>%1$s - (Logical) = The camera device is a logical camera backed by two or more physical cameras.]]>%1$s - (Profile) = Camera supports multiple Raw Sizes]]>%1$s - Check Source Code on GitHub]]>%1$s + Camera Dump + Version : %1$s + A simple app to display all available cameras on an Android device.]]> + LOGICAL = The camera device is a logical camera backed by two or more physical cameras.]]> + Check Source Code on GitHub]]>
]]>
+ No data available!\nDid you grant root permission? + Root Not Available
\ No newline at end of file diff --git a/build.gradle b/build.gradle index 6754c23..46397e1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath "com.android.tools.build:gradle:4.0.1" + classpath 'com.android.tools.build:gradle:4.1.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/checkroot/.gitignore b/checkroot/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/checkroot/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/checkroot/build.gradle b/checkroot/build.gradle new file mode 100644 index 0000000..bb92d18 --- /dev/null +++ b/checkroot/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdkVersion 23 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation 'androidx.appcompat:appcompat:1.2.0' +} \ No newline at end of file diff --git a/checkroot/consumer-rules.pro b/checkroot/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/checkroot/proguard-rules.pro b/checkroot/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/checkroot/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/checkroot/src/main/AndroidManifest.xml b/checkroot/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c52137d --- /dev/null +++ b/checkroot/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/checkroot/src/main/java/com/vibhorsrv/cameraids/checkroot/CheckRoot.java b/checkroot/src/main/java/com/vibhorsrv/cameraids/checkroot/CheckRoot.java new file mode 100644 index 0000000..bb43c3f --- /dev/null +++ b/checkroot/src/main/java/com/vibhorsrv/cameraids/checkroot/CheckRoot.java @@ -0,0 +1,61 @@ +package com.vibhorsrv.cameraids.checkroot; + +import android.util.Log; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; + +public class CheckRoot { + private static final String[] pathList; + private static final String SU = "su"; + private static final String TAG = "CheckRoot"; + + static { + pathList = new String[]{ + "/sbin/", + "/system/bin/", + "/system/xbin/", + "/data/local/xbin/", + "/data/local/bin/", + "/system/sd/xbin/", + "/system/bin/failsafe/", + "/data/local/" + }; + } + + public static boolean isRooted() { + return binariesExist(SU); + } + + private static boolean binariesExist(String binaryName) { + for (String path : pathList) { + if (new File(path, binaryName).exists()) + return true; + } + return false; + } + + public static boolean hasRootPermission() { + boolean hasRoot = false; + if (isRooted()) { + try { + Process p = Runtime.getRuntime().exec("su"); + DataOutputStream os = new DataOutputStream(p.getOutputStream()); + Log.d(TAG, "hasRootPermission(): Checking..."); + os.writeBytes("exit\n"); + os.flush(); + try { + p.waitFor(); + hasRoot = p.exitValue() != 255; + } catch (InterruptedException e) { + hasRoot = false; + } + } catch (IOException e) { + hasRoot = false; + } + } + Log.d(TAG, "hasRootPermission(): hasRoot = " + hasRoot); + return hasRoot; + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7da1fa5..307d6a0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Sep 20 20:04:02 IST 2020 +#Mon Feb 22 15:56:06 IST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/settings.gradle b/settings.gradle index 9c8c501..33ba216 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ +include ':checkroot' include ':app' rootProject.name = "CameraIDs" \ No newline at end of file