Using Zxing Library with Jetpack compose - zxing

I am trying to implement qr scanner using zxing library. For this, i have added a button on screen, and on click of it, i am launching scanner as below
Button(
onClick = {
val intentIntegrator = IntentIntegrator(context)
intentIntegrator.setPrompt(QrScanLabel)
intentIntegrator.setOrientationLocked(true)
intentIntegrator.initiateScan()
},
modifier = Modifier
.fillMaxWidth()
) {
Text(
text = QrScanLabel
)
}
but, it launches an intent, which expects onActivityResult method to get back the results. And Jetpack compose uses rememberLauncherForActivityResult like below
val intentLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) {
if (it.resultCode != RESULT_OK) {
return#rememberLauncherForActivityResult
}
...
}
but how do we integrate both things together here?

I make a provisional solution with same library:
Gradle dependencies:
implementation('com.journeyapps:zxing-android-embedded:4.1.0') { transitive = false }
implementation 'com.google.zxing:core:3.4.0'
My new Screen with jetpack compose and camera capture, that works for my app:
#Composable
fun AdminClubMembershipScanScreen(navController: NavHostController) {
val context = LocalContext.current
var scanFlag by remember {
mutableStateOf(false)
}
val compoundBarcodeView = remember {
CompoundBarcodeView(context).apply {
val capture = CaptureManager(context as Activity, this)
capture.initializeFromIntent(context.intent, null)
this.setStatusText("")
capture.decode()
this.decodeContinuous { result ->
if(scanFlag){
return#decodeContinuous
}
scanFlag = true
result.text?.let { barCodeOrQr->
//Do something and when you finish this something
//put scanFlag = false to scan another item
scanFlag = false
}
//If you don't put this scanFlag = false, it will never work again.
//you can put a delay over 2 seconds and then scanFlag = false to prevent multiple scanning
}
}
}
AndroidView(
modifier = Modifier,
factory = { compoundBarcodeView },
)
}

Since zxing-android-embedded:4.3.0 there is a ScanContract, which can be used directly from Compose:
val scanLauncher = rememberLauncherForActivityResult(
contract = ScanContract(),
onResult = { result -> Log.i(TAG, "scanned code: ${result.contents}") }
)
Button(onClick = { scanLauncher.launch(ScanOptions()) }) {
Text(text = "Scan barcode")
}

Addendum to the accepted answer
This answer dives into the issues commented on by #Bharat Kumar and #Jose Pose S
in the accepted answer.
I basically just implemented the accepted answer in my code and then added the following code just after the defining compundBarCodeView
DisposableEffect(key1 = "someKey" ){
compoundBarcodeView.resume()
onDispose {
compoundBarcodeView.pause()
}
}
this makes sure the scanner is only active while it is in the foreground and unbourdens our device.
TL;DR
In escence even after you scan a QR code successfully and leave the scanner screen, the barcodeview will "haunt" you by continuing to scan from the backstack. which you usually dont want. And even if you use a boolean flag to prevent the scanner from doing anything after the focus has switched away from the scanner it will still burden your processor and slow down your UI since there is still a process constantly decrypting hi-res images in the background.

I have a problem, I've the same code as you, but i don't know why it's showing me a black screen
Code AddProduct
#ExperimentalPermissionsApi
#Composable
fun AddProduct(
navController: NavController
) {
val context = LocalContext.current
var scanFlag by remember {
mutableStateOf(false)
}
val compoundBarcodeView = remember {
CompoundBarcodeView(context).apply {
val capture = CaptureManager(context as Activity, this)
capture.initializeFromIntent(context.intent, null)
this.setStatusText("")
capture.decode()
this.decodeContinuous { result ->
if(scanFlag){
return#decodeContinuous
}
scanFlag = true
result.text?.let { barCodeOrQr->
//Do something
}
scanFlag = false
}
}
}
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { compoundBarcodeView },
)
}

Related

Jetpack compose LazyColumn or Scrollable Column and IME padding for TextField doesn't work

I am trying to set a list if text fields, and when user set the focus on one text field at the bottom I expect that the user can see the appearing IME soft keyboard and the text field being padded according to the configuration set in my manifest file android:windowSoftInputMode="adjustPan", but it doesn't work the first time, it works only when some of the listed textfield have already a focus.
Behavior in video.
My code in onCreate method.
// Turn off the decor fitting system windows, which allows us to handle insets,
// including IME animations
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
// Provide WindowInsets to our content. We don't want to consume them, so that
// they keep being pass down the view hierarchy (since we're using fragments).
ProvideWindowInsets(consumeWindowInsets = false) {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background, modifier = Modifier.systemBarsPadding()) {
Column(modifier = Modifier.fillMaxHeight()) {
val list: List<#Composable () -> Unit> = (1..10).map {
{
Text(text = "$it")
Divider()
TextField(value = "", onValueChange = {}, modifier = Modifier.navigationBarsWithImePadding(),)
}
}
LazyColumn(modifier = Modifier.fillMaxSize().weight(1F)) {
itemsIndexed(list) { index, inputText ->
inputText()
}
}
}
}
}
}
}
If your problem is going on, check Insets for Jetpack Compose.
Only add this dependency:
implementation 'com.google.accompanist:accompanist-insets:{insetsVersion}'
and use Modifier.imePadding() or the custom solution:
#Composable
fun ImeAvoidingBox() {
val insets = LocalWindowInsets.current
val imeBottom = with(LocalDensity.current) { insets.ime.bottom.toDp() }
Box(Modifier.padding(bottom = imeBottom))
}

Scala Swing skips repaint of the Frame

I am currently working on an implementation of the game Othello in Scala and so far it's working pretty nicely. When implementing the GUI though (using Scala Swing) I've stumbled across an issue that I can't seem to fix.
When playing against the computer opponent the Frame seems to be repainted only when the bot is done making its move.
The game is also playable through the terminal and doing so updates the Frame properly every time regardless of the player configuration (Player vs Player or Player vs Computer). Also playing player vs player using the GUI exclusively presents no issues at all.
It might be an oversight on my behalf, but so far I am unable to find a solution and would greatly appreciate any help.
So far I have tried various combinations of revalidating and repainting the individual panels, adding and removing listeners, changing my implementation of the reactor pattern to the one provided by Scala Swing, adding Thread.sleep to see if there could be a scheduling conflict of sorts.
import java.awt.Color
import othello.controller.Controller
import javax.swing.ImageIcon
import javax.swing.border.LineBorder
import scala.swing.event.MouseClicked
import scala.swing.{BorderPanel, BoxPanel, Dimension, FlowPanel, GridPanel, Label, Orientation}
class TablePanel(controller: Controller) extends FlowPanel {
val sides = 32
val sidesColor: Color = Color.lightGray
val squareSize = 52
def tableSize: Int = controller.board.size
def edgeLength: Int = tableSize * squareSize
def rows: BoxPanel = new BoxPanel(Orientation.Vertical) {
background = sidesColor
preferredSize = new Dimension(sides, edgeLength)
contents += new Label {
preferredSize = new Dimension(sides, sides)
}
contents += new GridPanel(tableSize, 1) {
background = sidesColor
for { i <- 1 to rows } contents += new Label(s"$i")
}
}
def columns: GridPanel = new GridPanel(1, tableSize) {
background = sidesColor
preferredSize = new Dimension(edgeLength, sides)
for { i <- 0 until columns } contents += new Label(s"${(i + 65).toChar}")
}
def table: GridPanel = new GridPanel(tableSize, tableSize) {
background = new Color(10, 90, 10)
for {
col <- 0 until columns
row <- 0 until rows
} contents += square(col, row)
}
def square(row: Int, col: Int): Label = new Label {
border = new LineBorder(new Color(30, 30, 30, 140), 1)
preferredSize = new Dimension(squareSize, squareSize)
icon = controller.board.valueOf(col, row) match {
case -1 => new ImageIcon("resources/big_dot.png")
case 0 => new ImageIcon("resources/empty.png")
case 1 => new ImageIcon("resources/black_shadow.png")
case 2 => new ImageIcon("resources/white_shadow.png")
}
listenTo(mouse.clicks)
reactions += {
case _: MouseClicked =>
if (controller.options.contains((col, row))) controller.set(col, row)
else if (controller.board.gameOver) controller.newGame()
else controller.highlight()
}
}
def redraw(): Unit = {
contents.clear
contents += new BorderPanel {
add(rows, BorderPanel.Position.West)
add(new BoxPanel(Orientation.Vertical) {
contents += columns
contents += table
}, BorderPanel.Position.East)
}
repaint
}
}
import scala.swing._
import othello.controller._
import othello.util.Observer
import scala.swing.event.Key
class SwingGui(controller: Controller) extends Frame with Observer {
controller.add(this)
lazy val tablePanel = new TablePanel(controller)
lazy val mainFrame: MainFrame = new MainFrame {
title = "Othello"
menuBar = menus
contents = tablePanel
centerOnScreen
// peer.setAlwaysOnTop(true)
resizable = false
visible = true
}
def menus: MenuBar = new MenuBar {
contents += new Menu("File") {
mnemonic = Key.F
contents += new MenuItem(Action("New Game") {
controller.newGame()
})
contents += new MenuItem(Action("Quit") {
controller.exit()
})
}
contents += new Menu("Edit") {
mnemonic = Key.E
contents += new MenuItem(Action("Undo") {
controller.undo()
})
contents += new MenuItem(Action("Redo") {
controller.redo()
})
}
contents += new Menu("Options") {
mnemonic = Key.O
contents += new MenuItem(Action("Highlight possible moves") {
controller.highlight()
})
contents += new MenuItem(Action("Reduce board size") {
controller.resizeBoard("-")
})
contents += new MenuItem(Action("Increase board size") {
controller.resizeBoard("+")
})
contents += new MenuItem(Action("Reset board size") {
controller.resizeBoard(".")
})
contents += new Menu("Game mode") {
contents += new MenuItem(Action("Player vs. Computer") {
controller.setupPlayers("1")
})
contents += new MenuItem(Action("Player vs. Player") {
controller.setupPlayers("2")
})
}
}
}
def update: Boolean = {
tablePanel.redraw()
mainFrame.pack
mainFrame.centerOnScreen
mainFrame.repaint
true
}
}
The expected behavior is a repainted Frame on every turn. The actual result is the Frame only being repainted after the opponent made a move. It only happens when playing player vs bot exclusively through clicking the UI.
I don't think the problem is in the code you have shown, but I would bet that you are blocking the "event dispatch thread" (the UI thread) with your AI computation for the computer player.
In a Swing app, there is a special thread called the "event dispatch thread" that is responsible for handling messages from the O/S including dealing with repaint messages. All UI event handlers will be invoked on this thread. If you use that thread to do any computations which take a long time (such as a computer move in a game like this), any UI updates will be blocked until the thread becomes free.
This tutorial has more info: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
You need to move the AI onto a background thread and release the event dispatch thread to deal with repaints. This might be tricky to implement if you are not familiar with multi-threaded programs! Good luck.
As Rich suggested, the solution was concurrency. Making the search algorithm a Future-type solved the issue for now. It might not be the cleanest implementation as it is the first time I'm using Future, but this is what it looks like now:
def selectAndSet(): Future[_] = Future(if (!board.gameOver && player.isBot) {
new MoveSelector(this).select() match {
case Success(square) => set(square)
case _ => omitPlayer()
}
selectAndSet()
})(ExecutionContext.global)

Extension event loop in Gnome 3.10 vs 3.14

I wrote this accessibility extension:
https://extensions.gnome.org/extension/975/keyboard-modifiers-status/
https://github.com/sneetsher/Keyboard-Modifiers-Status
Which works as supposed in Gnome Shell v3.14 & v3.16 but not in v3.10. It shows the only the initial keyboard modifiers state after i
restarted it and never update it after that.
Here the full code:
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
const Gdk = imports.gi.Gdk
let button, label, keymap;
function _update() {
let symbols = "⇧⇬⋀⌥①◆⌘⎇";
let state = keymap.get_modifier_state();
label.text = " ";
for (var i=0; i<=8; i++ ) {
if (state & 1<<i) {
label.text += symbols[i];
} else {
//label.text += "";
}
}
label.text += " ";
}
function init() {
button = new St.Bin({ style_class: 'panel-button',
reactive: false,
can_focus: false,
x_fill: true,
y_fill: false,
track_hover: false });
label = new St.Label({ style_class: "state-label", text: "" });
button.set_child(label);
keymap = Gdk.Keymap.get_default();
keymap.connect('state_changed', _update );
Mainloop.timeout_add(1000, _update );
}
function enable() {
Main.panel._rightBox.insert_child_at_index(button, 0);
}
function disable() {
Main.panel._rightBox.remove_child(button);
}
Trying to debug, I modified the code to show (state label + a counter)
let c,button, label, keymap;
c=0;
function _update() {
Gtk.main_iteration_do(false);
c++;
let symbols = "⇧⇬⋀⌥①◆⌘⎇";
//let keymap = Gdk.Keymap.get_default()
let state = keymap.get_modifier_state();
label.text = " ";
for (var i=0; i<=8; i++ ) {
if (state & 1<<i) {
label.text += symbols[i];
} else {
//label.text += "";
}
}
label.text += " "+c+" ";
return true;
}
I can confirm these:
keymap.connect('state_changed', _update ); this signal is never raised
timeout callback works well
label is updated and show the initial state & the incrementing counter
So I think there is something with event loop as it does not pull
state update or does not process its events.
Could you please point me to way to fix this and what's the difference
between v3.10 & v3.14?
Assuming that commenting out the definition of keymap was intentional, check that it is still assigned elsewhere in your code. Have you tried using a -(minus) rather than a _(underscore)? Most events use the former in JS space, rather than the latter and this has been the problem for me when in several cases where I was attaching events to changing the active workspace, where the back-end for Meta.Display fires workspace_switched, the GJS space connects through workspace-switched and there are a lot more examples there.
For official documentation, including the correct event, property and function names for within GJS space, refer to GNOME DevDocs I don't know when it became official, but they state that it is here

Update OpenLayers popup

I am trying to update some popups in my map but I am not able to do that.
Firstly I create some markers, and with the next code, I create a popup associated to them. One popup for each marker:
popFeature = new OpenLayers.Feature(markers, location);
popFeature.closeBox = true;
popFeature.popupClass = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {
'autoSize': true
});
popFeature.data.popupContentHTML = "hello";
popFeature.data.overflow = (false) ? "auto" : "hidden";
var markerClick = function (evt) {
if (this.popup == null) {
this.popup = this.createPopup(this.closeBox);
map.addPopup(this.popup);
this.popup.show();
} else {
this.popup.toggle();
}
currentPopup = this.popup;
OpenLayers.Event.stop(evt);
};
mark.events.register("mousedown", popFeature, markerClick);
After that, I add the new marker to my marker layer.
Everything is fine until here, but, I want to update the popupcontentHTML some time later and I don't know how I can access to that value.
I read OL API but I don't understand how to get it. I am lost about features, events, extensions...
I want to know if I can access to that property and write other word.
I answer myself, maybe it helps other people in future:
for(i = 0; i < map.popups.length; i++){
if(map.popups[i].lonlat.lon == marker.lonlat.lon){
map.popups[i].setContentHTML("new content");
}
}
Content will be refreshed at the moment.

Scala Swing Newbie

Im trying to create an login window for an app im doing. I have searched all day for an example but I cant seem to find anything that helps. My basic structure is as follows:
// App.scala
object App extends SimpleSwingApplication {
val ui = new BorderPanel {
//content
}
def top = new MainFrame {
title = "title"
contents = ui
}
}
So whats the strategy to create a login box without the mainframe showing and closing it after login and displaying the mainframe. thanks
Here is working example. Took it from one of my projects and adjusted a little bit for you:
import swing._
import scala.swing.BorderPanel.Position._
object App extends SimpleSwingApplication {
val ui = new BorderPanel {
//content
}
def top = new MainFrame {
title = "title"
contents = ui
}
val auth = new LoginDialog().auth.getOrElse(throw new IllegalStateException("You should login!!!"))
}
case class Auth(userName: String, password: String)
class LoginDialog extends Dialog {
var auth: Option[Auth] = None
val userName = new TextField
val password = new PasswordField
title = "Login"
modal = true
contents = new BorderPanel {
layout(new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(5,5,5,5)
contents += new Label("User Name:")
contents += userName
contents += new Label("Password:")
contents += password
}) = Center
layout(new FlowPanel(FlowPanel.Alignment.Right)(
Button("Login") {
if (makeLogin()) {
auth = Some(Auth(userName.text, password.text))
close()
} else {
Dialog.showMessage(this, "Wrong username or password!", "Login Error", Dialog.Message.Error)
}
}
)) = South
}
def makeLogin() = true // here comes you login logic
centerOnScreen()
open()
}
As you can see I'm generally using modal dialog, so it will block during application initialization. There 2 outcomes: either user makes successful login and sees your main frame or he closes login dialog and IllegalStateException would be thrown.