Skip to content

Commit

Permalink
many changes, ready for 3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
mickstar committed May 9, 2021
1 parent ac3b8ed commit fd07e2f
Show file tree
Hide file tree
Showing 89 changed files with 1,051 additions and 478 deletions.
10 changes: 8 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
applicationId "com.mickstarify.zooforzotero"
minSdkVersion 21
targetSdkVersion 30
versionCode 43
versionName "2.9a"
versionCode 44
versionName "3.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "zotero_api_key", apikeyProperties['zotero_api_key'])
buildConfigField("String", "zotero_api_secret", apikeyProperties['zotero_api_secret'])
Expand Down Expand Up @@ -107,6 +107,12 @@ dependencies {
implementation 'com.google.dagger:dagger:2.28.3'
kapt 'com.google.dagger:dagger-compiler:2.28.3'

// for fab on main screen
implementation 'com.getbase:floatingactionbutton:1.10.1'

// barcode stuff
implementation 'com.google.android.gms:play-services-vision:20.1.3'

configurations {
all*.exclude group: 'xpp3', module: 'xpp3'
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

<application
android:name=".ZooForZoteroApplication"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mickstarify.zooforzotero.LibraryActivity

import com.mickstarify.zooforzotero.LibraryActivity.ViewModels.LibraryListViewModel
import com.mickstarify.zooforzotero.LibraryActivity.ViewModels.LibraryLoadingScreenViewModel
import com.mickstarify.zooforzotero.ZoteroAPI.Model.Note
import com.mickstarify.zooforzotero.ZoteroStorage.Database.Collection
import com.mickstarify.zooforzotero.ZoteroStorage.Database.GroupInfo
Expand All @@ -10,15 +11,17 @@ interface Contract {
interface View {
fun initUI()
fun createErrorAlert(title: String, message: String, onClick: () -> Unit)
fun showLoadingAnimation(showScreen: Boolean)

// fun showLoadingAnimation(showScreen: Boolean)
fun updateLibraryLoadingProgress(
progress: Int,
total: Int = -1,
message: String
)

fun addSharedCollection(groupInfo: GroupInfo)
fun hideLoadingAnimation()

// fun hideLoadingAnimation()
fun setTitle(title: String)
fun addNavigationEntry(collection: Collection, parent: String)
fun populateEntries(entries: List<ListEntry>)
Expand All @@ -38,9 +41,6 @@ interface Contract {
)

fun showNote(note: Note)

fun showBasicSyncAnimation()
fun hideBasicSyncAnimation()
fun highlightMenuItem(state: LibraryModelState)
fun showLoadingAlertDialog(message: String)
fun hideLoadingAlertDialog()
Expand All @@ -49,7 +49,7 @@ interface Contract {
interface Presenter {
fun createErrorAlert(title: String, message: String, onClick: () -> Unit)
fun receiveCollections(collections: List<Collection>)
fun setCollection(collectionName: String, isSubCollection: Boolean = false)
fun setCollection(collectionName: String, fromNavigationDrawer: Boolean = false)
fun selectItem(item: Item, longPress: Boolean = false)
fun requestLibraryRefresh()
fun showLibraryLoadingAnimation()
Expand Down Expand Up @@ -98,6 +98,7 @@ interface Contract {
fun hideLoadingAlertDialog()

var libraryListViewModel: LibraryListViewModel
var libraryLoadingViewModel: LibraryLoadingScreenViewModel
}

interface Model {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
package com.mickstarify.zooforzotero.LibraryActivity.Fragments

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.graphics.ImageFormat
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.media.ImageReader
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.google.android.gms.vision.Frame
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector
import com.mickstarify.zooforzotero.LibraryActivity.ViewModels.LibraryListViewModel
import com.mickstarify.zooforzotero.R

class BarcodeScanningScreen : Fragment() {

companion object {
fun newInstance() = BarcodeScanningScreen()
const val REQUEST_CAMERA = 1729
}

private lateinit var viewModel: LibraryListViewModel
lateinit var cameraView: SurfaceView

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.barcode_scanning_screen_fragment, container, false)
}

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.clear()
inflater.inflate(R.menu.barcode_scanning_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true)
viewModel = ViewModelProvider(requireActivity()).get(LibraryListViewModel::class.java)

cameraView = requireView().findViewById<SurfaceView>(R.id.cameraPreview)
cameraView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {}

override fun surfaceChanged(
holder: SurfaceHolder,
format: Int,
width: Int,
height: Int
) {
showCameraPreview(width, height)
}
})
}

override fun onStart() {
super.onStart()

if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA)
} else {
// start camera feed
}
}

// we will use this to determine the amount of frames in a row that we detect a barcode.
// this is done for accuracy reasons as using one frame was allowing the phone to misdetect
// barcodes.
var detectedInARow: Int = 0
var lastChange = 0L
var lastReadBarcodeNo: String = ""

var camera: CameraDevice? = null

fun showCameraPreview(width: Int, height: Int) {
val cameraView = requireView().findViewById<SurfaceView>(R.id.cameraPreview)

try {
val cameraBkgHandler = Handler()

val cameraManager =
requireActivity().getSystemService(Context.CAMERA_SERVICE) as CameraManager

cameraManager.cameraIdList.find {
val characteristics = cameraManager.getCameraCharacteristics(it)
val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)

return@find cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_BACK
}?.let {
val cameraStateCallback = object : CameraDevice.StateCallback() {
override fun onOpened(camera: CameraDevice) {
this@BarcodeScanningScreen.camera = camera
val barcodeDetector = BarcodeDetector.Builder(requireContext())
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build()

if (barcodeDetector.isOperational == false) {
Toast.makeText(
requireContext(),
"Failed to load barcode detector",
Toast.LENGTH_SHORT
).show()
stopScanning()
}

val imgReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1)
imgReader.setOnImageAvailableListener({ reader ->
val cameraImage = reader.acquireNextImage()
val buffer = cameraImage.planes.first().buffer
val bytes = ByteArray(buffer.capacity())
buffer.get(bytes)

val bitmap =
BitmapFactory.decodeByteArray(bytes, 0, bytes.count(), null)
val frameToProcess = Frame.Builder().setBitmap(bitmap).build()
val barcodeResults = barcodeDetector.detect(frameToProcess)
if (barcodeResults.size() > 0) {
// Scanned a barcode!
val result = barcodeResults.valueAt(0)
Log.d("zotero", "scanned ${result.rawValue}")
if (result.rawValue == lastReadBarcodeNo) {
detectedInARow++
} else {
detectedInARow = 0
lastReadBarcodeNo = result.rawValue
}

if (detectedInARow == 3) {
val barcodeNo = barcodeResults.valueAt(0).rawValue
viewModel.scannedBarcodeNumber(barcodeNo)
stopScanning()
}
} else {
}
cameraImage.close()

}, cameraBkgHandler)

val captureStateCallback = object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
val builder =
camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)

builder.addTarget(cameraView.holder.surface)
builder.addTarget(imgReader.surface)
session.setRepeatingRequest(builder.build(), null, null)
}

override fun onConfigureFailed(session: CameraCaptureSession) {
}
}

camera.createCaptureSession(
listOf(cameraView.holder.surface, imgReader.surface),
captureStateCallback,
cameraBkgHandler
)
}

override fun onClosed(camera: CameraDevice) {
}

override fun onDisconnected(camera: CameraDevice) {
}

override fun onError(camera: CameraDevice, error: Int) {
}
}

cameraManager.openCamera(it, cameraStateCallback, cameraBkgHandler)
return
}

// TODO: - No available camera found case

} catch (e: CameraAccessException) {
} catch (e: SecurityException) {
}
}

private fun stopScanning() {
this.camera?.close()
findNavController().navigateUp()
}

override fun onStop() {
super.onStop()
this.camera?.close()
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)

if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& requestCode == REQUEST_CAMERA
) {
showCameraPreview(cameraView.measuredWidth, cameraView.measuredHeight)
} else {
Toast.makeText(
requireContext(),
"This functionality requires the camera. Please provide the camera permission.",
Toast.LENGTH_SHORT
).show()
findNavController().navigateUp()
}
}
}
Loading

0 comments on commit fd07e2f

Please sign in to comment.