App has ML Kit functionality (translation). I'm trying to reduce the app size by introducing a dynamic module feature, on demand loading.
Following this guide
Added 'com.google.mlkit:playstore-dynamic-feature-support:16.0.0-beta1' to base apk's build.gradle
com.google.mlkit:translate:16.1.2 in feature module build.gradle,
everything compiles and tries to run on emulator, but unfortunately crashes on app start with log
java.lang.RuntimeException: Unable to get provider com.google.mlkit.common.internal.MlKitInitProvider: com.google.firebase.components.MissingDependencyException: Unsatisfied dependency for component Component<[class com.google.android.gms.internal.mlkit_translate.zzxa]>{0, type=0, deps=[Dependency{anInterface=class com.google.mlkit.common.sdkinternal.SharedPrefManager, type=required, injection=direct}, Dependency{anInterface=class com.google.android.gms.internal.mlkit_translate.zzwx, type=required, injection=direct}]}: class com.google.mlkit.common.sdkinternal.SharedPrefManager
Caused by: com.google.firebase.components.MissingDependencyException: Unsatisfied dependency for component Component<[class com.google.android.gms.internal.mlkit_translate.zzxa]>{0, type=0, deps=[Dependency{anInterface=class com.google.mlkit.common.sdkinternal.SharedPrefManager, type=required, injection=direct}, Dependency{anInterface=class com.google.android.gms.internal.mlkit_translate.zzwx, type=required, injection=direct}]}: class com.google.mlkit.common.sdkinternal.SharedPrefManager
Which kind of does not make sense. Because I've added playstore-dynamic-feature-support.
Figured it out,
Step 1. disable MlKitInitProvider in app module (stops app crash)
<provider
android:name="com.google.mlkit.common.internal.MlKitInitProvider"
android:authorities="${applicationId}.mlkitinitprovider"
tools:node="remove"
/>
Step 2.
build apk and open the app manifest, find all MLKit Registrars used in your app.
Step 3.
add all found to a ComponentRegistrar array ArrayList;
Step 4.
In the dynamic feature library, call MlKitContext.initialize(context, arr); (in getProvider the service provider) prior to using mlkit functionality;
Also, only using these mlkit dependencies in app module (for split install)
api group: 'com.google.mlkit', name: 'common', version: '17.5.0'
api group: 'com.google.mlkit', name: 'playstore-dynamic-feature-support', version: '16.0.0-beta1'
If someone is still not able to figure out using #Shane Gallagher's answer I am detailing out the steps:
First as mentioned add Provider in app module to disable MlKit initialization:
<provider
android:name="com.google.mlkit.common.internal.MlKitInitProvider"
android:authorities="${applicationId}.mlkitinitprovider"
tools:node="remove"/>
Next build apk and open your merged manifest. Find all the component registrars used in your app. You can open merged manifest by clicking on merged manifest text in bottom left corner of Android Studio after opening AndroidManifest.xml
Next in your dynamic feature module add the following code as per the registrars used in your app
val registrars = listOf(CommonComponentRegistrar(), VisionCommonRegistrar(), BarcodeRegistrar())
MlKitContext.initialize(this, registrars)
To set up your dynamic module, you will need to move the mlkit translate dependency from the base app's gradle file to the dynamic module's gradle build file. You will also need to move all related usage to the dynamic module. Therefore, when app start up, it won't look for any mlkit translate dependency. Please refer to the play store on demand delivery guide for step to step set up guidance.
Related
Background
Suppose I make an Android library called "MySdk", and I publish it on Jitpack/Maven.
The user of the SDK would use it by adding just the dependency of :
implementation 'com.github.my-sdk:MySdk:1.0.1'
What I'd like to get is the "1.0.1" part from it, whether I do it from within the Android library itself (can be useful to send to the SDK-server which version is used), or from the app that uses it (can be useful to report about specific issues, including via Crashlytics).
The problem
I can't find any reflection or gradle task to reach it.
What I've tried
Searching about it, if I indeed work on the Android library (that is used as a dependency), all I've found is that I can manage the version myself, via code.
Some said I could use BuildConfig of the package name of the library, but then it means that if I forget to update the code a moment before I publish the dependency, it will use the wrong value. Example of using this method:
plugins {
...
}
final def sdkVersion = "1.0.22"
android {
...
buildTypes {
release {
...
buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "\""
}
debug {
buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "-unreleased\""
}
}
Usage is just checking the value of BuildConfig.SDK_VERSION (after building).
Another possible solution is perhaps from gradle task inside the Android-library, that would be forced to be launched whenever you build the app that uses this library. However, I've failed to find how do it (found something here)
The question
Is it possible to query the dependency version from within the Android library of the dependency (and from the app that uses it, of course), so that I could use it during runtime?
Something automatic, that won't require me to update it before publishing ?
Maybe using Gradle task that is defined in the library, and forced to be used when building the app that uses the library?
You can use a Gradle task to capture the version of the library as presented in the build.gradle dependencies and store the version information in BuildConfig.java for each build type.
The task below captures the version of the "appcompat" dependency as an example.
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.0'
}
task CaptureLibraryVersion {
def libDef = project.configurations.getByName('implementation').allDependencies.matching {
it.group.equals("androidx.appcompat") && it.name.equals("appcompat")
}
if (libDef.size() > 0) {
android.buildTypes.each {
it.buildConfigField 'String', 'LIB_VERSION', "\"${libDef[0].version}\""
}
}
}
For my example, the "appcompat" version was 1.4.0. After the task is run, BuildConfig.java contains
// Field from build type: debug
public static final String LIB_VERSION = "1.4.0";
You can reference this field in code with BuildConfig.LIB_VERSION. The task can be automatically run during each build cycle.
The simple answer to your question is 'yes' - you can do it. But if you want a simple solution to do it so the answer transforms to 'no' - there is no simple solution.
The libraries are in the classpath of your package, thus the only way to access their info at the runtime would be to record needed information during the compilation time and expose it to your application at the runtime.
There are two major 'correct' ways and you kinda have described them in your question but I will elaborate a bit.
The most correct way and relatively easy way is to expose all those variables as BuildConfig or String res values via gradle pretty much as described here. You can try to generify the approach for this using local-prefs(or helper gradle file) to store versions and use them everywhere it is needed. More info here, here, and here
The second correct, but much more complicated way is to write a gradle plugin or at least some set of tasks for collecting needed values during compile-time and providing an interface(usually via your app assets or res) for your app to access them during runtime. A pretty similar thing is already implemented for google libraries in Google Play services Plugins so it would be a good place to start.
All the other possible implementations are variations of the described two or their combination.
You can create buildSrc folder and manage dependencies in there.
after that, you can import & use Versions class in anywhere of your app.
To be clear on what i am asking i will provide a real world example. take look at this and notice the following section:
Hotline - Android SDK Integration Steps Modified on: Fri, 6 Oct, 2017 at 8:21 PM
Integrate Hotline SDK (Using Gradle) Pre Requisites :
Hotline SDK clients require devices running Android 2.3 or higher
Hotline App Id and App Key from here: Where to find App ID and App Key
Android Studio and Gradle
If you have any queries during the integration, please send it to us - Submit a Query
1. Add Hotline SDK to your app
Add the maven URL to the root build.gradle (project/build.gradle)
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
Add the following dependency to your app module's build.gradle file
(project/app/build.gradle):
apply plugin: 'com.android.application'
android {
// ...
}
dependencies {
// ...
compile 'com.github.freshdesk:hotline-android:1.2.+'
}
1.1 Android target version supported
Hotline SDK supports apps targeting Android version 5.0+. The SDK
itself is compatible all the way down to Gingerbread (API Level 10).
When app targets Android 7.0+
When FileProvider is not configured for Hotline SDK, the following
error code is displayed
"Missing/Bad FileProvider for Hotline. Camera capture will fail in devices running Nougat or later versions of OS (error code 354)"
To fix this, please include the provider in the
AndroidManifest.xml as below and specify the authority in strings.xml.
Assuming, com.example.demoapp is the package name of your app, the
declaration would be
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.demoapp.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/hotline_file_provider_paths" />
</provider>
Strings.xml
<string name="hotline_file_provider_authority">com.example.demoapp.provider</string>
When app targets Android 8.0+
When the app's target is Android 8.0 or later, and by extension includes appcompat-v7 r26.0.0.+, you'll see the following errors
E/UncaughtException: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/freshdesk/hotline/activity/InterstitialActivity;
Hotline SDK's activities extends ActionBarActivity to keep the SDK
compatible with app's targeting older Android versions/appcompat-v7
revisions. It can be resolved by adding a proxy class
(ActionBarActivity was replaced by AppCompatActivity and was proxied
by lib itself since 24.2.0 of appcomapt-v7, until it was removed in
26.0.0) manually if you are building with support library 26.x.x.
Add the following class in the appropriate package
package android.support.v7.app;
public class ActionBarActivity extends AppCompatActivity {
}
my question has nothing to do with Hotline. But after i did what they asked my package structure looks like this:
now that you have some background let me tell you what i dont understand. Does this mean that i am overriding any calls in package android.support.v7.app.ActionBarActivity ? so does this mean that for any 3rd party build i have i can override its classes this way as long as i know the package and class name ?
Basically what does it mean to put package name of something i do not own into my package structure ? what does it do ?
UPDATE: look at this article here as another example . if you read solution 3 you see we can do the same thing with facebook.login. i personally implemented this and it works. my test package structure looks like this and it overrides facebooks loginCreator etc:
even though i implemented it, i still dont get whats happening. can someone explain ?
I'm attempting to get familiar with Google Cast and it's usage inside an Android application. The codebase I am working with has a working integration, but seems to have various discrepancies when compared to the official guides.
For example, one of the first steps in the guide is to implement the OptionsProvider interface, like so:
class CastOptionsProvider implements OptionsProvider {
#Override
public CastOptions getCastOptions(Context appContext) {
CastOptions castOptions = new CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.build();
return castOptions;
}
#Override
public List<SessionProvider> getAdditionalSessionProviders(Context context) {
return null;
}
}
However, the codebase i'm working with does not implement this interface anywhere in the application. Confused, I took a look at the dependencies and noticed the following dependency:
compile 'com.google.android.gms:play-services-cast:$androidGoogleServicesVersion'
This was odd, as the guide recommends using the following instead:
compile 'com.google.android.gms:play-services-cast-framework:10.0.1'
Googling the differences between the com.google.android.gms:play-services-cast-framework library and the com.google.android.gms:play-services-cast library returned no usable results.
Additionally, I was unable to find these libraries on either jcenter or maven.
My questions:
What are the differences between com.google.android.gms:play-services-cast-framework and com.google.android.gms:play-services-cast?
Where are these libraries hosted?
Thanks!
So there is a lot to unpack here...
1.
It sounds like your app is using CCL, which is a modified version of the v2 client. You can verify this by searching your app's build.gradle for the "com.google.android.libraries.cast.companionlibrary:ccl" dependency. This requires com.google.android.gms:play-services-cast rather than com.google.android.gms:play-services-cast-framework, although play-services-cast is a transitive dependency of play-services-cast-framework, so it will be included implicitly. CastOptionsProvider is a new thing for the v3 cast api. ($androidGoogleServicesVersion is a groovy variable that is providing the version number and should be set somewhere else, like in your projects top level build file. This represents the 10.0.1.)
CCL
https://github.com/googlecast/CastCompanionLibrary-android
CCL -> v3 Migration
https://developers.google.com/cast/v2/ccl_migrate_sender
2.
These libraries are either pulled from your local SDK. In the SDK tool it is under SDK Tools/Google Play services. Now it can be pulled from Google's maven repo, which can be setup via the instructions here: https://developer.android.com/studio/build/dependencies.html#google-maven. As a note, in AndroidStudio 3.0 you can simply use google() to load it.
I have to integrate the flurry ad in my android application. Can anybody provide the steps to integrate the flurry ad. I have gone through official sdk for flurry but not get any idea. I have followed the link android: Flurry Ads Banner taking Full screen
I have used the code not get any results
FlurryAgent.onStartSession(this, getString(R.string.flurry_api_key));
FlurryAds.fetchAd(this, "ANDROID_BANNER_TOP", mBanner,
FlurryAdSize.BANNER_TOP);
Prerequisites
Flurry Analytics requires a minimum Android API level 10.
Flurry Analytics uses the Android Advertising ID provided by Google Play Services and will check for and respect the user’s ad tracking preference.
Get your API Keys
Start by creating an app. Once you create the app, you’ll receive a Flurry API Key, which you’ll need when using the SDK.
Download the Flurry Android SDK
There are currently two ways of getting the Flurry Android SDK into your application:
Install via jCenter (Recommended):
The Flurry SDK is available via jCenter. You can add it to your application by including the following in your build.gradle file:
// In your top level Gradle config file:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
}
}
allprojects {
repositories {
jcenter()
}
}
// In your main app's Gradle config file:
dependencies {
compile 'com.flurry.android:analytics:6.3.1'
/*
* Optional library to help in monetizing your app with ads.
* If you include the ads library, you do not need to include
* the analytics library as it is a transitive dependency.
*/
// compile 'com.flurry.android:ads:6.3.1'
//... other dependencies
}
Download .jar files from Flurry Dev Portal
The downloaded archive should contain these files for use with Flurry Analytics:
FlurryAnalytics_x.y.z.jar: The library containing Flurry’s analytic collection and reporting code (where x.y.x denotes the latest version of Flurry SDK).
FlurryAds_x.y.z.jar: The optional library to incorporate Flurry’s ads into your application (where x.y.x denotes the latest version of Flurry SDK).
ProjectApiKey.txt: This file contains the name of your project and your project’s API key.
FlurryAndroidAnalyticsReadmevx.y.z.pdf: A PDF file with instructions (where x.y.x denotes the latest version of Flurry SDK).
Add the FlurryAnalytics_x.y.z.jar to your classpath¶
Using Android Studio:
If using Android Studio, you do not need to do anything further to include the Flurry SDK in your project, as long as you have installed the SDK through jCenter in your Gradle configuration.
However, if you prefer to use the downloaded .jar files, follow these procedures:
Add FlurryAnalytics-5.x.x.jar to your project’s libs folder.
Navigate to File > Project Structure > Module > Dependencies. Click the ‘+’ button in the bottom of the ‘Project Structure’ popup to add dependencies. Select ‘File dependency’ and add libs/FlurryAnalytics-5.x.x.jar.
Add Google Play Services library. If selectively compiling individual Google Play Service APIs, you should include the Google Analytics API.
Using Eclipse
Add FlurryAnalytics-5.x.x.jar to your project’s libs folder. Right-click on each JAR file and select Build Path > Add to Build Path.
Add the Google Play Service library jar file.
Configure your AndroidManifest.xml
- Have access to the Internet and allow the Flurry SDK to check state of the network connectivity.
- Specify a versionName attribute in the manifest to have data reported under that version name.
- Declare min version of that Android OS that the app supports. Flurry supports Android OS versions 10 and above.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.flurry.sample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="21" />
<!--required permission-->
<uses-permission android:name="android.permission.INTERNET" />
<!--optional permission - highly recommended-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!--optional permission -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:name=".MyApplication"
android:icon="#drawable/app_icon">
<!--your activities -->
</application>
</manifest>
Add calls to init, onStartSession and onEndSession
Follow these steps, adding these calls:
If you are shipping an app, insert a call to FlurryAgent.init(Context, String) in your Application class, passing it a reference to your application Context and your project’s API key:
//If you are shipping an app, extend the Application class if you are not already doing so:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
new FlurryAgent.Builder()
.withLogEnabled(false)
.build(this, FLURRY_API_KEY);
}
}
Alternatively, you may call init() just before onStartSession(). It is safe to call init() more than once, provided that you use the same API key throughout the application. You may use any type of Context you wish.
If you are writing an app and the minimum target is Ice Cream Sandwich or above (minSdkVersion is set to API level 14 or greater), session handling is completely automatic, and you may skip steps 3 and 4. If you are instrumenting another type of Context, such as a Service, or your minimum target is Gingerbread, proceed with steps 3 or 4.
Insert a call to FlurryAgent.onStartSession(Context) in the Activity’s onStart() method, passing it a reference to a Context object (such as an Activity or Service). If you are targeting Gingerbread, Flurry recommends using the onStart() method of each Activity in your app, and passing the Activity itself as the Context object. For services (or other Contexts), use the Service or the relevant Context as the Context object. Do not pass in the global Application context.
Insert a call to FlurryAgent.onEndSession(Context) in the Activity’s onStop() method, when a session is complete. If you are targeting Gingerbread, we recommend using the onStop method of each Activity in your app. For services (or other Contexts), ensure that onStop is called in each instrumented Service. Make sure to match up a call to onEndSession for each call of onStartSession, passing in the same Context object that was used to call onStartSession. Do not pass in the global Application context.
As long as there is any Context that has called onStartSession() but not onEndSession(), the session will be continued. Also, if a new Context calls onStartSession() within 10 seconds of the last Context calling onEndSession(), then the session will be resumed, instead of a new session being created. Session length, usage frequency, events and errors will continue to be tracked as part of the same session. This ensures that as a user transitions from one Activity to another in your app that they will not have a separate session tracked for each Activity, but will have a single session that spans many activities. If you want to track Activity usage, Flurry recommends using logEvent(), as described in the Custom Events section.
If you wish to change the window during which a session can be resumed, call FlurryAgent.setContinueSessionMillis(long milliseconds) before the call to FlurryAgent.init().
The Flurry SDK automatically transfers the data captured during the session once the SDK determines the session completed. In case the device is not connected, the data is saved on the device and transferred once the device is connected again. The SDK manages the entire process. Currently, there is no way for the app to schedule the data transfer.
You’re done! That’s all you need to do to begin receiving basic metric
data.
We integrated libGoogleAnalyticsServices.jar in our app and it is working fine. By default it includes 'VERSION_CODE' from BuildConfig.java when sending messages to Google Analytics service. I'd like to include 'VERSION_NAME' instead of 'VERSION_CODE'.
1. Is there a way to get a project for libGoogleAnalyticsServices.jar?
2. Is there a way to configure libGoogleAnalyticsServices class to send version_name instead of version_code?