diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/GenerateMeasurementsTask.groovy b/buildSrc/src/main/groovy/com/google/firebase/gradle/GenerateMeasurementsTask.groovy new file mode 100644 index 00000000000..93fa0cf00d5 --- /dev/null +++ b/buildSrc/src/main/groovy/com/google/firebase/gradle/GenerateMeasurementsTask.groovy @@ -0,0 +1,226 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +package com.google.firebase.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.Task +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +/** + * Generates size measurements after building the test apps and outputs them as a text-format + * protocol buffer report. + * + *
This task requires two properties, an SDK map, as input, and the report, as output. The map is + * used to convert project names and build variants into the SDK identifiers used by the database. + * The report path is where the output should be stored. Additionally, a project property, {@code + * pull_request} is used in the report. Excluding this value will send a human-readable version + * to standard out. + */ +public class GenerateMeasurementsTask extends DefaultTask { + + /** + * The file storing the SDK map. + * + *
This may be any type recognized by Gradle as a file. The format of the file's contents is + * headerless CSV with a colon as a delimiter: projectName-buildVariant:sdkId. The first column + * contains both the project name and build variant separated by an hyphen. The SDK ID is the + * integer identifier used by the SQL database to represent this SDK and build variant pair. + * + *
A complete example follows: + *
{@code + * database-debug:1 + * database-release:2 + * firestore-release:7 + * firestore-debug:4 + *}+ */ + @InputFile + File sdkMapFile + + /** + * The file for storing the report. + * + *
This may be any type recognized by Gradle as a file. The contents, if any, will be + * overwritten by the new report. + */ + @OutputFile + File reportFile + + @Override + Task configure(Closure closure) { + project.android.variantFilter { + if (it.buildType.name != "aggressive") { + it.ignore = true; + } + } + + outputs.upToDateWhen { false } + dependsOn "assemble" + return super.configure(closure) + } + + @TaskAction + def generate() { + // Check if we need to run human-readable or upload mode. + if (project.hasProperty("pull_request")) { + def pullRequestNumber = project.properties["pull_request"] + def sdkMap = createSdkMap() + def sizes = calculateSizesForUpload(sdkMap, project.android.applicationVariants) + def report = createReportForUpload(pullRequestNumber, sizes) + + reportFile.withWriter { + it.write(report) + } + } else { + def sizes = calculateHumanReadableSizes(project.android.applicationVariants) + printHumanReadableReport(sizes) + } + } + + private def calculateHumanReadableSizes(variants) { + def sizes = [:] + def processor = {flavor, build, size -> + sizes[new Tuple2(flavor, build)] = size + } + + calculateSizesFor(variants, processor) + return sizes + } + + private def calculateSizesForUpload(sdkMap, variants) { + def sizes = [:] + def processor = { flavor, build, size -> + def name = "${flavor}-${build}" + def sdk = sdkMap[name]; + + if (sdk == null) { + throw new IllegalStateException("$name not included in SDK map") + } + sizes[sdk] = size + } + + calculateSizesFor(variants, processor) + return sizes + } + + private def calculateSizesFor(variants, processor) { + // Each variant should have exactly one APK. If there are multiple APKs, then this file is + // out of sync with our Gradle configuration, and this task fails. If an APK is missing, it + // is silently ignored, and the APKs from the other variants will be used to build the + // report. + variants.each { variant -> + def flavorName = variant.flavorName + def buildType = variant.buildType.name + def apks = variant.outputs.findAll { it.outputFile.name.endsWith(".apk") } + if (apks.size() > 1) { + def msg = "${flavorName}-${buildType} produced more than one APK" + throw new IllegalStateException(msg) + } + + // This runs at most once, as each variant at this point has zero or one APK. + apks.each { + def size = it.outputFile.size() + processor.call(flavorName, buildType, size) + } + } + } + + private def printHumanReadableReport(sizes) { + project.logger.quiet("|------------------ APK Sizes ------------------|") + project.logger.quiet("|-- project --|-- build type --|-- size in bytes --|") + + sizes.each { key, value -> + def line = sprintf("|%-19s|%-19s|%-21s|", key.first, key.second, value) + project.logger.quiet(line) + } + } + + // TODO(allisonbm): Remove hard-coding protocol buffers. This code manually generates the + // text-format protocol buffer report. This eliminates requiring buildSrc to depend on the + // uploader (or simply, the protocol buffer library), but this isn't the most scalable option. + private def createReportForUpload(pullRequestNumber, sizes) { + def sdkId = 0 + def apkSize = 0 + + def pullRequestGroup = """ + groups { + table_name: "PullRequests" + column_names: "pull_request_id" + measurements { + values { + int_value: ${pullRequestNumber} + } + } + } + """ + + def sizeGroupHeader = """ + groups { + table_name: "ApkSizes" + column_names: "sdk_id" + column_names: "pull_request_id" + column_names: "apk_size" + """ + + def sizeGroupEntry = """ + measurements { + values { + int_value: ${->sdkId} + } + values { + int_value: ${pullRequestNumber} + } + values { + int_value: ${->apkSize} + } + } + """ + + def sizeGroupFooter = """ + } + """ + + + def builder = new StringBuilder() + builder.append(pullRequestGroup) + builder.append(sizeGroupHeader) + + sizes.each { key, value -> + // sdkId and apkSize are lazily interpolated into sizeGroupEntry. + sdkId = key + apkSize = value + builder.append(sizeGroupEntry) + } + + builder.append(sizeGroupFooter) + return builder.toString() + } + + private def createSdkMap() { + def map = [:] + + sdkMapFile.eachLine { + def delimiter = it.indexOf(":") + def key = it.substring(0, delimiter).trim() + def value = it.substring(delimiter + 1).trim() + map[key] = Integer.parseInt(value) + } + + return map + } +} diff --git a/buildSrc/src/main/groovy/com/google/firebase/gradle/UploadMeasurementsTask.groovy b/buildSrc/src/main/groovy/com/google/firebase/gradle/UploadMeasurementsTask.groovy new file mode 100644 index 00000000000..21af89d3999 --- /dev/null +++ b/buildSrc/src/main/groovy/com/google/firebase/gradle/UploadMeasurementsTask.groovy @@ -0,0 +1,93 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +package com.google.firebase.gradle + +import java.net.URL +import java.nio.file.Files +import java.nio.file.StandardCopyOption +import org.gradle.api.DefaultTask +import org.gradle.api.Task +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.TaskAction + +/** + * Takes the size information created by {@link GenerateMeasurementTask} and uploads it to the + * database using the uploader tool. + * + *
The uploader tool is fetched from the Internet using a URL. This URL, and the path to the + * report to upload, must be given as properties to this task. This task also requires a project + * property, {@code database_config} for connecting to the database. The format of this file is + * dictated by the uploader tool. + */ +public class UploadMeasurementsTask extends DefaultTask { + + /** + * The URL of the uploader tool. + * + *
This must be a valid URL as a {@link String}. + */ + @Input + String uploader + + /** + * The file to upload. + * + *
This file must exist prior to executing this task, but it may be created by other tasks
+ * provided they run first.
+ */
+ @InputFile
+ File reportFile
+
+ @TaskAction
+ def upload() {
+ if (!project.hasProperty("database_config")) {
+ throw new IllegalStateException("Cannot upload measurements without database config")
+ }
+
+ def configuration = project.file(project.properties["database_config"])
+
+ withTempJar { jar ->
+ getUploaderUrl().withInputStream {
+ Files.copy(it, jar, StandardCopyOption.REPLACE_EXISTING)
+ }
+
+ project.exec {
+ executable("java")
+
+ args(
+ "-jar",
+ jar,
+ "--config_path=${configuration}",
+ "--proto_path=${reportFile}",
+ )
+ }.rethrowFailure()
+ }
+ }
+
+ def getUploaderUrl() {
+ return new URL(uploader)
+ }
+
+ private def withTempJar(Closure action) {
+ def path = Files.createTempFile("uploader", ".jar")
+ try {
+ action.call(path)
+ } finally {
+ Files.delete(path);
+ }
+ }
+}
diff --git a/subprojects.cfg b/subprojects.cfg
index fcfe44f2041..62fe85e130f 100644
--- a/subprojects.cfg
+++ b/subprojects.cfg
@@ -6,4 +6,5 @@ firebase-database-collection
firebase-storage
protolite-well-known-types
+tools:apksize
tools:errorprone
diff --git a/tools/apksize/README.md b/tools/apksize/README.md
new file mode 100644
index 00000000000..a310e5e2388
--- /dev/null
+++ b/tools/apksize/README.md
@@ -0,0 +1,37 @@
+# APK Size Tooling
+
+## Purpose
+
+This tooling measures the size of APKs using Firebase. The APKs are simple apps
+that exercise only a small faction of the API surface. These numbers help to
+show how an app's size might grow if Firebase is included.
+
+## How to Use
+
+There are two tasks defined in this subproject: generateMeasurements and
+uploadMeasurements. The former gathers the measurements and writes them to a
+file in the build directory. The latter is invoked by CI and uploads the report
+to an SQL database.
+
+The generateMeasurements task may be manually run with `./gradlew -q
+generateMeasurements`. This will output a human readable report to standard out.
+Appending `-Ppull_request=999` will instead generate the report to upload, where
+`999` is the pull request number to place in the report.
+
+The uploadMeasurements task is not intended to be invoked manually. However, it
+may be invoked with the above pull request flag and `-Pdatabase_config=path`
+where `path` is the path to the config file. The config file must have the
+following structure where the values in all-caps are placeholders for the
+relevant pieces of configuration:
+
+```
+host:HOST
+database:DATABASE
+user:USER
+password:PASSWORD
+```
+
+## Current Support
+
+All projects in this repository are supported with an aggressive ProGuard
+profile. Less aggressive ProGuard profiles will be added at a future date.
diff --git a/tools/apksize/apksize.gradle b/tools/apksize/apksize.gradle
new file mode 100644
index 00000000000..8ee091aa002
--- /dev/null
+++ b/tools/apksize/apksize.gradle
@@ -0,0 +1,67 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+import com.google.firebase.gradle.GenerateMeasurementsTask
+import com.google.firebase.gradle.UploadMeasurementsTask
+
+// Linting needs to be disabled as none of these apps are real or intended to conform to
+// linting standards.
+tasks.whenTaskAdded { task ->
+ if (task.name.equals("lint")) {
+ //task.enabled = false
+ }
+}
+
+apply plugin: "com.android.application"
+apply from: "default.gradle"
+android {
+ flavorDimensions "apkSize"
+}
+
+apply from: "src/database/database.gradle"
+apply from: "src/storage/storage.gradle"
+apply from: "src/firestore/firestore.gradle"
+apply from: "src/functions/functions.gradle"
+
+/**
+ * This task builds all supported variants (only aggressive as of writing) and writes the
+ * APK sizes to a text-format protocol buffer file.
+ *
+ * @param -Ppull_request the pull request number to be included in the report
+ */
+task generateMeasurements(type: GenerateMeasurementsTask) {
+ sdkMapFile = file("sdks.csv")
+ reportFile = file("$buildDir/size-report.textpb")
+}
+
+/**
+ * This task uploads the report produced by the generate measurements task to a SQL database.
+ *
+ * @param -Pdatabase_config the file with the database configuration
+ * @param -Ppull_request the pull request number to be included in the report
+ */
+task uploadMeasurements(type: UploadMeasurementsTask) {
+ dependsOn generateMeasurements
+
+ reportFile = file("$buildDir/size-report.textpb")
+ uploader = "https://storage.googleapis.com/firebase-engprod-metrics/upload_tool.jar"
+}
+
+// ==========================================================================
+// Copy from here down if you want to use the google-services plugin in your
+// androidTest integration tests.
+// ==========================================================================
+ext.packageName = "com.google.apksize"
+apply from: '../../gradle/googleServices.gradle'
diff --git a/tools/apksize/debug.keystore b/tools/apksize/debug.keystore
new file mode 100644
index 00000000000..c948a6e6414
Binary files /dev/null and b/tools/apksize/debug.keystore differ
diff --git a/tools/apksize/default.gradle b/tools/apksize/default.gradle
new file mode 100644
index 00000000000..2ba6977252b
--- /dev/null
+++ b/tools/apksize/default.gradle
@@ -0,0 +1,59 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * Default build settings
+ */
+android {
+ adbOptions {
+ timeOutInMs 60 * 1000
+ }
+
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
+ compileSdkVersion 26
+
+ defaultConfig {
+ applicationId 'com.google.apksize'
+ minSdkVersion 26
+ multiDexEnabled true
+ targetSdkVersion 26
+ versionCode 1
+ versionName '1.0'
+ }
+
+ packagingOptions {
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/LICENSE-FIREBASE.txt'
+ exclude 'META-INF/NOTICE'
+ }
+
+ buildTypes {
+ aggressive {
+ debuggable false
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-aggressive.pro'
+ signingConfig signingConfigs.debug
+ matchingFallbacks = ['release']
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/tools/apksize/proguard-rules-aggressive.pro b/tools/apksize/proguard-rules-aggressive.pro
new file mode 100644
index 00000000000..8962eae7dbb
--- /dev/null
+++ b/tools/apksize/proguard-rules-aggressive.pro
@@ -0,0 +1 @@
+-dontwarn **
diff --git a/tools/apksize/sdks.csv b/tools/apksize/sdks.csv
new file mode 100644
index 00000000000..2380050269c
--- /dev/null
+++ b/tools/apksize/sdks.csv
@@ -0,0 +1,4 @@
+database-aggressive:4
+firestore-aggressive:1
+functions-aggressive:2
+storage-aggressive:3
diff --git a/tools/apksize/src/database/database.gradle b/tools/apksize/src/database/database.gradle
new file mode 100644
index 00000000000..f79341626d2
--- /dev/null
+++ b/tools/apksize/src/database/database.gradle
@@ -0,0 +1,34 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+android {
+ productFlavors {
+ database {
+ dimension "apkSize"
+ applicationId "com.google.apksize.database"
+ }
+ }
+ sourceSets {
+ database {
+ java.srcDirs = [
+ "src/database/java",
+ ]
+ }
+ }
+}
+dependencies {
+ databaseImplementation project(":firebase-database")
+ databaseImplementation project(":firebase-common")
+}
diff --git a/tools/apksize/src/database/java/com.google.apksize/RealtimeDatabase.java b/tools/apksize/src/database/java/com.google.apksize/RealtimeDatabase.java
new file mode 100644
index 00000000000..8a171b6f293
--- /dev/null
+++ b/tools/apksize/src/database/java/com.google.apksize/RealtimeDatabase.java
@@ -0,0 +1,50 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.apksize;
+
+import android.content.Context;
+import android.util.Log;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.database.DataSnapshot;
+import com.google.firebase.database.DatabaseError;
+import com.google.firebase.database.DatabaseReference;
+import com.google.firebase.database.FirebaseDatabase;
+import com.google.firebase.database.ValueEventListener;
+
+public class RealtimeDatabase implements SampleCode {
+
+ @Override
+ public void runSample(Context context) {
+
+ FirebaseApp.initializeApp(context, new FirebaseOptions.Builder().build());
+
+ DatabaseReference db =
+ FirebaseDatabase.getInstance().getReference("android/saving-data/fireblog/posts");
+
+ db.addListenerForSingleValueEvent(
+ new ValueEventListener() {
+ @Override
+ public void onDataChange(DataSnapshot dataSnapshot) {
+ Log.d("test", "onDataChange");
+ }
+
+ @Override
+ public void onCancelled(DatabaseError databaseError) {
+ Log.d("test", "onDataChange");
+ }
+ });
+ }
+}
diff --git a/tools/apksize/src/database/java/com.google.apksize/SampleCodeLoader.java b/tools/apksize/src/database/java/com.google.apksize/SampleCodeLoader.java
new file mode 100644
index 00000000000..863a17c7379
--- /dev/null
+++ b/tools/apksize/src/database/java/com.google.apksize/SampleCodeLoader.java
@@ -0,0 +1,24 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.apksize;
+
+import android.content.Context;
+
+public class SampleCodeLoader {
+
+ public void runSamples(Context context) {
+ new RealtimeDatabase().runSample(context);
+ }
+}
diff --git a/tools/apksize/src/empty/empty.gradle b/tools/apksize/src/empty/empty.gradle
new file mode 100644
index 00000000000..bcd80ddcfeb
--- /dev/null
+++ b/tools/apksize/src/empty/empty.gradle
@@ -0,0 +1,23 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+android {
+ productFlavors {
+ empty {
+ dimension "apkSize"
+ applicationId "com.google.apksize.empty"
+ }
+ }
+}
diff --git a/tools/apksize/src/empty/java/com.google.apksize/SampleCodeLoader.java b/tools/apksize/src/empty/java/com.google.apksize/SampleCodeLoader.java
new file mode 100644
index 00000000000..531038ac30b
--- /dev/null
+++ b/tools/apksize/src/empty/java/com.google.apksize/SampleCodeLoader.java
@@ -0,0 +1,22 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.apksize;
+
+import android.content.Context;
+
+public class SampleCodeLoader {
+
+ public void runSamples(Context context) {}
+}
diff --git a/tools/apksize/src/firestore/firestore.gradle b/tools/apksize/src/firestore/firestore.gradle
new file mode 100644
index 00000000000..16ced1f72f4
--- /dev/null
+++ b/tools/apksize/src/firestore/firestore.gradle
@@ -0,0 +1,35 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+android {
+ productFlavors {
+ firestore {
+ dimension "apkSize"
+ applicationId "com.google.apksize.firestore"
+ }
+ }
+ sourceSets {
+ firestore {
+ java.srcDirs = [
+ "src/firestore/java",
+ ]
+ }
+ }
+}
+dependencies {
+ firestoreImplementation project(":firebase-firestore")
+ firestoreImplementation "com.google.android.gms:play-services-auth:16.0.1"
+ firestoreImplementation "com.android.support:support-v4:27.1.0"
+}
diff --git a/tools/apksize/src/firestore/java/com.google.apksize/Firestore.java b/tools/apksize/src/firestore/java/com.google.apksize/Firestore.java
new file mode 100644
index 00000000000..9b90237c1a2
--- /dev/null
+++ b/tools/apksize/src/firestore/java/com.google.apksize/Firestore.java
@@ -0,0 +1,99 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.apksize;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.util.Log;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.firestore.DocumentReference;
+import com.google.firebase.firestore.FirebaseFirestore;
+import com.google.firebase.firestore.QueryDocumentSnapshot;
+import com.google.firebase.firestore.QuerySnapshot;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Firestore implements SampleCode {
+ private static final String TAG = "sizetest";
+
+ @Override
+ public void runSample(Context context) {
+ FirebaseFirestore db = FirebaseFirestore.getInstance();
+
+ Map MainActivity will take SampleCodeLoader from every flavor and run the samples
+ *
+ * We need to do this in order to prevent cutting out packages during pro-guarding
+ */
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ new SampleCodeLoader().runSamples(this);
+ }
+}
diff --git a/tools/apksize/src/main/java/com.google.apksize/SampleCode.java b/tools/apksize/src/main/java/com.google.apksize/SampleCode.java
new file mode 100644
index 00000000000..4c6bc01fff4
--- /dev/null
+++ b/tools/apksize/src/main/java/com.google.apksize/SampleCode.java
@@ -0,0 +1,22 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.apksize;
+
+/** SampleCode interface allows to load sample code for the selected build variant */
+import android.content.Context;
+
+public interface SampleCode {
+ void runSample(Context context);
+}
diff --git a/tools/apksize/src/main/res/layout/activity_main.xml b/tools/apksize/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000000..b3a97b3050b
--- /dev/null
+++ b/tools/apksize/src/main/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+
+