From 29c21aa923ad03f69077b693fbf827297fc2dc36 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 27 Dec 2019 17:01:05 -0500 Subject: [PATCH 1/6] Added notification service using Firebase. App should now display notifications when it is in the foreground. --- .gitignore | 2 +- .idea/misc.xml | 2 +- app/build.gradle | 11 ++++- app/google-services.json | 48 ------------------- .../spartahack_android/FAQActivity.kt | 4 +- .../spartahack_android/MainActivity.kt | 18 ++++++- .../spartahack_android/QRScannerActivity.kt | 22 +++++++-- .../scripts/FAQAsyncTask.kt | 7 +-- .../scripts/faq_activity.kt | 30 +++++++++++- .../tools/FirebaseNotification.kt | 34 ++++++++++--- app/src/main/res/layout/qr_view.xml | 26 ++++------ app/src/main/res/values/strings.xml | 8 ++-- build.gradle | 2 +- 13 files changed, 121 insertions(+), 93 deletions(-) delete mode 100644 app/google-services.json diff --git a/.gitignore b/.gitignore index 2c83b4d..c38d84b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ /build /captures .externalNativeBuild -/google-services.json \ No newline at end of file +app/google-services.json \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0d45e8d..f5c6d9e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8b785a2..3e546a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { @@ -36,8 +40,11 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-RC' implementation 'com.google.firebase:firebase-analytics:17.2.1' - implementation 'com.google.firebase:firebase-messaging:20.0.1' - implementation 'com.google.android.gms:play-services-vision:19.0.0' + implementation 'com.google.firebase:firebase-messaging:20.1.0' + implementation 'com.google.firebase:firebase-ml-vision:24.0.1' + implementation 'com.google.firebase:firebase-ml-vision-barcode-model:16.0.2' + implementation "androidx.camera:camera-core:1.0.0-alpha06" // DO NOT UPDATE! + implementation "androidx.camera:camera-camera2:1.0.0-alpha06" // DO NOT UPDATE! } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index 7cfb638..0000000 --- a/app/google-services.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "project_info": { - "project_number": "160819321562", - "firebase_url": "https://spartahack-mobile.firebaseio.com", - "project_id": "spartahack-mobile", - "storage_bucket": "spartahack-mobile.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:160819321562:android:3a4f407f6b9047639e63e3", - "android_client_info": { - "package_name": "com.spartahack.spartahack_android" - } - }, - "oauth_client": [ - { - "client_id": "160819321562-onbv93bt6tg7ipu83n5murnld4agugdd.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.spartahack.spartahack_android", - "certificate_hash": "9a7d7c38be9bd47671c16032e563b3d2f13289b0" - } - }, - { - "client_id": "160819321562-las6hdoqe26ghvrc88pedq1t9c6leakh.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyBYCFej1s7GOCC9wzhQfQH58kwM_wj--4Y" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "160819321562-las6hdoqe26ghvrc88pedq1t9c6leakh.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt index 8a8d168..7dc55f5 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt @@ -13,7 +13,7 @@ import android.view.Menu import androidx.core.text.HtmlCompat import kotlinx.coroutines.* import kotlinx.android.synthetic.main.faq_view.* -import com.spartahack.spartahack_android.scripts.faqMain +import com.spartahack.spartahack_android.scripts.faqMainSuspend class FAQActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { @@ -35,7 +35,7 @@ class FAQActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelected navView.setNavigationItemSelectedListener(this) // Makes the API call and sends the data to the activity. - val displayString = GlobalScope.async { faqMain() } + val displayString = GlobalScope.async { faqMainSuspend() } runBlocking { faqTextView.text = HtmlCompat.fromHtml(displayString.await(), 0) } } diff --git a/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt index 9c08259..5283bfd 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt @@ -1,6 +1,9 @@ package com.spartahack.spartahack_android +import android.app.NotificationChannel +import android.app.NotificationManager import android.content.Intent +import android.os.Build import android.os.Bundle import androidx.core.view.GravityCompat import androidx.appcompat.app.ActionBarDrawerToggle @@ -32,7 +35,20 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte navView.setNavigationItemSelectedListener(this) - val END_DATE = 1580468400000 // Should be the date of the event + // Create the events notification channel. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val CHANNEL_ID = getString(R.string.events_id) + val name = getString(R.string.events_name) + val descriptionText = getString(R.string.events_description) + val importance = NotificationManager.IMPORTANCE_DEFAULT + val eventsChannel = NotificationChannel(CHANNEL_ID, name, importance) + eventsChannel.description = descriptionText + // Register the channel with the system. + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(eventsChannel) + } + + val END_DATE = 1580468400000 // Should be the date of the event in milliseconds val currentDate = currentTimeMillis() val endTimeMills = END_DATE - currentDate val timer = Timer(endTimeMills, 1000, countdownTimer) diff --git a/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt index a3f5752..976b811 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt @@ -1,7 +1,9 @@ package com.spartahack.spartahack_android import android.content.Intent +import android.graphics.Matrix import android.os.Bundle +import android.util.Size import androidx.core.view.GravityCompat import androidx.appcompat.app.ActionBarDrawerToggle import android.view.MenuItem @@ -9,10 +11,16 @@ import androidx.drawerlayout.widget.DrawerLayout import com.google.android.material.navigation.NavigationView import androidx.appcompat.app.AppCompatActivity import android.view.Menu +import android.view.Surface +import android.view.ViewGroup import androidx.appcompat.widget.Toolbar -import com.spartahack.spartahack_android.tools.Timer -import kotlinx.android.synthetic.main.app_bar_main.* -import java.lang.System.currentTimeMillis +import androidx.camera.core.CameraX +import androidx.camera.core.Preview +import androidx.camera.core.PreviewConfig +import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode +import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcodeDetectorOptions +import kotlinx.android.synthetic.main.qr_view.* + class QRScannerActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { @@ -26,12 +34,18 @@ class QRScannerActivity : AppCompatActivity(), NavigationView.OnNavigationItemSe val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) val navView: NavigationView = findViewById(R.id.nav_view) val toggle = ActionBarDrawerToggle( - this, drawerLayout, R.string.navigation_drawer_open, R.string.navigation_drawer_close) + this, drawerLayout, R.string.navigation_drawer_open, R.string.navigation_drawer_close + ) drawerLayout.addDrawerListener(toggle) toggle.syncState() navView.setNavigationItemSelectedListener(this) + val options = FirebaseVisionBarcodeDetectorOptions.Builder() + .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE) + .build() + + val viewFinder = view_finder } override fun onBackPressed() { diff --git a/app/src/main/java/com/spartahack/spartahack_android/scripts/FAQAsyncTask.kt b/app/src/main/java/com/spartahack/spartahack_android/scripts/FAQAsyncTask.kt index 15180e9..7fb3729 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/scripts/FAQAsyncTask.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/scripts/FAQAsyncTask.kt @@ -3,19 +3,20 @@ package com.spartahack.spartahack_android.scripts import android.os.AsyncTask import android.util.Log import android.widget.TextView +import androidx.core.text.HtmlCompat class FAQAsyncTask(val textView: TextView) : AsyncTask(){ override fun doInBackground(vararg p0: Void?): String { Log.i("FAQAsync", "doInBackground") - val dispText = ""//faqMain() + val dispText = faqMain() publishProgress(dispText) return "" } - override fun onProgressUpdate(vararg values: String?) { + override fun onProgressUpdate(vararg values: String) { Log.i("FAQAsync", "onProgressUpdate") - textView.text = values[0] + textView.text = HtmlCompat.fromHtml(values[0], 0) super.onProgressUpdate(*values) } } \ No newline at end of file diff --git a/app/src/main/java/com/spartahack/spartahack_android/scripts/faq_activity.kt b/app/src/main/java/com/spartahack/spartahack_android/scripts/faq_activity.kt index 0cfc9f1..1356daa 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/scripts/faq_activity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/scripts/faq_activity.kt @@ -44,7 +44,7 @@ fun getAnswer(str: String): String { } // getAnswer. -suspend fun faqMain(): String = withContext(Dispatchers.Default){ +suspend fun faqMainSuspend(): String = withContext(Dispatchers.Default){ /** The main structure of the script. Uses getQuestion and getAnswer. */ // Makes a call to the api to get the FAQ information as a raw string and splits the raw FAQ @@ -69,4 +69,30 @@ suspend fun faqMain(): String = withContext(Dispatchers.Default){ return@withContext displayStr -} // faqMain. \ No newline at end of file +} // faqMainSuspend. + +fun faqMain(): String{ + /** The main structure of the script. Uses getQuestion and getAnswer. */ + + // Makes a call to the api to get the FAQ information as a raw string and splits the raw FAQ + // string into a list. + var faqRawStr = APICall("faqs").sendGet() + faqRawStr = faqRawStr.removeRange(0, 1) + val faqList = faqRawStr.split("},") + + var displayStr = "" + + // Takes every entry in the FAQ list, then formats it in such a way that it is easy to display. + for (i in faqList) { + val question = getQuestion(i) + val answer = getAnswer(i) + + // Does not make an addition to the display string if the current answer is + // "What are exfdsact nufgddmbers?" + if (answer != "skip"){ + displayStr += ("

" + question + "

\n" + "

" + answer + "

\n") + } + } + + return displayStr +} // faqMain \ No newline at end of file diff --git a/app/src/main/java/com/spartahack/spartahack_android/tools/FirebaseNotification.kt b/app/src/main/java/com/spartahack/spartahack_android/tools/FirebaseNotification.kt index c91227f..779a3e4 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/tools/FirebaseNotification.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/tools/FirebaseNotification.kt @@ -1,26 +1,46 @@ package com.spartahack.spartahack_android.tools -//import android.app.Notification -//import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import com.spartahack.spartahack_android.R +import com.spartahack.spartahack_android.ScheduleActivity class FirebaseNotification : FirebaseMessagingService() { /* This class extends FirebaseMessagingService to allow the app to receive notifications from * our Firebase server. This class is only necessary because it would probably be a good idea to * display notifications to our users even when they are in the app, so that they don't miss * special activity notifications. This class is unnecessary if it is decided that foreground - * notifications are not needed.*/ + * notifications are not needed. */ + + private val CHANNEL_ID = getString(R.string.events_id) + private val intent = Intent(this, ScheduleActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + private val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0) override fun onMessageReceived(message: RemoteMessage) { - /* Retrieves the message from Firebase and creates a notification using the built-in - Notification.Builder.*/ - /*val notificationBuilder = Notification.Builder() + // Retrieves the message from Firebase and creates a notification using the built-in + // Notification Builder. + val notificationBuilder = NotificationCompat.Builder(applicationContext, CHANNEL_ID) // ?. is Kotlin's way of checking for NULL (message.notification can return NULL) .setContentTitle(message.notification?.title) .setContentText(message.notification?.body) - .build()*/ + .setContentIntent(pendingIntent) + .setAutoCancel(true) + val notificationId = 1 // I have no idea what this is supposed to be. + + with(NotificationManagerCompat.from(this)) { + // notificationId is a unique int for each notification that you must define + notify(notificationId, notificationBuilder.build()) + } + } + override fun onDeletedMessages() { + super.onDeletedMessages() } } \ No newline at end of file diff --git a/app/src/main/res/layout/qr_view.xml b/app/src/main/res/layout/qr_view.xml index f2e3802..e6e245d 100644 --- a/app/src/main/res/layout/qr_view.xml +++ b/app/src/main/res/layout/qr_view.xml @@ -5,20 +5,14 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - app:layout_behavior="@string/appbar_scrolling_view_behavior" - tools:showIn="@layout/app_bar_faq" - tools:context=".FAQActivity" - android:id="@+id/faq_constraits"> - - - - - + tools:context=".QRScannerActivity" + android:id="@+id/QR_constraints"> + \ 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 1347bfd..75ab8f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,9 +14,7 @@ FAQ view - Login - Sign Up - Username - Password - or + EVENT_ID + Events + Used for alerting the user for meals, tech talks, etc. diff --git a/build.gradle b/build.gradle index 763feb5..187ed81 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.3' // NOTE: Do not place your application dependencies here; they belong From 257c5d3144e7d3cdc11d3e2e2385f515343e8d4a Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 30 Dec 2019 18:28:24 -0500 Subject: [PATCH 2/6] QR scanner is ready to test. --- .idea/inspectionProfiles/Project_Default.xml | 8 + app/build.gradle | 5 +- app/src/main/AndroidManifest.xml | 2 + .../spartahack_android/QRScannerActivity.kt | 189 ++++++++++++++++-- .../spartahack_android/tools/CameraPreview.kt | 44 ++++ .../spartahack_android/tools/EasyCamera.kt | 45 +++++ .../spartahack_android/tools/ImageAnalyzer.kt | 64 ++++++ app/src/main/res/layout/qr_view.xml | 27 ++- build.gradle | 3 +- 9 files changed, 362 insertions(+), 25 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 app/src/main/java/com/spartahack/spartahack_android/tools/CameraPreview.kt create mode 100644 app/src/main/java/com/spartahack/spartahack_android/tools/EasyCamera.kt create mode 100644 app/src/main/java/com/spartahack/spartahack_android/tools/ImageAnalyzer.kt diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..d5d3f00 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 3e546a7..7bf22f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,8 +43,9 @@ dependencies { implementation 'com.google.firebase:firebase-messaging:20.1.0' implementation 'com.google.firebase:firebase-ml-vision:24.0.1' implementation 'com.google.firebase:firebase-ml-vision-barcode-model:16.0.2' - implementation "androidx.camera:camera-core:1.0.0-alpha06" // DO NOT UPDATE! - implementation "androidx.camera:camera-camera2:1.0.0-alpha06" // DO NOT UPDATE! + implementation 'com.camerakit:camerakit:1.0.0-beta3.11' + implementation 'com.camerakit:jpegkit:0.1.0' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.60' } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2280507..42e9ea8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ + + + pictureData = data + return@PictureCallback + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -41,13 +65,152 @@ class QRScannerActivity : AppCompatActivity(), NavigationView.OnNavigationItemSe navView.setNavigationItemSelectedListener(this) + + // Initializes the camera and preview. + initCamera() + initPreview() + + /* When the user clicks the camera button, a picture is taken. + * Additionally, all of the QR processing is done as well, since it guarantees that there is + * picture data to work with. */ + cameraButton.setOnClickListener { + // get an image from the camera + camera?.takePicture(null, null, picture) + // Stops the preview after taking a picture. + camera?.stopPreview() + // Release the camera so that it can be used by other applications. + camera?.release() + + // Get the string represented by the QR code. + qrString = processImage(pictureData, this) + } + + + } + + + private fun checkCameraHardware(context: Context): Boolean { + /* Make sure device has a camera. */ + return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) + } + + private fun getCameraInstance(): Camera? { + /* Safely gets an instance of the Camera. */ + return try { + Camera.open(cameraID) // attempt to get a Camera instance + } catch (e: Exception) { + // Camera is not available (in use or does not exist) + null // returns null if camera is unavailable + } + } + + private fun initCamera(){ + /* Takes care of all of the processes needed to start the camera. */ + + // Initializes the camera safely. + if(checkCameraHardware(this)){ + camera = getCameraInstance() + } + + // Sets the camera orientation. + camera?.setDisplayOrientation(90) + } + + private fun initPreview(){ + /* Takes care of all of the processes needed to create a preview and associate it with the + * camera. */ + + // Creates an instance of the CameraPreview class. + cameraPreview = camera?.let{CameraPreview(this, it)} + + // Initializes the preview. + cameraPreview?.also { + val preview: FrameLayout = previewView + preview.addView(it) + } + + // Checks if the preview was able to be initialized. If not, a Toast is created to tell + // the user to exit the application and retry. + if(cameraPreview == null){ + val message = "Camera preview was unable to be initialized. Please close the application" + + " and try again." + Toast.makeText(this,message,Toast.LENGTH_SHORT).show() + } + + // Starts the preview for the camera. + camera?.startPreview() + } + + + private fun processImage(imgData:ByteArray, context: Context) : String?{ + + // Set options for the detector. val options = FirebaseVisionBarcodeDetectorOptions.Builder() .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE) .build() - val viewFinder = view_finder + // Create an ImageAnalyzer and use it to get the rotation of the camera. + val imageAnalyzer = ImageAnalyzer() + val rotation = imageAnalyzer.getRotation(cameraID.toString(), QRScannerActivity(), context) + + // Set metadata for the detector. + val metadata = FirebaseVisionImageMetadata.Builder() + .setWidth(480) // 480x360 is typically sufficient for + .setHeight(360) // image recognition + .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) + .setRotation(rotation) + .build() + + // Create an image compatible with Firebase's Barcode Detector. + val image = FirebaseVisionImage.fromByteArray(imgData, metadata) + + // Create the detector. + val detector = FirebaseVision.getInstance().getVisionBarcodeDetector(options) + + var returnValue : String? = null + + val result = detector.detectInImage(image) + .addOnSuccessListener { barcodes -> + // Task completed successfully + val barcode = barcodes[0] + + // QR code should only ever be a TYPE_TEXT. + if (barcode.valueType == FirebaseVisionBarcode.TYPE_TEXT){ + returnValue = barcode.rawValue + + }else{ + Log.d("FirebaseVision", "Barcode returned non-text type.") + val message = "QR code read incorrectly. Please close and try again." + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() + } + + } + .addOnFailureListener { + // Task failed with an exception + Log.d("FirebaseVision", it.toString()) + val message = "QR code read incorrectly. Please close and try again." + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() + } + + return returnValue + } + + override fun onResume() { + // Initializes the camera and preview. + initCamera() + initPreview() + super.onResume() } + override fun onPause() { + // Stop the camera preview. + camera?.stopPreview() + // Release the camera so that it can be used by other applications. + camera?.release() + super.onPause() + } + + override fun onBackPressed() { val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) if (drawerLayout.isDrawerOpen(GravityCompat.START)) { diff --git a/app/src/main/java/com/spartahack/spartahack_android/tools/CameraPreview.kt b/app/src/main/java/com/spartahack/spartahack_android/tools/CameraPreview.kt new file mode 100644 index 0000000..6236494 --- /dev/null +++ b/app/src/main/java/com/spartahack/spartahack_android/tools/CameraPreview.kt @@ -0,0 +1,44 @@ +package com.spartahack.spartahack_android.tools + +import android.content.Context +import android.hardware.Camera +import android.util.Log +import android.view.SurfaceHolder +import android.view.SurfaceView +import java.io.IOException + +@Suppress("DEPRECATION") +class CameraPreview(context: Context, private val mCamera: Camera) : SurfaceView(context), SurfaceHolder.Callback { + + private val previewHolder: SurfaceHolder = holder.apply { + // Install a SurfaceHolder.Callback so we get notified when the + // underlying surface is created and destroyed. + addCallback(this@CameraPreview) + // deprecated setting, but required on Android versions prior to 3.0 + setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) + } + + init{ + surfaceCreated(previewHolder) + } + + override fun surfaceCreated(holder: SurfaceHolder) { + // The Surface has been created, now tell the camera where to draw the preview. + mCamera.apply { + try { + setPreviewDisplay(holder) + startPreview() + } catch (e: IOException) { + Log.d("Camera Preview", "Error setting camera preview: ${e.message}") + } + } + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + // Releasing the camera preview is done in the main activity. + } + + override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) { + // Leave empty. I'll add stuff to this if things break. + } +} \ No newline at end of file diff --git a/app/src/main/java/com/spartahack/spartahack_android/tools/EasyCamera.kt b/app/src/main/java/com/spartahack/spartahack_android/tools/EasyCamera.kt new file mode 100644 index 0000000..6310fbe --- /dev/null +++ b/app/src/main/java/com/spartahack/spartahack_android/tools/EasyCamera.kt @@ -0,0 +1,45 @@ +package com.spartahack.spartahack_android.tools + +import android.content.Context +import android.content.pm.PackageManager +import android.hardware.Camera +import android.view.SurfaceHolder +import com.spartahack.spartahack_android.QRScannerActivity + +/* Hi. I am aware that the Camera API is deprecated. However, it is a known problem in Android that +* it is extremely difficult to work with the camera. I have researched alternatives, but the +* original Camera API is the most stable, and easiest to implement in an understandable manner. +* Additionally, the ML Kit used to read the barcodes requires metadata about the camera that is not +* provided by other libraries and APIs. In future versions of the app, this should be updated to a +* more recent library or API. */ + + +@Suppress("DEPRECATION") +@SuppressWarnings("deprecation") +class EasyCamera(holder:SurfaceHolder) { + + private val context = QRScannerActivity() + var camera : Camera? = null + val id = 0 + + init{ + if(checkCameraHardware(context)){ + camera = getCameraInstance() + } + } + + + private fun checkCameraHardware(context: Context): Boolean { + /* Make sure device has a camera. */ + return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) + } + + private fun getCameraInstance(): Camera? { + return try { + Camera.open(id) // attempt to get a Camera instance + } catch (e: Exception) { + // Camera is not available (in use or does not exist) + null // returns null if camera is unavailable + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/spartahack/spartahack_android/tools/ImageAnalyzer.kt b/app/src/main/java/com/spartahack/spartahack_android/tools/ImageAnalyzer.kt new file mode 100644 index 0000000..2f9b1d3 --- /dev/null +++ b/app/src/main/java/com/spartahack/spartahack_android/tools/ImageAnalyzer.kt @@ -0,0 +1,64 @@ +package com.spartahack.spartahack_android.tools + +import android.app.Activity +import android.content.ContentValues.TAG +import android.content.Context +import android.content.Context.CAMERA_SERVICE +import android.hardware.camera2.CameraAccessException +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager +import android.os.Build +import android.util.Log +import android.util.SparseIntArray +import android.view.Surface +import androidx.annotation.RequiresApi +import com.google.firebase.ml.vision.common.FirebaseVisionImageMetadata + +class ImageAnalyzer { + + private val ORIENTATIONS = SparseIntArray() + + init { + ORIENTATIONS.append(Surface.ROTATION_0, 90) + ORIENTATIONS.append(Surface.ROTATION_90, 0) + ORIENTATIONS.append(Surface.ROTATION_180, 270) + ORIENTATIONS.append(Surface.ROTATION_270, 180) + } + /** + * Get the angle by which an image must be rotated given the device's current + * orientation. + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Throws(CameraAccessException::class) + fun getRotation(cameraId: String, activity: Activity, context: Context): Int { + // Get the device's current rotation relative to its "native" orientation. + // Then, from the ORIENTATIONS table, look up the angle the image must be + // rotated to compensate for the device's rotation. + val deviceRotation = activity.windowManager.defaultDisplay.rotation + var rotationCompensation = ORIENTATIONS.get(deviceRotation) + + // On most devices, the sensor orientation is 90 degrees, but for some + // devices it is 270 degrees. For devices with a sensor orientation of + // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. + val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager + val sensorOrientation = cameraManager + .getCameraCharacteristics(cameraId) + .get(CameraCharacteristics.SENSOR_ORIENTATION)!! + rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 + + // Return the corresponding FirebaseVisionImageMetadata rotation value. + val result: Int + when (rotationCompensation) { + 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 + 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 + 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 + 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 + else -> { + result = FirebaseVisionImageMetadata.ROTATION_0 + Log.e(TAG, "Bad rotation value: $rotationCompensation") + } + } + return result + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/qr_view.xml b/app/src/main/res/layout/qr_view.xml index e6e245d..cd47ffe 100644 --- a/app/src/main/res/layout/qr_view.xml +++ b/app/src/main/res/layout/qr_view.xml @@ -1,18 +1,27 @@ - - + + - \ No newline at end of file + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"/> + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 187ed81..740be86 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,11 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.60' + ext.kotlin_version = '1.3.61' repositories { google() jcenter() + maven { url 'https://dl.bintray.com/camerakit/camerakit-android'} } dependencies { From 031f721562a9155eb3c29ea2eb6039c7cda50f2b Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 2 Feb 2020 16:19:04 -0500 Subject: [PATCH 3/6] Tweaks to UI --- .../spartahack_android/FAQActivity.kt | 2 +- .../spartahack_android/MainActivity.kt | 22 +--------- .../spartahack_android/MapsActivity.kt | 2 +- .../spartahack_android/ProfileActivity.kt | 2 +- .../spartahack_android/QRScannerActivity.kt | 3 +- .../spartahack_android/ScheduleActivity.kt | 2 +- .../spartahack_android/Secrets.java | 21 ++++++++++ app/src/main/res/layout/activity_faq.xml | 26 ------------ app/src/main/res/layout/activity_main.xml | 26 ------------ app/src/main/res/layout/activity_maps.xml | 26 ------------ app/src/main/res/layout/activity_qr.xml | 26 ------------ app/src/main/res/layout/activity_schedule.xml | 26 ------------ app/src/main/res/layout/app_bar_faq.xml | 26 ------------ app/src/main/res/layout/app_bar_maps.xml | 34 ---------------- app/src/main/res/layout/app_bar_schedule.xml | 34 ---------------- app/src/main/res/layout/content_main.xml | 9 ----- app/src/main/res/layout/faq_view.xml | 2 +- app/src/main/res/layout/main_view.xml | 13 ++++++ app/src/main/res/layout/nav_header_main.xml | 37 ----------------- app/src/main/res/layout/qr_view.xml | 40 +++++++++---------- app/src/main/res/values/strings.xml | 1 + 21 files changed, 62 insertions(+), 318 deletions(-) create mode 100644 app/src/main/java/com/spartahack/spartahack_android/Secrets.java delete mode 100644 app/src/main/res/layout/activity_faq.xml delete mode 100644 app/src/main/res/layout/activity_main.xml delete mode 100644 app/src/main/res/layout/activity_maps.xml delete mode 100644 app/src/main/res/layout/activity_qr.xml delete mode 100644 app/src/main/res/layout/activity_schedule.xml delete mode 100644 app/src/main/res/layout/app_bar_faq.xml delete mode 100644 app/src/main/res/layout/app_bar_maps.xml delete mode 100644 app/src/main/res/layout/app_bar_schedule.xml delete mode 100644 app/src/main/res/layout/content_main.xml create mode 100644 app/src/main/res/layout/main_view.xml delete mode 100644 app/src/main/res/layout/nav_header_main.xml diff --git a/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt index 7dc55f5..4e222c5 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/FAQActivity.kt @@ -21,7 +21,7 @@ class FAQActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelected override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_faq) + setContentView(R.layout.faq_view) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) diff --git a/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt index 5283bfd..5baf988 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/MainActivity.kt @@ -5,10 +5,7 @@ import android.app.NotificationManager import android.content.Intent import android.os.Build import android.os.Bundle -import androidx.core.view.GravityCompat -import androidx.appcompat.app.ActionBarDrawerToggle import android.view.MenuItem -import androidx.drawerlayout.widget.DrawerLayout import com.google.android.material.navigation.NavigationView import androidx.appcompat.app.AppCompatActivity import android.view.Menu @@ -22,19 +19,10 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + setContentView(R.layout.main_view) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) - val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) - val navView: NavigationView = findViewById(R.id.nav_view) - val toggle = ActionBarDrawerToggle( - this, drawerLayout, R.string.navigation_drawer_open, R.string.navigation_drawer_close) - drawerLayout.addDrawerListener(toggle) - toggle.syncState() - - navView.setNavigationItemSelectedListener(this) - // Create the events notification channel. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val CHANNEL_ID = getString(R.string.events_id) @@ -57,13 +45,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } override fun onBackPressed() { - val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) - if (drawerLayout.isDrawerOpen(GravityCompat.START)) { - drawerLayout.closeDrawer(GravityCompat.START) - } else { super.onBackPressed() - - } } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -106,8 +88,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } } - val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) - drawerLayout.closeDrawer(GravityCompat.START) return true } } diff --git a/app/src/main/java/com/spartahack/spartahack_android/MapsActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/MapsActivity.kt index 4243a60..4611d95 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/MapsActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/MapsActivity.kt @@ -18,7 +18,7 @@ class MapsActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_maps) + setContentView(R.layout.maps_view) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) diff --git a/app/src/main/java/com/spartahack/spartahack_android/ProfileActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/ProfileActivity.kt index ec2aac0..02c74f9 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/ProfileActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/ProfileActivity.kt @@ -18,7 +18,7 @@ class ProfileActivity : AppCompatActivity(), NavigationView.OnNavigationItemSele override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + setContentView(R.layout.main_view) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) diff --git a/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt index 7b16daa..110d2ba 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/QRScannerActivity.kt @@ -51,7 +51,7 @@ class QRScannerActivity : AppCompatActivity(), NavigationView.OnNavigationItemSe override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_qr) + setContentView(R.layout.qr_view) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) @@ -177,6 +177,7 @@ class QRScannerActivity : AppCompatActivity(), NavigationView.OnNavigationItemSe // QR code should only ever be a TYPE_TEXT. if (barcode.valueType == FirebaseVisionBarcode.TYPE_TEXT){ returnValue = barcode.rawValue + Toast.makeText(context, "QR Value: " + returnValue, Toast.LENGTH_SHORT).show() }else{ Log.d("FirebaseVision", "Barcode returned non-text type.") diff --git a/app/src/main/java/com/spartahack/spartahack_android/ScheduleActivity.kt b/app/src/main/java/com/spartahack/spartahack_android/ScheduleActivity.kt index de5b13a..62549fe 100644 --- a/app/src/main/java/com/spartahack/spartahack_android/ScheduleActivity.kt +++ b/app/src/main/java/com/spartahack/spartahack_android/ScheduleActivity.kt @@ -18,7 +18,7 @@ class ScheduleActivity : AppCompatActivity(), NavigationView.OnNavigationItemSel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_schedule) + setContentView(R.layout.schedule_view) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) diff --git a/app/src/main/java/com/spartahack/spartahack_android/Secrets.java b/app/src/main/java/com/spartahack/spartahack_android/Secrets.java new file mode 100644 index 0000000..d6a4b6e --- /dev/null +++ b/app/src/main/java/com/spartahack/spartahack_android/Secrets.java @@ -0,0 +1,21 @@ +package com.spartahack.spartahack_android; + +/** + * Class which stores all the "Secrets" of this program + * + * Stores API keys, paths, secrets for apis, tokens, etc. all in one centralized location + * + * WARNING: Make sure this file is never pushed to the github repo. This is a localized file + */ + +public class Secrets { + + // TODO: Change URL for production + final static private String SPARTAHACK_API_URL = "http://api.elephant.spartahack.com/"; + + + public static String getSPARTAHACK_API_URL() + { + return SPARTAHACK_API_URL; + } +} diff --git a/app/src/main/res/layout/activity_faq.xml b/app/src/main/res/layout/activity_faq.xml deleted file mode 100644 index 3db4944..0000000 --- a/app/src/main/res/layout/activity_faq.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index b0a74ec..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/activity_maps.xml b/app/src/main/res/layout/activity_maps.xml deleted file mode 100644 index ca3400e..0000000 --- a/app/src/main/res/layout/activity_maps.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/activity_qr.xml b/app/src/main/res/layout/activity_qr.xml deleted file mode 100644 index 3db4944..0000000 --- a/app/src/main/res/layout/activity_qr.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/activity_schedule.xml b/app/src/main/res/layout/activity_schedule.xml deleted file mode 100644 index 0a25581..0000000 --- a/app/src/main/res/layout/activity_schedule.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/app_bar_faq.xml b/app/src/main/res/layout/app_bar_faq.xml deleted file mode 100644 index d965d75..0000000 --- a/app/src/main/res/layout/app_bar_faq.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_maps.xml b/app/src/main/res/layout/app_bar_maps.xml deleted file mode 100644 index 095fee2..0000000 --- a/app/src/main/res/layout/app_bar_maps.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_schedule.xml b/app/src/main/res/layout/app_bar_schedule.xml deleted file mode 100644 index 5cad4d7..0000000 --- a/app/src/main/res/layout/app_bar_schedule.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml deleted file mode 100644 index e530a6f..0000000 --- a/app/src/main/res/layout/content_main.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/faq_view.xml b/app/src/main/res/layout/faq_view.xml index f2e3802..13c878d 100644 --- a/app/src/main/res/layout/faq_view.xml +++ b/app/src/main/res/layout/faq_view.xml @@ -5,7 +5,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:showIn="@layout/app_bar_faq" tools:context=".FAQActivity" android:id="@+id/faq_constraits"> diff --git a/app/src/main/res/layout/main_view.xml b/app/src/main/res/layout/main_view.xml new file mode 100644 index 0000000..b567330 --- /dev/null +++ b/app/src/main/res/layout/main_view.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml deleted file mode 100644 index 246001c..0000000 --- a/app/src/main/res/layout/nav_header_main.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/qr_view.xml b/app/src/main/res/layout/qr_view.xml index cd47ffe..c78bd4a 100644 --- a/app/src/main/res/layout/qr_view.xml +++ b/app/src/main/res/layout/qr_view.xml @@ -1,27 +1,25 @@ - - + > - - \ No newline at end of file +