Commit f7a1ee64 authored by Martin LOISEAU's avatar Martin LOISEAU 🎄
Browse files

Add most QRLudo's QR-related classes

parent 81538578
......@@ -20,7 +20,6 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
......@@ -35,6 +34,30 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.zxing:core:3.2.1'
implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
//noinspection GradleCompatible
implementation 'com.android.support:appcompat-v7:21.0.3'
// from QRLudo
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestImplementation 'com.android.support:support-annotations:25.3.1'
implementation 'pub.devrel:easypermissions:1.0.1'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
//noinspection GradleCompatible
implementation 'com.google.android.gms:play-services-vision:10.2.6'
//noinspection GradleCompatible
implementation 'com.android.support:design:25.3.1'
implementation 'org.jsoup:jsoup:1.12.1'
testImplementation 'junit:junit:4.12'
implementation 'com.google.android.gms:play-services-auth:10.2.6'
implementation('com.google.api-client:google-api-client-android:1.23.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.apis:google-api-services-drive:v3-rev90-1.23.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation 'com.google.code.gson:gson:2.8.2'
}
package com.example.qrquest;
import android.Manifest;
import android.app.Dialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import com.example.qrquest.QR.handling.QRCodeDefaultDetectionModeStrategy;
import com.example.qrquest.QR.handling.QRCodeDetectionModeStrategy;
import com.example.qrquest.QR.model.QRCode;
import com.example.qrquest.QR.model.QRCodeCollection;
import com.example.qrquest.QR.model.QRContent;
import com.example.qrquest.QR.model.QRText;
import com.example.qrquest.utils.OnSwipeTouchListener;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.gms.common.GoogleApiAvailability;
import androidx.annotation.ContentView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.Environment;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
......@@ -33,6 +49,41 @@ public class MainActivity extends AppCompatActivity {
MediaPlayer mp;
TextToSpeech tts;
SpeechRecognizer sr;
GoogleApiAvailability m_gga;
// attributes from QRLudo:
CameraSource m_cameraSource;
SurfaceView m_cameraView;
private int cameraState;
private final int CAMERA_INACTIVE_STATE = 30;
private final int CAMERA_DETECTING_STATE = 40;
private int m_detectionProgress;
public final static int NO_QR_DETECTED = 50;
public final static int FIRST_QR_DETECTED = 60;
public final static int MULTIPLE_QR_DETECTED = 100;
private boolean m_isApplicationDetecting;
private boolean m_marshmallowOrHigher;
private boolean m_lollipopOrHigher;
private QRCodeCollection m_detectedQRCodes;
/**
* List of the current QRContent (url or text) which have been extracted from the detected QRCodes
*/
private List<QRContent> m_currentReading;
/**
* Strategy in charge of the behaviour of detecting the QRCodes, starting the reading and handling the user actions
* It depends on the type of QRCodes that the user is currently detecting
*/
private QRCodeDetectionModeStrategy m_currentDetectionModeStrategy;
/**
* Current position in m_currentReading
*/
private int m_currentPos;
/**
* The main layout where all happens
*/
private LinearLayout m_mainLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -46,6 +97,28 @@ public class MainActivity extends AppCompatActivity {
}
});
// from QRLudo
m_gga = GoogleApiAvailability.getInstance();
int status = m_gga.isGooglePlayServicesAvailable(getApplicationContext());
if(status != ConnectionResult.SUCCESS) {
int GOOGLE_SERVICES_REQUEST = 500;
Dialog errorDia = m_gga.getErrorDialog(this,status, GOOGLE_SERVICES_REQUEST);
errorDia.show();
}
initializeAttributes();
if(savedInstanceState != null) {
cameraState = savedInstanceState.getInt("STATE");
switch (cameraState) {
case CAMERA_DETECTING_STATE: {
startDetection();
break;
}
}
}
initializeListeners();
// end of from QRLudo
//setRecognitionListener(android.speech.RecognitionListener)
FloatingActionButton photo = findViewById(R.id.photo);
......@@ -92,6 +165,70 @@ public class MainActivity extends AppCompatActivity {
});
}
// From QRLudo
/**
* Starts the detection of new QRCodes
*/
private void startDetection() {
final AppCompatActivity activity = this;
activity.runOnUiThread(new Runnable() {
public void run() {
if(true /*m_ttsready*/) {
m_cameraView.setVisibility(View.VISIBLE);
m_isApplicationDetecting = true;
}else{
cameraState = CAMERA_DETECTING_STATE;
}
}
});
}
private void initializeAttributes() {
m_cameraView = (SurfaceView) findViewById(R.id.camera_view);
m_cameraView.setVisibility(View.INVISIBLE);
PackageManager manager = getPackageManager();
m_mainLayout = (LinearLayout) findViewById(R.id.main_layout);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
cameraState = CAMERA_INACTIVE_STATE;
m_detectionProgress = NO_QR_DETECTED;
m_marshmallowOrHigher = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M;
m_lollipopOrHigher = android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ;
m_detectedQRCodes = new QRCodeCollection();
m_currentReading = new ArrayList<>();
m_currentDetectionModeStrategy = new QRCodeDefaultDetectionModeStrategy(this);
setUpDetector();
}
private void initializeListeners() {
m_mainLayout.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) {
/**
* Relying on m_currentDetectionModeStrategy to manage the swipe top
*/
public void onSwipeTop() {
m_currentDetectionModeStrategy.onSwipeTop();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
......@@ -163,4 +300,268 @@ public class MainActivity extends AppCompatActivity {
}
}
}
/**
* Unregisters the MainActivity as a listener of the current QRFile if necessary
* Called when the user swipes right or left
*/
public void unregisterToQRFile(){
QRContent currentContent = m_currentReading.get(m_currentPos);
// if (currentContent != null && currentContent instanceof QRFile){
// ((QRFile) currentContent).unregisterAsDownloadListener();
//}
}
private void setUpDetector() {
/*
The barcode detector
*/
BarcodeDetector detector = new BarcodeDetector.Builder(getApplicationContext())
.setBarcodeFormats(Barcode.DATA_MATRIX | Barcode.QR_CODE)
.build();
if (!detector.isOperational()) {
Log.e("DETECTOR","Could not set up detector.");
return;
}
m_cameraSource = new CameraSource
.Builder(this, detector)
.setRequestedPreviewSize(640, 480)
.setAutoFocusEnabled(true)
.build();
m_cameraView = (SurfaceView) findViewById(R.id.camera_view);
m_cameraView.setVisibility(View.INVISIBLE);
m_cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
m_cameraSource.start(m_cameraView.getHolder());
cameraState = CAMERA_DETECTING_STATE;
Log.d("CAMERA_START", "Camera started");
} catch(SecurityException se)
{
Log.e("CAMERA SECURITY", se.getMessage());
}
catch (Exception e)
{
Log.e("CAMERA SOURCE", e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
if(cameraState == CAMERA_DETECTING_STATE) {
cameraState = CAMERA_INACTIVE_STATE;
}
m_cameraSource.stop();
} catch (Exception e)
{
Log.e("CAMERA SOURCE", e.getMessage());
}
}
});
/*
The processor of the detector, where the events from the detector are handled
*/
Detector.Processor<Barcode> detector_processor = new Detector.Processor<Barcode>() {
ArrayList<QRCode> listQR = new ArrayList<>();
@Override
public void release() {
}
/**
* Method called when QRCodes have been detected by the camera
* @param detections
*/
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
}
};
detector.setProcessor(detector_processor);
}
/**
* Called when a single QRCode has been detected.
* Reads the first content of the QRCode and allows the user to hear the others content by swiping the screen
*/
public void singleReading(){
//Getting the first detected QRCode only
m_currentReading = m_detectedQRCodes.getContentFirstQR();
readCurrentContent();
}
/**
* Called in case of multiple reading of QR Codes which don't belong to a family
* Also called during a family reading if QRCodes not belonging to a family have been detected first
* In those cases, a QR code is already printed/told
*/
public void classicMultipleReading(){
//Fetching the content of all the detected QR but the first because it has already been read by singleReading()
m_currentReading.addAll(m_detectedQRCodes.getContentAllQRButFirst());
}
/**
* Called in case of detection of QRCodeEnsemble
* Can be called by QRCodeEnsemble at the end of a multiple detection or by the MultipleDetectionTimer if only one QRCodeEnsemble has been detected
*/
/**
* Detects the type of the current content and calls the methods adequately
* If the current content is a QRFile, tests if it has already been downloaded. If not, waits for it
*/
public void readCurrentContent(){
QRContent currentContent = m_currentReading.get(m_currentPos);
if (currentContent instanceof QRText){
// sayTextContent();
}
// else if (currentContent instanceof QRFile){
//If the file is already downloaded, playing the m_mediaPlayer
// if (((QRFile) currentContent).isFileInMemory()){
// playCurrentSoundFromFile();
// }
//The file is not downloaded
else {
/*
//Registering as listener of the QRFile. The method onQRFileDownloadComplete() will then be called when the downloading is over.
((QRFile) currentContent).registerAsDownloadListener(this);
if (isOnline()) {
toSpeech("Téléchargement en cours", TextToSpeech.QUEUE_ADD);
} else {
toSpeech("Pas de connexion internet, téléchargement impossible.", TextToSpeech.QUEUE_ADD);
}
*/
// }
}
}
public void readAllContent(){
for(m_currentPos = 0; m_currentPos<m_currentReading.size();m_currentPos++){
readCurrentContent();
}
}
/**
* Plays the current QRFile
*/
public void playCurrentSoundFromFile(){
Log.i("%%%%APRESTELECHARGEMENT", "Lecture directe du fichier");
QRContent currentContent = m_currentReading.get(m_currentPos);
// if (currentContent instanceof QRFile) {
//If the file is already downloaded, playing the m_mediaPlayer
// if (((QRFile) currentContent).isFileInMemory()) {
// String pathToFile = ((QRFile) currentContent).getDownloadedFilePath();
/*
try {
m_mediaPlayer.stop();
m_mediaPlayer = new MediaPlayer();
m_mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
m_mediaPlayer.setDataSource(pathToFile);
m_mediaPlayer.prepare();
m_mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}*/
// }
// }
}
/**
* Stops the detection : the application is no longer trying to detect QRCodes
* The cameraView becomes white
*/
public void stopDetection() {
// ToneGeneratorSingleton.getInstance().endOfDetectionTone();
final AppCompatActivity activity = this;
activity.runOnUiThread(new Runnable() {
public void run() {
if(true/*m_ttsready*/) {
m_cameraView.setVisibility(View.INVISIBLE);
m_isApplicationDetecting = false;
}else{
cameraState = CAMERA_INACTIVE_STATE;
}
}
});
}
/* ************************************************************************************** */
/* Getters and setters of the fields. Used by the subclasses of QRCodeDetectionModeStrategy */
/* ************************************************************************************** */
public void setDetectionProgress(int state){
m_detectionProgress = state;
}
public int getDetectionProgress(){
return m_detectionProgress;
}
public QRCodeCollection getDetectedQRCodes(){
return m_detectedQRCodes;
}
public void setDetectionStrategy(QRCodeDetectionModeStrategy strategy){
m_currentDetectionModeStrategy = strategy;
}
public boolean isApplicationDetecting(){
return m_isApplicationDetecting;
}
public int getCurrentPos(){
return m_currentPos;
}
public void incrementCurrentPos(){
m_currentPos++;
}
public void decrementCurrentPos(){
m_currentPos--;
}
public void setCurrentReading(List<QRContent> contents, int currentPos){
m_currentReading = contents;
m_currentPos = currentPos;
}
public int getContentSize(){
return m_currentReading.size();
}
}
package com.example.qrquest.QR.handling;
import android.util.Log;
import com.example.qrquest.QR.model.FileJson;
import com.example.qrquest.QR.model.QRCode;
import com.example.qrquest.QR.model.QRCodeAtomique;
import com.example.qrquest.QR.model.QRCodeEnsemble;
import com.example.qrquest.QR.model.QrCodeJson;
import com.example.qrquest.exceptions.UnhandledQRException;
import com.example.qrquest.exceptions.UnsupportedQRException;
import com.example.qrquest.utils.DecompressionJSON;
import com.example.qrquest.utils.JSONDownloader;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.LinkedTreeMap;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
/**
* Created by Jules Leguy on 20/01/18.
* Modified by Florian Lherbeil
* Builds a QRCode object from a Json object
*/
public class QRCodeBuilder {
public static QRCode build(String dataQR, int current_version) throws UnhandledQRException, UnsupportedQRException {
// On stocke la valeur brute initiale pour pouvoir effetuer la détection multiple
final String rawvalue = dataQR;
String result;
// On vérfie si la chaine est encodée en base64
if (dataQR.matches("^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$")) {
// Début de la décompression
try {
dataQR = DecompressionJSON.decompresser(dataQR);
} catch (IOException e) {
e.printStackTrace();
}
}
boolean qrIsWeb = false;
// Si la chaine trouvée n'est pas compressée et qu'elle n'est pas du json, c'est alors simplement du texte
// On rajoute le texte dans une chaine json comportement les attributs type et data
// Le texte est alors traité comme un qrcode atomique mais avec un site web actif
if (!dataQR.startsWith("{")) {
dataQR = "{\"type\"=\"unique\",\"data\"=[\"" + dataQR + "\"]}";
qrIsWeb = true;
}
Log.v("data_qr", dataQR);
Gson gson = new GsonBuilder().create();
QrCodeJson code = gson.fromJson(dataQR, QrCodeJson.class);
Log.i("build","Miss Build : "+code.getType());
if(code.getVersion()>current_version){
throw new UnsupportedQRException("Ce QRCode ne peut pas être lu par cette application, veuillez mettre à jour QRLudo ou QRLudoGénérator");
}
// Si le json est trop gros et est stocké sur le drive dans un fichier texte
// Manque tests avec un qrcode réel
// LinkedTreeMap est la classe qui est instanciée lorsque GSON trouve du JSON
if ((code.getData().size() > 0) && (code.getData().get(0) instanceof LinkedTreeMap)) {
FileJson file = QRCode.createJsonFile((LinkedTreeMap) code.getData().get(0));
if (file.getType().equalsIgnoreCase("json")) {
JSONDownloader downloader = new JSONDownloader(file.getUrl());
downloader.execute();
try {
result = downloader.get();
System.out.println("result : "+result);
dataQR = DecompressionJSON.decompresser(result);
Gson gsonResult = new GsonBuilder().create();
code = gsonResult.fromJson(dataQR, QrCodeJson.class);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (code.getType().equalsIgnoreCase("atomique")||code.getType().equalsIgnoreCase("unique")||code.getType().equalsIgnoreCase("xl")) {