Updating to 3.0.0¶
API Changes¶
We introduced some significant changes to the library API without breaking backwards compatibility. These changes were required as the old API was not extensible and it limited us in what features we could build without making the library integration too difficult. The new API allows us more flexibility when adding new features while making the integration even easier as before.
We are keeping network code out of the Gini Vision Library in order to allow clients freedom in choosing their desired network implementation. The multi-page feature required us to add more network related logic to the library which would have made integration more difficult.
We decided to implement a plugin architecture and introduced a network layer with a single interface
called GiniVisionNetworkService
. This declares all the required network tasks and is used in all
the screens where documents have to be sent to the Gini API. The GiniVisionNetworkService
implementation is outside of the scope of the Gini Vision Library and has to be specified before
launching the library.
We provide a default networking implementation in the Gini Vision Network Library. Adding this to your app along with the Gini Vision Library will allow you to quickly integrate Gini invoice scanning.
The multi-page feature requires the new API. Existing features will continue to work without any changes, but new features we add in the future will require the new API.
Note
To easily compare the old API to the new one check out the Gini Vision Library Android Example App. There we’ve set up a legacy
and
a newest
flavor. legacy
shows how integration was performed in previous versions and
newest
shows how integration should be performed using the new API.
GiniVision Singleton¶
We introduced the GiniVision
class as the single entry-point for configuring and interacting
with the Gini Vision Library.
To configure and create a new instance use the GiniVision.Builder
returned by
GiniVision.newInstance()
. The builder creates a new GiniVision
singleton which you
can later destroy with GiniVision.getInstance().cleanup()
. This will also free up any resources used
by the Gini Vision Library. You should call cleanup()
after the Gini Vision Library returned
control to your application and your app is not using it anymore.
Configuration¶
Configuration is performed when building a new GiniVision
instance. Previously configuration was
done by setting Activity extras and using the GiniVisionFeatureConfiguration
.
Set Custom Onboarding Pages¶
3.0.0¶
GiniVision.newInstance()
.setCustomOnboardingPages(getOnboardingPages())
.build();
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
intent.putParcelableArrayListExtra(CameraActivity.EXTRA_IN_ONBOARDING_PAGES, myOnboardingPages());
Disable Showing Onboarding on the First Run¶
3.0.0¶
GiniVision.newInstance()
.setShouldShowOnboardingAtFirstRun(false)
.build();
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
intent.putExtra(CameraActivity.EXTRA_IN_SHOW_ONBOARDING_AT_FIRST_RUN, false);
Force Showing of the Onboarding¶
3.0.0¶
GiniVision.newInstance()
.setShouldShowOnboarding(true)
.build();
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
intent.putExtra(CameraActivity.EXTRA_IN_SHOW_ONBOARDING, true);
Close Library on Pressing the Back Button From Any Activity in the Library¶
3.0.0¶
This option has been removed.
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
intent.putExtra(CameraActivity.EXTRA_IN_BACK_BUTTON_SHOULD_CLOSE_LIBRARY, true);
Enable Document Import from the Camera Screen¶
3.0.0¶
GiniVision.newInstance()
.setDocumentImportEnabledFileTypes(DocumentImportEnabledFileTypes.PDF_AND_IMAGES)
.build();
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
final GiniVisionFeatureConfiguration giniVisionFeatureConfiguration =
GiniVisionFeatureConfiguration.buildNewConfiguration()
.setDocumentImportEnabledFileTypes(DocumentImportEnabledFileTypes.PDF_AND_IMAGES)
.build();
intent.putExtra(CameraActivity.EXTRA_IN_GINI_VISION_FEATURE_CONFIGURATION,
giniVisionFeatureConfiguration);
Enable File Import (“open with”)¶
3.0.0¶
GiniVision.newInstance()
.setFileImportEnabled(true)
.build();
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
final GiniVisionFeatureConfiguration giniVisionFeatureConfiguration =
GiniVisionFeatureConfiguration.buildNewConfiguration()
.setFileImportEnabled(true)
.build();
intent.putExtra(CameraActivity.EXTRA_IN_GINI_VISION_FEATURE_CONFIGURATION,
giniVisionFeatureConfiguration);
Enable QRCode Scanning¶
3.0.0¶
GiniVision.newInstance()
.setQRCodeScanningEnabled(true)
.build()
2.5.0 and older¶
Intent intent = new Intent(this, CameraActivity.class);
final GiniVisionFeatureConfiguration giniVisionFeatureConfiguration =
GiniVisionFeatureConfiguration.buildNewConfiguration()
.setQRCodeScanningEnabled(true)
.build();
intent.putExtra(CameraActivity.EXTRA_IN_GINI_VISION_FEATURE_CONFIGURATION,
giniVisionFeatureConfiguration);
File Import¶
We moved methods in GiniVisionFileImport
to GiniVision
in order to simplify interaction.
Starting from this version GiniVision
is the go-to class for interaction with the Gini Vision
Library.
Create a Document from the Imported File¶
When using the Component API you need to create a Document
from the imported file and pass the
Document
either to the ReviewActivity
(ReviewFragment
) or the AnalysisActivity
(AnalysisFragment
).
3.0.0¶
GiniVision.createDocumentForImportedFile(...);
2.5.0 and older¶
GiniVisionFileImport.createDocumentForImportedFile(..);
Networking Integration¶
We don’t provide network code with the Gini Vision Library in order to allow clients to use their desired networking implementation. In previous versions integration of network code was achieved either by subclassing Activities and overriding methods or by implementing Fragment listeners.
Starting from this version we unified all required network tasks into the
GiniVisionNetworkService
interface. The Gini Vision Library uses this interface to request
document upload and analysis. We also added the GiniVisionNetworkApi
to declare network tasks
which may be called by the client outside of the Gini Vision Library (e.g. for sending feedback).
When using the Screen API the extractions are returned to your app in the EXTRA_OUT_EXTRACTIONS
Bundle in the CameraActivity
’s result Intent. This Bundle contains extraction label Strings as
keys and GiniVisionSpecificExtraction
as values.
For the Component API the extractions are returned in the onExtractionsAvailable(Map<String,
GiniVisionSpecificExtraction>)
method of the CameraFragmentListener
,
ReviewFragmentListener
or AnalysisFragmentListener
.
Gini Vision Network Library¶
The Gini Vision Network Library provides a default implementation of the networking interfaces. By using this library you can quickly integrate invoice scanning in your application.
To use it add the gini-vision-network-lib
dependency to your app’s build.gradle
along with
the Gini Vision Library:
repositories {
...
maven {
url 'https://repo.gini.net/nexus/content/repositories/open'
}
}
dependencies {
...
implementation 'net.gini:gini-vision-lib:3.0.0'
implementation 'net.gini:gini-vision-network-lib:3.0.0'
}
For the Gini Vision Library to be aware of the default implementations create the instances and pass
them to the builder of GiniVision
:
GiniVisionDefaultNetworkService networkService =
GiniVisionDefaultNetworkService.builder((Context) this)
.setClientCredentials(myClientId, myClientSecret, myEmailDomain)
.build();
GiniVisionDefaultNetworkApi networkApi =
GiniVisionDefaultNetworkApi.builder()
.withGiniVisionDefaultNetworkService(networkService)
.build();
GiniVision.newInstance()
.setGiniVisionNetworkService(networkService)
.setGiniVisionNetworkApi(networkApi)
.build();
Subclassing the CameraActivity
, ReviewActivity
and AnalysisActivity
is not required
anymore. Likewise adding network code to your implementations of the CameraFragmentListener
,
ReviewFragmentListener
and AnalysisFragmentListener
is not required. All related methods
have been deprecated, but will still be used, if there is no GiniVision
instance.
Warning
A GiniVision
instance is required to use the new network integration API. Without a
GiniVision
instance the Gini Vision Library will fall back to the previous API and requires
Activity subclasses or Fragment listeners for adding network calls.
Custom Networking Implementation¶
You can also provide your own networking by implementing the GiniVisionNetworkService
and the
GiniVisionNetworkApi
interfaces. Pass your instances to the builder of GiniVision
as shown
above.
UI Changes¶
Camera Screen¶
We added a subtitle to the document import button to make the purpose of the button more clear. You
can customize the text via the string resource named gv_camera_document_import_subtitle
and the
text style via overriding the style named
GiniVisionTheme.Camera.DocumentImportSubtitle.TextStyle
(make sure to use the root parent style
Root.GiniVisionTheme.Camera.DocumentImportSubtitle.TextStyle
).
Onboarding Screen¶
We made the background opaque. Usability testing showed that the semi-transparent background lead users to believe they should already perform the tips while in the onboarding.
Multi-Page Document Scanning¶
The API changes detailed above allowed us to introduce multi-page document scanning. With this feature users can take or import pictures of invoice pages. The Gini API then analyzes the pages as a single invoice and extracts the required payment information.
Requirements¶
To use this feature you need to use GiniVision
along with the new networking integration.
Enable Multi-Page¶
Multi-page scanning is disabled by default. Enable it when building a new GiniVision
instance:
GiniVision.newInstance()
.setMultiPageEnabled(true)
.build();
Camera Screen¶
When multi-page is enabled, then taken or imported pictures are shown in an image stack in the bottom right corner of the Camera Screen. When users tap the stack the Multi-Page Review Screen is launched.
Customizing the UI¶
- Images stack badge background and text style
- Images stack subtitle
For detailed customization options consult the Javadoc of the CameraActivity
.
Multi-Page Review Screen¶
In this screen users can review their images. They can also reordered, rotate and delete them. Users may add more pages by tapping on the “Add pages” button or going back to the Camera Screen.
The first time users take a picture the Multi-Page Review Screen is launched. Subsequent pictures are added to the image stack in the Camera Screen and users can tap it to go to the Multi-Page Review Screen.
Image uploads start as soon as users enter this screen. Upload activity indicators are shown on each image thumbnail. If the uploads are successfull green checkmarks are displayed on the image thumbnails. When all uploads were successfull the user can proceed to the Analysis Screen. If uploads failed, then red crosses are shown on the image thumbnails and users may retry or delete the failed images.
Note
There is no Standard Fragment for the Multi-Page Review Screen. From this version on we only provide Android Support Library Fragments to simplify the UI code and we are confident, that today the Android Support Library is a must-have for every Android application.
Customizing the UI¶
- Page indicator
- Next button
- Background colors
- Thumbnail cards
- Thumbnail activity indicator
- Thumbnail upload success and failure icons
- Add page icon and subtitle
- Reorder pages tip
- Error message background color and text styles
- Delete last page confirmation dialog
For detailed customization options consult the Javadoc of the MultiPageReviewActivity
.
Importing Multiple Images¶
To allow users to send multiple images (jpeg, png and gif) to the Gini Vision Library from other apps through your app you need to register one of your Activities to receive multiple images.
Registering to Receive Multiple Image Files¶
Add the following intent filter to the Activity in your AndroidManifest.xml
you wish to receive multiple incoming images:
<activity android:name=".ui.MyActivity">
<intent-filter android:label="@string/label_for_open_with">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
Importing multiple PDFs is not supported so the intent filter for PDFs must not contain the
SEND_MULTIPLE
action. Simply add a separate intent filter to be able to receive single PDF
files:
<activity android:name=".ui.MyActivity">
<intent-filter android:label="@string/label_for_open_with">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/pdf" />
</intent-filter>
</activity>
Handling Imported Files¶
When you use the multi-page feature you always have to call the new
GiniVision.createIntentForImportedFiles()
method or if you use the Component API the
GiniVision.createDocumentForImportedFiles()
method.
Note
You don’t have to check whether the user imported one or multiple files. Simply use
GiniVision.createIntentForImportedFiles()
or GiniVision.createDocumentForImportedFiles()
to handle one or more incoming files.
When your app is requested to handle one or multiple images or a PDF your Activity (declaring the intent filter
shown before) is launched or resumed (onNewIntent(Intent)
) with an Intent having ACTION_VIEW
,
ACTION_SEND
or ACTION_SEND_MULTIPLE
.
Important
To make sure your application can read the shared file declare and request the
READ_EXTERNAL_STORAGE
permission before accessing the Uri
or before starting the Gini
Vision Library.
Checking whether the Intent has the required action:
String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)
|| Intent.ACTION_SEND.equals(action)
|| Intent.ACTION_SEND_MULTIPLE.equals(action)) {
...
}
Using the Screen API, create an Intent for launching the Gini Vision Library with
GiniVisionFileImport.createIntentForImportedFiles()
. This method requires a callback with which
it will notify your app about the outcome of the import process. Since importing multiple files can
take some seconds (images are processed and compressed), you should show an activity indicator until
one of the callback methods is invoked.
// Token to request cancellation of the file import
private CancellationToken mFileImportCancellationToken;
void startGiniVisionLibraryForImportedFile(final Intent importedFileIntent) {
showActivityIndicator();
mFileImportCancellationToken = GiniVision.getInstance().createIntentForImportedFiles(
importedFileIntent, this,
new AsyncCallback<Intent, ImportedFileValidationException>() {
@Override
public void onSuccess(final Intent result) {
mFileImportCancellationToken = null;
hideActivityIndicator();
startActivityForResult(result, REQUEST_SCAN);
}
@Override
public void onError(final ImportedFileValidationException exception) {
mFileImportCancellationToken = null;
hideActivityIndicator();
String message = "File cannot be analyzed";
if (exception.getValidationError() != null) {
switch (e.getValidationError()) {
case TYPE_NOT_SUPPORTED:
message = "File type not supported.";
break;
case SIZE_TOO_LARGE:
message = "File too large, must be less than 10 MB.";
break;
case TOO_MANY_PDF_PAGES:
message = "Pdf must have less than 10 pages.";
break;
}
}
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialogInterface, final int i) {
finish();
}
})
.show();
}
@Override
public void onCancelled() {
mFileImportCancellationToken = null;
hideActivityIndicator();
}
});
}
The returned Intent will launch either the MultiPageReviewActivity or the AnalysisActivity (the one from the Gini Vision Library - subclassing the AnalysisActivity is not required anymore). For example PDFs cannot be reviewed by the user and for those the AnalysisActivity is launched.
For the Component API, create a Document
with
GiniVisionFileImport.createDocumentForImportedFiles()
. Like the previous method this one
requires a callback, too. You should show an activity indicator until one of the callback methods is
invoked.
The MultiPageReviewFragment may only be used with reviewable documents. Therefore, it is important to check whether the document is reviewable or not:
// Token to request cancellation of the file import
private CancellationToken mFileImportCancellationToken;
void startGiniVisionLibraryForImportedFile(final Intent importedFileIntent) {
showActivityIndicator();
mFileImportCancellationToken = GiniVision.getInstance().createDocumentForImportedFiles(
importedFileIntent, mActivity,
new AsyncCallback<Document, ImportedFileValidationException>() {
@Override
public void onSuccess(@NonNull final Document result) {
if (result.isReviewable()) {
launchMultiPageReviewScreen();
} else {
launchAnalysisScreen(result);
}
mActivity.finish();
}
@Override
public void onError(
@NonNull final ImportedFileValidationException exception) {
mFileImportCancellationToken = null;
hideActivityIndicator();
String message = "File cannot be analyzed";
if (exception.getValidationError() != null) {
switch (e.getValidationError()) {
case TYPE_NOT_SUPPORTED:
message = "File type not supported.";
break;
case SIZE_TOO_LARGE:
message = "File too large, must be less than 10 MB.";
break;
case TOO_MANY_PDF_PAGES:
message = "Pdf must have less than 10 pages.";
break;
}
}
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialogInterface, final int i) {
finish();
}
})
.show();
}
@Override
public void onCancelled() {
}
});
}