

Android Barcode / QR Code Scanner using Google Mobile Vision – Building Movie Ti...
source link: https://www.androidhive.info/2017/08/android-barcode-scanner-using-google-mobile-vision-building-movie-tickets-app/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Android Barcode / QR Code Scanner using Google Mobile Vision – Building Movie Tickets App
By Ravi Tamada November 15, 2017 263 CommentsNowadays Barcodes and QR Codes are widely used in lot of mobile apps. In a QR Code you can store information like text, sms, email, url, image, audio and few other formats. In Android you can extract the information stored in barcodes by using Google Vision Library. Even though there are lot of other libraries available, google vision library is best to consider as it’s not only provide barcode reading but also have other features like face detection, text detection.
In this article we are going to learn how to use the google vision library by creating a simple movie ticket scanning app.
1. Google Mobile Vision API
Google Mobile Vision api helps in finding objects in an image or video. It provides functionalities like face detection, text detection and barcode detection. All these functionalities can be used separately or combined together.
This article aims to explain the barcode detection with a realtime use case scenario. We can see lot of barcode scanning apps used in supermarkets, theatres and hotels which scans a barcode and provides user desired information. In this article we’ll try to build a simple movie ticket scanner app which scans a barcode / qrcode and displays the movie information to book a ticket.
The google vision library is a part of play services and can be added to your project’s build.gradle.
compile
'com.google.android.gms:play-services-vision:11.0.2'
2. Barcode Scanner Library
Google provided a simple tutorial to tryout the barcode scanning library with a simple bitmap image. But when it comes to scanning a realtime camera feed for a barcode, things become difficult to implement as we need to perform barcode detection on camera video.
I have developed a simple barcode scanner library by forking the google vision sample. In this library few bugs were fixed and added other functionalities like callbacks when barcode is scanned and a overlay scanning line indicator that can be used in your apps.
3. How to Use the Barcode library
Follow the below simple steps to include the barcode / qrcode library in your project.
1. Add the androidhive barcode reader and google vision library to your app’s build.gradle file.
This article was written using Android Studio 3.0 Canary 9. The command compile is deprecated and replaced with implementation
dependencies {
// barcode reader library
implementation
'info.androidhive:barcode-reader:1.1.5'
// google vision library
implementation
'com.google.android.gms:play-services-vision:11.0.2'
}
2. Add the barcode camera fragment to your activity or fragment.
<
fragment
android:id
=
"@+id/barcode_scanner"
android:name
=
"info.androidhive.barcode.BarcodeReader"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
app:auto_focus
=
"true"
app:use_flash
=
"false"
/>
3. Implement your Activity from BarcodeReader.BarcodeReaderListener and override the necessary methods.
import
android.support.v7.app.AppCompatActivity;
import
android.os.Bundle;
import
android.util.SparseArray;
import
com.google.android.gms.vision.barcode.Barcode;
import
java.util.List;
import
info.androidhive.barcode.BarcodeReader;
public
class
MainActivity
extends
AppCompatActivity
implements
BarcodeReader.BarcodeReaderListener {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
}
@Override
public
void
onScanned(Barcode barcode) {
// single barcode scanned
}
@Override
public
void
onScannedMultiple(List<Barcode> list) {
// multiple barcodes scanned
}
@Override
public
void
onBitmapScanned(SparseArray<Barcode> sparseArray) {
// barcode scanned from bitmap image
}
@Override
public
void
onScanError(String s) {
// scan error
}
@Override
public
void
onCameraPermissionDenied() {
// camera permission denied
}
}
4. Run your project and try to scan barcode or qrcode. The scanned result will be returned in onScanned() or onScannedMultiple() method.
3.1 Adding Scanning Overlay Indicator Line
We can see all the scanning apps generally adds an indicator line on the camera overlay to indicate the scanning progress in going on. To achieve this, I have added a reusable class in the same library which can be added on to camera screen.
To add the animating scanning line, add the info.androidhive.barcode.ScannerOverlay to same activity overlapping the camera fragment.
<
info.androidhive.barcode.ScannerOverlay
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"#44000000"
app:line_color
=
"#7323DC"
app:line_speed
=
"6"
app:line_width
=
"4"
app:square_height
=
"200"
app:square_width
=
"200"
/>
The library also contains few other useful functionalities like auto flash, beep sound etc., Detailed information about library can be found on it’s Github page.
4. Creating New Project – Building Simple Ticket Scanning App
As the barcode scanning library is already available, we’ll see how to use the library considering a real use case scenario. The app we are going to build not only explains the barcode scanning, but it also covers building the complex UI, making REST api calls to fetch the movie json and writing custom view classes.
Overall the app contains three screens. The first is splash screen, second is barcode scanning and the last one is to display the movie ticket information.
Below are the screenshots of the app.
4.1 The REST API
To build this app we need a REST api to search through movie database by movie barcode. I have written a simple rest api to search for a movie by barcode. This takes predefined barcode value as query param and searches it in the db. Here are the few sample barcodes for testing.
1. dn_barcode.jpg
2. spiderman_barcode.jpg
3. wonderwoman_barcode.jpg
4. dunkirk_barcode.jpg
Search Movie:
Make GET request with the barcode value read.
https://api.androidhive.info/barcodes/search.php?code=dunkirk
Movie Result:
The matched movie json will be given in the response.
{
"name"
:
"Dunkirk"
,
"duration"
:
"1hr 46min"
,
"rating"
: 4.6,
"released"
: true,
"genre"
:
"Action"
,
"price"
:
"₹200"
,
"director"
:
"Christopher Nolan"
}
Now we have all the necessary information with us. Let’s start by creating a new project in Android Studio.
Note: This project is developed using Android Studio 3.0 Canary 9
1. Create a new project in Android Studio from File ⇒ New Project and fill the project details. I gave project name as MovieTickets and package name as info.androidhive.movietickets
2. Open app’s build.gradle and add the barcode and google vision dependencies. I am also adding Glide, Volley and Gson libraries as they are required to make http calls, json parsing and display images.
dependencies {
implementation
'com.google.android.gms:play-services-vision:11.0.2'
// vision barcode scanner
implementation
'info.androidhive:barcode-reader:1.1.2'
// glide image library
implementation
'com.github.bumptech.glide:glide:4.0.0-RC1'
annotationProcessor
'com.github.bumptech.glide:compiler:4.0.0-RC1'
implementation
'com.android.volley:volley:1.0.0'
implementation
'com.google.code.gson:gson:2.6.2'
}
3. Add the below string, dimen, color resources to respective files under res directory.
<
resources
>
<
string
name
=
"app_name"
>Movie Tickets</
string
>
<
string
name
=
"title_activity_ticket"
>Book Ticket</
string
>
<
string
name
=
"lbl_duration"
>DURATION</
string
>
<
string
name
=
"lbl_genre"
>GENRE</
string
>
<
string
name
=
"lbl_rating"
>RATING</
string
>
<
string
name
=
"lbl_price"
>PRICE</
string
>
<
string
name
=
"btn_buy_now"
>BUY NOW</
string
>
<
string
name
=
"btn_coming_soon"
>COMING SOON</
string
>
<
string
name
=
"msg_no_ticket_found"
>No ticket found. Try scanning the QR Codes from http://api.androidhive.info/qrcodes/</
string
>
</
resources
>
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<
dimen
name
=
"dimen_20"
>20dp</
dimen
>
<
dimen
name
=
"dimen_10"
>10dp</
dimen
>
<
dimen
name
=
"activity_margin"
>16dp</
dimen
>
<
dimen
name
=
"lbl_directory"
>14dp</
dimen
>
<
dimen
name
=
"lbl_movie_name"
>28dp</
dimen
>
<
dimen
name
=
"img_poster_height"
>220dp</
dimen
>
<
dimen
name
=
"dimen_40"
>40dp</
dimen
>
</
resources
>
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
resources
>
<
color
name
=
"colorPrimary"
>#6d0094</
color
>
<
color
name
=
"colorPrimaryDark"
>#6d0094</
color
>
<
color
name
=
"colorAccent"
>#ff2068</
color
>
<
color
name
=
"colorAccentSecondary"
>#ad1a7f</
color
>
<
color
name
=
"viewBg"
>#f8f8f8</
color
>
<
color
name
=
"btn_disabled"
>#999</
color
>
<
color
name
=
"lbl_value"
>#222222</
color
>
</
resources
>
4. Create a class named MyApplication.java and add the below code. Here we create volley singleton instance.
Volley is not suggested for making http calls, but to make the integration simple, it’s considered in this tutorial. Consider using Retrofit in your production apps.
import
android.app.Application;
import
android.text.TextUtils;
import
com.android.volley.Request;
import
com.android.volley.RequestQueue;
import
com.android.volley.toolbox.Volley;
/**
* Created by ravi on 31/07/17.
*/
public
class
MyApplication
extends
Application {
public
static
final
String TAG = MyApplication.
class
.getSimpleName();
private
RequestQueue mRequestQueue;
private
static
MyApplication mInstance;
@Override
public
void
onCreate() {
super
.onCreate();
mInstance =
this
;
}
public
static
synchronized
MyApplication getInstance() {
return
mInstance;
}
public
RequestQueue getRequestQueue() {
if
(mRequestQueue ==
null
) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return
mRequestQueue;
}
public
<T>
void
addToRequestQueue(Request<T> req, String tag) {
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public
<T>
void
addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public
void
cancelPendingRequests(Object tag) {
if
(mRequestQueue !=
null
) {
mRequestQueue.cancelAll(tag);
}
}
}
5. Open AndroidManifest.xml and add the MyApplication class to <applicaton> tag. Also add the INTERNET permission as we need to make http calls.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
package
=
"info.androidhive.movietickets"
>
<
uses-permission
android:name
=
"android.permission.INTERNET"
/>
<
application
android:name
=
".MyApplication"
android:allowBackup
=
"true"
android:icon
=
"@mipmap/ic_launcher"
android:label
=
"@string/app_name"
android:roundIcon
=
"@mipmap/ic_launcher_round"
android:supportsRtl
=
"true"
android:theme
=
"@style/AppTheme"
>
<
activity
android:name
=
".MainActivity"
android:screenOrientation
=
"portrait"
android:theme
=
"@style/AppTheme.NoActionBar"
>
<
intent-filter
>
<
action
android:name
=
"android.intent.action.MAIN"
/>
<
category
android:name
=
"android.intent.category.LAUNCHER"
/>
</
intent-filter
>
</
activity
>
<
activity
android:name
=
".ScanActivity"
android:screenOrientation
=
"portrait"
/>
<
activity
android:name
=
".TicketActivity"
android:label
=
"@string/title_activity_ticket"
android:screenOrientation
=
"portrait"
android:theme
=
"@style/AppTheme.NoActionBar"
></
activity
>
</
application
>
</
manifest
>
6. Under res ⇒ drawable folder create an xml drawable named bg_gradient.xml This drawable gives gradient background to view.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:shape
=
"rectangle"
>
<
gradient
android:angle
=
"135"
android:centerColor
=
"@color/colorAccentSecondary"
android:endColor
=
"@color/colorPrimary"
android:startColor
=
"@color/colorAccent"
android:type
=
"linear"
/>
<
corners
android:radius
=
"0dp"
/>
</
shape
>
4.2 Adding Landing Screen
The landing screen will have few text fields and a button to open the scanner camera. The interesting thing you will learn here is giving gradient background to activity.
7. Open the layout file your main activity (activity_main.xml) add the below layout. Here we are adding button to launch scanner activity.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"@drawable/bg_gradient"
tools:context
=
"info.androidhive.movietickets.MainActivity"
>
<
LinearLayout
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_centerHorizontal
=
"true"
android:layout_centerInParent
=
"true"
android:gravity
=
"center"
android:orientation
=
"vertical"
android:paddingLeft
=
"40dp"
android:paddingRight
=
"40dp"
>
<
ImageView
android:id
=
"@+id/icon"
android:layout_width
=
"100dp"
android:layout_height
=
"100dp"
android:layout_centerHorizontal
=
"true"
android:clickable
=
"true"
android:foreground
=
"?attr/selectableItemBackground"
android:src
=
"@drawable/qrcode"
android:tint
=
"@android:color/white"
/>
<
TextView
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_marginTop
=
"30dp"
android:fontFamily
=
"sans-serif-light"
android:gravity
=
"center"
android:text
=
"Scan the QR code on the poster and book your movie tickets"
android:textColor
=
"@android:color/white"
android:textSize
=
"16dp"
/>
</
LinearLayout
>
<
Button
android:id
=
"@+id/btn_scan"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_alignParentBottom
=
"true"
android:layout_centerHorizontal
=
"true"
android:layout_marginBottom
=
"40dp"
android:background
=
"@android:color/transparent"
android:foreground
=
"?attr/selectableItemBackground"
android:paddingLeft
=
"20dp"
android:paddingRight
=
"20dp"
android:fontFamily
=
"sans-serif-medium"
android:text
=
"Scan QR Code"
android:textColor
=
"@android:color/white"
android:textSize
=
"18sp"
/>
</
RelativeLayout
>
8. Open the main activity and do the below necessary changes. In this activity transparentToolbar() method makes toolbar transparent. The button click listener launches the scanner activity which we are going to create shortly.
import
android.app.Activity;
import
android.content.Intent;
import
android.graphics.Color;
import
android.os.Build;
import
android.support.v7.app.AppCompatActivity;
import
android.os.Bundle;
import
android.util.SparseArray;
import
android.view.View;
import
android.view.Window;
import
android.view.WindowManager;
import
com.google.android.gms.vision.barcode.Barcode;
import
java.util.List;
import
info.androidhive.barcode.BarcodeReader;
public
class
MainActivity
extends
AppCompatActivity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
// making toolbar transparent
transparentToolbar();
setContentView(R.layout.activity_main);
findViewById(R.id.btn_scan).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
startActivity(
new
Intent(MainActivity.
this
, ScanActivity.
class
));
}
});
}
private
void
transparentToolbar() {
if
(Build.VERSION.SDK_INT >=
19
&& Build.VERSION.SDK_INT <
21
) {
setWindowFlag(
this
, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
true
);
}
if
(Build.VERSION.SDK_INT >=
19
) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
if
(Build.VERSION.SDK_INT >=
21
) {
setWindowFlag(
this
, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
false
);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
}
private
void
setWindowFlag(Activity activity,
final
int
bits,
boolean
on) {
Window win = activity.getWindow();
WindowManager.LayoutParams winParams = win.getAttributes();
if
(on) {
winParams.flags |= bits;
}
else
{
winParams.flags &= ~bits;
}
win.setAttributes(winParams);
}
}
If you run the app, you can see the landing screen as below.
4.3 Adding Ticket Scan Screen
Now we have the landing screen ready. Let’s create the scanner activity.
9. Create a new activity from File ⇒ New ⇒ Activity ⇒ Empty Activity and name it as ScanActivity.java
10. Open layout file of ticket activity (activity_scan.xml) and add the Barcode Reader fragment as below. Here we are also adding the scanner line indicator animation view.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
tools:context
=
"info.androidhive.movietickets.ScanActivity"
>
<
fragment
android:id
=
"@+id/barcode_scanner"
android:name
=
"info.androidhive.barcode.BarcodeReader"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
app:auto_focus
=
"true"
app:use_flash
=
"false"
/>
<
info.androidhive.barcode.ScannerOverlay
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"#44000000"
app:line_color
=
"#7323DC"
app:line_speed
=
"6"
app:line_width
=
"4"
app:square_height
=
"200"
app:square_width
=
"200"
/>
</
RelativeLayout
>
11. Open ScanActivity.java and do the below necessary changes.
> Implement the activity from BarcodeReader.BarcodeReaderListener
> Override the callback methods onScanned(), onScannedMultiple() and other callbacks.
> barcodeReader.playBeep() plays beep sound upon barcode is read.
> Launch the TicketActivity once the barcode is scanned by passing the barcode value in intent.
package
info.androidhive.movietickets;
import
android.content.Intent;
import
android.support.v7.app.AppCompatActivity;
import
android.os.Bundle;
import
android.util.SparseArray;
import
android.widget.Toast;
import
com.google.android.gms.vision.barcode.Barcode;
import
java.util.List;
import
info.androidhive.barcode.BarcodeReader;
public
class
ScanActivity
extends
AppCompatActivity
implements
BarcodeReader.BarcodeReaderListener {
BarcodeReader barcodeReader;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
// get the barcode reader instance
barcodeReader = (BarcodeReader) getSupportFragmentManager().findFragmentById(R.id.barcode_scanner);
}
@Override
public
void
onScanned(Barcode barcode) {
// playing barcode reader beep sound
barcodeReader.playBeep();
// ticket details activity by passing barcode
Intent intent =
new
Intent(ScanActivity.
this
, TicketActivity.
class
);
intent.putExtra(
"code"
, barcode.displayValue);
startActivity(intent);
}
@Override
public
void
onScannedMultiple(List<Barcode> list) {
}
@Override
public
void
onBitmapScanned(SparseArray<Barcode> sparseArray) {
}
@Override
public
void
onCameraPermissionDenied() {
finish();
}
@Override
public
void
onScanError(String s) {
Toast.makeText(getApplicationContext(),
"Error occurred while scanning "
+ s, Toast.LENGTH_SHORT).show();
}
}
Run the app and try to scan any barcode. The scanned barcode will be returned in onScanned() method.
4.4 Adding Ticket Scan Result Screen
The next screen will be ticket results activity. On this screen the movie details will be displayed by sending the scanned qrcode to search endpoint and get the result.
If you observe the design, I have placed the movie details on a ticket view which will have punched holes in the corners. To achieve this I have created a custom view and made the holes using eraser.
4.4.1 Preparing Ticket View with Punching Holes
12. Create a class named TicketView.java. This is a plain view class in which the Canvas is used to render the view with transparent holes on the corners.
import
android.content.Context;
import
android.graphics.Bitmap;
import
android.graphics.Canvas;
import
android.graphics.Color;
import
android.graphics.DashPathEffect;
import
android.graphics.Paint;
import
android.graphics.Path;
import
android.graphics.PorterDuff;
import
android.graphics.PorterDuffXfermode;
import
android.util.AttributeSet;
import
android.widget.LinearLayout;
/**
* Created by ravi tamada on 29/07/17.
* Ticket view creates view with view punches making circular holes
* in the view
*/
public
class
TicketView
extends
LinearLayout {
private
Bitmap bm;
private
Canvas cv;
private
Paint eraser;
private
int
holesBottomMargin =
70
;
private
int
holeRadius =
40
;
public
TicketView(Context context) {
super
(context);
Init();
}
public
TicketView(Context context, AttributeSet attrs) {
super
(context, attrs);
Init();
}
public
TicketView(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
Init();
}
private
void
Init() {
eraser =
new
Paint();
eraser.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.CLEAR));
eraser.setAntiAlias(
true
);
}
@Override
protected
void
onSizeChanged(
int
w,
int
h,
int
oldw,
int
oldh) {
if
(w != oldw || h != oldh) {
bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
cv =
new
Canvas(bm);
}
super
.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected
void
onDraw(Canvas canvas) {
int
w = getWidth();
int
h = getHeight();
bm.eraseColor(Color.TRANSPARENT);
// set the view background color
cv.drawColor(Color.WHITE);
// drawing footer square contains the buy now button
Paint paint =
new
Paint();
paint.setARGB(
255
,
250
,
250
,
250
);
paint.setStrokeWidth(
0
);
paint.setStyle(Paint.Style.FILL);
cv.drawRect(
0
, h, w, h - pxFromDp(getContext(), holesBottomMargin), paint);
// adding punching holes on the ticket by erasing them
cv.drawCircle(
0
,
0
, holeRadius, eraser);
// top-left hole
cv.drawCircle(w /
2
,
0
, holeRadius, eraser);
// top-middle hole
cv.drawCircle(w,
0
, holeRadius, eraser);
// top-right
cv.drawCircle(
0
, h - pxFromDp(getContext(), holesBottomMargin), holeRadius, eraser);
// bottom-left hole
cv.drawCircle(w, h - pxFromDp(getContext(), holesBottomMargin), holeRadius, eraser);
// bottom right hole
// drawing the image
canvas.drawBitmap(bm,
0
,
0
,
null
);
// drawing dashed lines at the bottom
Path mPath =
new
Path();
mPath.moveTo(holeRadius, h - pxFromDp(getContext(), holesBottomMargin));
mPath.quadTo(w - holeRadius, h - pxFromDp(getContext(), holesBottomMargin), w - holeRadius, h - pxFromDp(getContext(), holesBottomMargin));
// dashed line
Paint dashed =
new
Paint();
dashed.setARGB(
255
,
200
,
200
,
200
);
dashed.setStyle(Paint.Style.STROKE);
dashed.setStrokeWidth(
2
);
dashed.setPathEffect(
new
DashPathEffect(
new
float
[]{
10
,
5
},
0
));
canvas.drawPath(mPath, dashed);
super
.onDraw(canvas);
}
public
static
float
pxFromDp(
final
Context context,
final
float
dp) {
return
dp * context.getResources().getDisplayMetrics().density;
}
}
13. Create new class named TicketResultActivity.java by navigating to File ⇒ New ⇒ Activity ⇒ Empty Activity.
14. Open the layout of ticket result activity (activity_ticket_result.xml) and add the below layout.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
android.support.design.widget.CoordinatorLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:id
=
"@+id/main_content"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"@drawable/bg_gradient"
android:descendantFocusability
=
"beforeDescendants"
android:fitsSystemWindows
=
"true"
tools:context
=
".TicketResultActivity"
>
<
android.support.design.widget.AppBarLayout
foreground
=
"?android:windowContentOverlay"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:theme
=
"@style/AppTheme.AppBarOverlay"
>
<
android.support.v7.widget.Toolbar
android:id
=
"@+id/toolbar"
android:layout_width
=
"match_parent"
android:layout_height
=
"?actionBarSize"
android:background
=
"@color/colorPrimary"
app:popupTheme
=
"@style/AppTheme.PopupOverlay"
></
android.support.v7.widget.Toolbar
>
</
android.support.design.widget.AppBarLayout
>
<
include
layout
=
"@layout/content_ticket_details"
/>
</
android.support.design.widget.CoordinatorLayout
>
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"@drawable/bg_gradient"
app:layout_behavior
=
"@string/appbar_scrolling_view_behavior"
tools:context
=
".TicketResultActivity"
>
<
TextView
android:id
=
"@+id/txt_error"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_centerHorizontal
=
"true"
android:layout_centerInParent
=
"true"
android:gravity
=
"center_horizontal"
android:text
=
"@string/msg_no_ticket_found"
android:textColor
=
"@android:color/white"
android:padding
=
"@dimen/dimen_20"
android:textSize
=
"16dp"
android:visibility
=
"gone"
/>
<
info.androidhive.movietickets.TicketView
android:id
=
"@+id/layout_ticket"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:layout_marginLeft
=
"@dimen/dimen_20"
android:layout_marginRight
=
"@dimen/dimen_20"
android:layout_marginTop
=
"@dimen/dimen_20"
android:background
=
"@android:color/transparent"
android:orientation
=
"vertical"
android:paddingTop
=
"@dimen/dimen_10"
android:visibility
=
"gone"
>
<
TextView
android:id
=
"@+id/director"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginBottom
=
"5dp"
android:fontFamily
=
"sans-serif-condensed"
android:paddingLeft
=
"@dimen/activity_margin"
android:paddingRight
=
"@dimen/activity_margin"
android:paddingTop
=
"@dimen/activity_margin"
android:textAllCaps
=
"true"
android:textSize
=
"@dimen/lbl_directory"
/>
<
TextView
android:id
=
"@+id/name"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:fontFamily
=
"sans-serif-condensed"
android:paddingLeft
=
"16dp"
android:paddingRight
=
"16dp"
android:textAllCaps
=
"true"
android:textColor
=
"#111"
android:maxLines
=
"1"
android:ellipsize
=
"end"
android:textSize
=
"@dimen/lbl_movie_name"
/>
<
ImageView
android:id
=
"@+id/poster"
android:layout_width
=
"match_parent"
android:layout_height
=
"@dimen/img_poster_height"
android:layout_marginBottom
=
"@dimen/activity_margin"
android:layout_marginTop
=
"@dimen/activity_margin"
android:scaleType
=
"centerCrop"
/>
<
LinearLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:orientation
=
"horizontal"
android:paddingLeft
=
"@dimen/activity_margin"
android:paddingRight
=
"@dimen/activity_margin"
android:weightSum
=
"2"
>
<
LinearLayout
android:layout_width
=
"0dp"
android:layout_height
=
"wrap_content"
android:layout_weight
=
"1"
android:orientation
=
"vertical"
>
<
TextView
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginBottom
=
"5dp"
android:fontFamily
=
"sans-serif-condensed"
android:text
=
"@string/lbl_duration"
android:textSize
=
"12dp"
/>
<
TextView
android:id
=
"@+id/duration"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:fontFamily
=
"sans-serif-condensed"
android:textColor
=
"@color/lbl_value"
android:textSize
=
"22dp"
/>
</
LinearLayout
>
<
LinearLayout
android:layout_width
=
"0dp"
android:layout_height
=
"wrap_content"
android:layout_weight
=
"1"
android:orientation
=
"vertical"
>
<
TextView
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginBottom
=
"5dp"
android:fontFamily
=
"sans-serif-condensed"
android:text
=
"@string/lbl_genre"
android:textSize
=
"12dp"
/>
<
TextView
android:id
=
"@+id/genre"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:fontFamily
=
"sans-serif-condensed"
android:textAllCaps
=
"true"
android:textColor
=
"@color/lbl_value"
android:textSize
=
"22dp"
/>
</
LinearLayout
>
</
LinearLayout
>
<
LinearLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginTop
=
"16dp"
android:orientation
=
"horizontal"
android:paddingLeft
=
"16dp"
android:paddingRight
=
"16dp"
android:weightSum
=
"2"
>
<
LinearLayout
android:layout_width
=
"0dp"
android:layout_height
=
"wrap_content"
android:layout_weight
=
"1"
android:orientation
=
"vertical"
>
<
TextView
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginBottom
=
"5dp"
android:fontFamily
=
"sans-serif-condensed"
android:text
=
"@string/lbl_rating"
android:textSize
=
"12dp"
/>
<
TextView
android:id
=
"@+id/rating"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:fontFamily
=
"sans-serif-condensed"
android:textColor
=
"@color/lbl_value"
android:textSize
=
"22dp"
/>
</
LinearLayout
>
<
LinearLayout
android:layout_width
=
"0dp"
android:layout_height
=
"wrap_content"
android:layout_weight
=
"1"
android:orientation
=
"vertical"
>
<
TextView
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_marginBottom
=
"5dp"
android:fontFamily
=
"sans-serif-condensed"
android:text
=
"@string/lbl_price"
android:textSize
=
"12dp"
/>
<
TextView
android:id
=
"@+id/price"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:fontFamily
=
"sans-serif-condensed"
android:textAllCaps
=
"true"
android:textColor
=
"@color/lbl_value"
android:textSize
=
"22dp"
/>
</
LinearLayout
>
</
LinearLayout
>
</
info.androidhive.movietickets.TicketView
>
<
Button
android:id
=
"@+id/btn_buy"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_alignParentBottom
=
"true"
android:layout_centerHorizontal
=
"true"
android:layout_marginBottom
=
"10dp"
android:background
=
"@android:color/transparent"
android:fontFamily
=
"sans-serif-condensed"
android:foreground
=
"?attr/selectableItemBackground"
android:paddingLeft
=
"@dimen/activity_margin"
android:paddingRight
=
"@dimen/activity_margin"
android:textColor
=
"@color/colorPrimary"
android:textSize
=
"26dp"
/>
<
ProgressBar
android:id
=
"@+id/progressBar"
android:layout_width
=
"@dimen/dimen_40"
android:layout_height
=
"@dimen/dimen_40"
android:layout_centerInParent
=
"true"
android:indeterminateTint
=
"@android:color/white"
android:indeterminateTintMode
=
"src_atop"
android:visibility
=
"visible"
/>
</
RelativeLayout
>
15. Open the respective TicketResultActivity.java and modify the code as below.
> searchBarcode() makes the volley http call to search endpoint by passing the barcode scanned.
> renderMovie() parses the json and renders the movie details on the screen.
import
android.os.Bundle;
import
android.support.v4.content.ContextCompat;
import
android.support.v7.app.AppCompatActivity;
import
android.support.v7.widget.Toolbar;
import
android.text.TextUtils;
import
android.util.Log;
import
android.view.MenuItem;
import
android.view.View;
import
android.widget.Button;
import
android.widget.ImageView;
import
android.widget.ProgressBar;
import
android.widget.TextView;
import
android.widget.Toast;
import
com.android.volley.Request;
import
com.android.volley.Response;
import
com.android.volley.VolleyError;
import
com.android.volley.toolbox.JsonObjectRequest;
import
com.bumptech.glide.Glide;
import
com.google.gson.Gson;
import
com.google.gson.JsonSyntaxException;
import
com.google.gson.annotations.SerializedName;
import
org.json.JSONObject;
public
class
TicketResultActivity
extends
AppCompatActivity {
private
static
final
String TAG = TicketResultActivity.
class
.getSimpleName();
// url to search barcode
private
TextView txtName, txtDuration, txtDirector, txtGenre, txtRating, txtPrice, txtError;
private
ImageView imgPoster;
private
Button btnBuy;
private
ProgressBar progressBar;
private
TicketView ticketView;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_ticket_result);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(
true
);
txtName = findViewById(R.id.name);
txtDirector = findViewById(R.id.director);
txtDuration = findViewById(R.id.duration);
txtPrice = findViewById(R.id.price);
txtRating = findViewById(R.id.rating);
imgPoster = findViewById(R.id.poster);
txtGenre = findViewById(R.id.genre);
btnBuy = findViewById(R.id.btn_buy);
imgPoster = findViewById(R.id.poster);
txtError = findViewById(R.id.txt_error);
ticketView = findViewById(R.id.layout_ticket);
progressBar = findViewById(R.id.progressBar);
String barcode = getIntent().getStringExtra(
"code"
);
// close the activity in case of empty barcode
if
(TextUtils.isEmpty(barcode)) {
Toast.makeText(getApplicationContext(),
"Barcode is empty!"
, Toast.LENGTH_LONG).show();
finish();
}
// search the barcode
searchBarcode(barcode);
}
/**
* Searches the barcode by making http call
* Request was made using Volley network library but the library is
* not suggested in production, consider using Retrofit
*/
private
void
searchBarcode(String barcode) {
// making volley's json request
JsonObjectRequest jsonObjReq =
new
JsonObjectRequest(Request.Method.GET,
URL + barcode,
null
,
new
Response.Listener<JSONObject>() {
@Override
public
void
onResponse(JSONObject response) {
Log.e(TAG,
"Ticket response: "
+ response.toString());
// check for success status
if
(!response.has(
"error"
)) {
// received movie response
renderMovie(response);
}
else
{
// no movie found
showNoTicket();
}
}
},
new
Response.ErrorListener() {
@Override
public
void
onErrorResponse(VolleyError error) {
Log.e(TAG,
"Error: "
+ error.getMessage());
showNoTicket();
}
});
MyApplication.getInstance().addToRequestQueue(jsonObjReq);
}
private
void
showNoTicket() {
txtError.setVisibility(View.VISIBLE);
ticketView.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
}
/**
* Rendering movie details on the ticket
*/
private
void
renderMovie(JSONObject response) {
try
{
// converting json to movie object
Movie movie =
new
Gson().fromJson(response.toString(), Movie.
class
);
if
(movie !=
null
) {
txtName.setText(movie.getName());
txtDirector.setText(movie.getDirector());
txtDuration.setText(movie.getDuration());
txtGenre.setText(movie.getGenre());
txtRating.setText(
""
+ movie.getRating());
txtPrice.setText(movie.getPrice());
Glide.with(
this
).load(movie.getPoster()).into(imgPoster);
if
(movie.isReleased()) {
btnBuy.setText(getString(R.string.btn_buy_now));
btnBuy.setTextColor(ContextCompat.getColor(
this
, R.color.colorPrimary));
}
else
{
btnBuy.setText(getString(R.string.btn_coming_soon));
btnBuy.setTextColor(ContextCompat.getColor(
this
, R.color.btn_disabled));
}
ticketView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
}
else
{
// movie not found
showNoTicket();
}
}
catch
(JsonSyntaxException e) {
Log.e(TAG,
"JSON Exception: "
+ e.getMessage());
showNoTicket();
Toast.makeText(getApplicationContext(),
"Error occurred. Check your LogCat for full report"
, Toast.LENGTH_SHORT).show();
}
catch
(Exception e) {
// exception
showNoTicket();
Toast.makeText(getApplicationContext(),
"Error occurred. Check your LogCat for full report"
, Toast.LENGTH_SHORT).show();
}
}
@Override
public
boolean
onOptionsItemSelected(MenuItem item) {
if
(item.getItemId() == android.R.id.home) {
finish();
}
return
super
.onOptionsItemSelected(item);
}
private
class
Movie {
String name;
String director;
String poster;
String duration;
String genre;
String price;
float
rating;
@SerializedName
(
"released"
)
boolean
isReleased;
public
String getName() {
return
name;
}
public
String getDirector() {
return
director;
}
public
String getPoster() {
return
poster;
}
public
String getDuration() {
return
duration;
}
public
String getGenre() {
return
genre;
}
public
String getPrice() {
return
price;
}
public
float
getRating() {
return
rating;
}
public
boolean
isReleased() {
return
isReleased;
}
}
}
Now if you run the app and scan the qr codes provided in this article, the results will be shown as below.
5. Testing the App
The app can scan any barcode but the movie ticket information will be shown only when the qr code is scanned provided in this article.
Run the app and try to scan the below qr codes. You should able to see the proper movie information as shown in the demo.
1. dn_barcode.jpg
2. spiderman_barcode.jpg
3. wonderwoman_barcode.jpg
4. dunkirk_barcode.jpg
Hi there! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If you have any idea that you would want me to develop? Let’s talk: [email protected]
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK