Skip to content

Commit e5819d7

Browse files
committed
Prepare for release 1.18.0.
1 parent 69c3e09 commit e5819d7

File tree

120 files changed

+5663
-361
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+5663
-361
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ https://developer.android.com/studio/command-line/bundletool
4646

4747
## Releases
4848

49-
Latest release: [1.17.2](https://github.com/google/bundletool/releases)
49+
Latest release: [1.18.0](https://github.com/google/bundletool/releases)

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
release_version = 1.17.2
1+
release_version = 1.18.0

src/main/dex_src/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Contains the source-code for any .dex files in bundletool.
2+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Source-code for java/com/android/tools/build/bundletool/archive/dex/classes.dex
2+
which is used to build APKs with build-mode ARCHIVE.
3+
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (C) 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License
15+
*/
16+
17+
package com.google.android.archive;
18+
19+
import static java.util.concurrent.TimeUnit.SECONDS;
20+
21+
import android.annotation.SuppressLint;
22+
import android.app.Activity;
23+
import android.app.AlertDialog;
24+
import android.content.ActivityNotFoundException;
25+
import android.content.DialogInterface;
26+
import android.content.Intent;
27+
import android.content.pm.PackageManager.NameNotFoundException;
28+
import android.os.Build.VERSION;
29+
import android.os.Build.VERSION_CODES;
30+
import android.os.Bundle;
31+
import android.util.Log;
32+
import java.util.concurrent.Executors;
33+
import java.util.concurrent.ScheduledExecutorService;
34+
import java.util.concurrent.ScheduledFuture;
35+
36+
/** Activity that triggers the reactivation of an app through the app store. */
37+
public class ReactivateActivity extends Activity implements DialogInterface.OnClickListener {
38+
39+
private static final String TAG = "ReactivateActivity";
40+
41+
private static final String EXTRA_DATA = "archive.extra.data";
42+
43+
private final ScheduledExecutorService scheduledExecutorService =
44+
Executors.newScheduledThreadPool(1);
45+
46+
private String appStorePackageName;
47+
private boolean processingError;
48+
private int retryCount = 0;
49+
50+
private ScheduledFuture<?> unhibernateFuture;
51+
52+
@Override
53+
protected void onCreate(Bundle savedInstanceState) {
54+
handleSplashScreen(this);
55+
super.onCreate(savedInstanceState);
56+
appStorePackageName = getAppStorePackageName();
57+
}
58+
59+
@Override
60+
public void onResume() {
61+
super.onResume();
62+
// Ensures we don't try to send another intent immediately after the first one failed, instead
63+
// an error dialog is shown.
64+
if (processingError) {
65+
return;
66+
}
67+
startUnhibernateActivityForResult();
68+
}
69+
70+
/** Returns true if the targeted Store is installed and enabled. */
71+
private boolean isStoreInstalled() {
72+
try {
73+
return getPackageManager().getApplicationInfo(appStorePackageName, 0).enabled;
74+
} catch (NameNotFoundException e) {
75+
return false;
76+
}
77+
}
78+
79+
private AlertDialog buildErrorDialog() {
80+
AlertDialog.Builder dialog =
81+
new AlertDialog.Builder(this)
82+
.setTitle("Installation failed")
83+
.setCancelable(false)
84+
.setNeutralButton("Close", this)
85+
.setMessage(
86+
String.format(
87+
"To open %s you must first complete its download from an official app store.",
88+
getAppName()));
89+
90+
if (isStoreInstalled()) {
91+
dialog.setPositiveButton("Retry", this);
92+
}
93+
94+
return dialog.create();
95+
}
96+
97+
private String getAppName() {
98+
return getApplicationInfo().loadLabel(getPackageManager()).toString();
99+
}
100+
101+
@Override
102+
public void onClick(DialogInterface ignored, int buttonType) {
103+
processingError = false;
104+
switch (buttonType) {
105+
case DialogInterface.BUTTON_POSITIVE:
106+
startUnhibernateActivityForResult();
107+
break;
108+
case DialogInterface.BUTTON_NEUTRAL:
109+
default:
110+
// Nothing specific, just close the app.
111+
finish();
112+
break;
113+
}
114+
}
115+
116+
/**
117+
* The archived stub is built with the activity flag noHistory, so this can only be triggered if
118+
* the app store is not installed.
119+
*/
120+
@Override
121+
public void onActivityResult(int ignored1, int resultCode, Intent ignored2) {
122+
if (resultCode == Activity.RESULT_CANCELED) {
123+
Log.i(
124+
TAG,
125+
String.format(
126+
"Couldn't reach the app store %s to unarchive, retry count: %s",
127+
appStorePackageName, retryCount));
128+
if (unhibernateFuture != null && retryCount++ < 3) {
129+
unhibernateFuture =
130+
scheduledExecutorService.schedule(this::startUnhibernateActivityForResult, 5, SECONDS);
131+
} else {
132+
processingError = true;
133+
buildErrorDialog().show();
134+
unhibernateFuture = null;
135+
retryCount = 0;
136+
}
137+
} else {
138+
// This should not be reachable due to the noHistory activity flag.
139+
finish();
140+
}
141+
}
142+
143+
/**
144+
* Getting resource by id does not work because classes.dex is prebuild and
145+
* reactivation_app_store_package_name resource is added dynamically with the next available id.
146+
*/
147+
@SuppressLint("DiscouragedApi")
148+
private String getAppStorePackageName() {
149+
return getResources()
150+
.getString(
151+
getResources()
152+
.getIdentifier("reactivation_app_store_package_name", "string", getPackageName()));
153+
}
154+
155+
@SuppressLint("DiscouragedApi")
156+
private int getSplashScreenLayoutResId() {
157+
return getResources()
158+
.getIdentifier(
159+
"com_android_vending_archive_splash_screen_layout", "layout", getPackageName());
160+
}
161+
162+
private void handleSplashScreen(ReactivateActivity activity) {
163+
if (VERSION.SDK_INT < VERSION_CODES.S) {
164+
int splashLayoutResId = getSplashScreenLayoutResId();
165+
if (splashLayoutResId != 0) {
166+
activity.setContentView(splashLayoutResId);
167+
}
168+
}
169+
}
170+
171+
private void startUnhibernateActivityForResult() {
172+
Log.i(TAG, "Unarchiving package: " + getPackageName());
173+
Intent intent = new Intent();
174+
intent.setAction("com.google.android.STORE_ARCHIVE");
175+
intent.setPackage(appStorePackageName);
176+
177+
// Forward any attached data to the app store for consideration.
178+
if (getIntent() != null && getIntent().getData() != null) {
179+
intent.putExtra(EXTRA_DATA, getIntent().getData());
180+
}
181+
182+
try {
183+
startActivityForResult(intent, /* flags= */ 0);
184+
} catch (ActivityNotFoundException e) {
185+
// We handle this case in onActivityResult, because a RESULT_CANCELED is emitted.
186+
}
187+
}
188+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License
15+
*/
16+
17+
package com.google.android.archive;
18+
19+
import android.content.BroadcastReceiver;
20+
import android.content.Context;
21+
import android.content.Intent;
22+
import java.io.File;
23+
24+
/** Clears up the app's cache once it has been archived. */
25+
public class UpdateBroadcastReceiver extends BroadcastReceiver {
26+
27+
@Override
28+
public void onReceive(Context context, Intent intent) {
29+
if (intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED)) {
30+
deleteDir(context.getCacheDir());
31+
}
32+
}
33+
34+
private static void deleteDir(File dir) {
35+
File[] contents = dir.listFiles();
36+
if (contents != null) {
37+
for (File file : contents) {
38+
deleteDir(file);
39+
}
40+
}
41+
dir.delete();
42+
}
43+
}

src/main/java/com/android/tools/build/bundletool/BundleToolMain.java

Lines changed: 31 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -171,97 +171,49 @@ static void main(String[] args, Runtime runtime) {
171171
runtime.exit(0);
172172
}
173173

174+
private static final ImmutableList<CommandHelp> COMMAND_HELPS =
175+
ImmutableList.of(
176+
BuildBundleCommand.help(),
177+
BuildApksCommand.help(),
178+
BuildSdkBundleCommand.help(),
179+
BuildSdkApksCommand.help(),
180+
BuildSdkApksForAppCommand.help(),
181+
BuildSdkAsarCommand.help(),
182+
PrintDeviceTargetingConfigCommand.help(),
183+
EvaluateDeviceTargetingConfigCommand.help(),
184+
ExtractApksCommand.help(),
185+
GetDeviceSpecCommand.help(),
186+
InstallApksCommand.help(),
187+
InstallMultiApksCommand.help(),
188+
ValidateBundleCommand.help(),
189+
DumpCommand.help(),
190+
DumpSdkBundleCommand.help(),
191+
GetSizeCommand.help(),
192+
VersionCommand.help(),
193+
AddTransparencyCommand.help(),
194+
CheckTransparencyCommand.help());
195+
196+
174197
/** Displays a general help. */
175198
public static void help() {
176-
ImmutableList<CommandHelp> commandHelps =
177-
ImmutableList.of(
178-
BuildBundleCommand.help(),
179-
BuildApksCommand.help(),
180-
BuildSdkBundleCommand.help(),
181-
BuildSdkApksCommand.help(),
182-
BuildSdkApksForAppCommand.help(),
183-
BuildSdkAsarCommand.help(),
184-
PrintDeviceTargetingConfigCommand.help(),
185-
EvaluateDeviceTargetingConfigCommand.help(),
186-
ExtractApksCommand.help(),
187-
GetDeviceSpecCommand.help(),
188-
InstallApksCommand.help(),
189-
InstallMultiApksCommand.help(),
190-
ValidateBundleCommand.help(),
191-
DumpCommand.help(),
192-
GetSizeCommand.help(),
193-
VersionCommand.help());
194-
195199
System.out.println("Synopsis: bundletool <command> ...");
196200
System.out.println();
197201
System.out.println("Use 'bundletool help <command>' to learn more about the given command.");
198202
System.out.println();
199-
commandHelps.forEach(commandHelp -> commandHelp.printSummary(System.out));
203+
COMMAND_HELPS.forEach(commandHelp -> commandHelp.printSummary(System.out));
200204
}
201205

202206
/** Displays help about a given command. */
203207
public static void help(String commandName, Runtime runtime) {
204-
CommandHelp commandHelp;
205-
switch (commandName) {
206-
case BuildBundleCommand.COMMAND_NAME:
207-
commandHelp = BuildBundleCommand.help();
208-
break;
209-
case BuildApksCommand.COMMAND_NAME:
210-
commandHelp = BuildApksCommand.help();
211-
break;
212-
case BuildSdkBundleCommand.COMMAND_NAME:
213-
commandHelp = BuildSdkBundleCommand.help();
214-
break;
215-
case BuildSdkApksCommand.COMMAND_NAME:
216-
commandHelp = BuildSdkApksCommand.help();
217-
break;
218-
case BuildSdkApksForAppCommand.COMMAND_NAME:
219-
commandHelp = BuildSdkApksForAppCommand.help();
220-
break;
221-
case BuildSdkAsarCommand.COMMAND_NAME:
222-
commandHelp = BuildSdkAsarCommand.help();
223-
break;
224-
case PrintDeviceTargetingConfigCommand.COMMAND_NAME:
225-
commandHelp = PrintDeviceTargetingConfigCommand.help();
226-
break;
227-
case EvaluateDeviceTargetingConfigCommand.COMMAND_NAME:
228-
commandHelp = EvaluateDeviceTargetingConfigCommand.help();
229-
break;
230-
case ExtractApksCommand.COMMAND_NAME:
231-
commandHelp = ExtractApksCommand.help();
232-
break;
233-
case GetDeviceSpecCommand.COMMAND_NAME:
234-
commandHelp = GetDeviceSpecCommand.help();
235-
break;
236-
case InstallApksCommand.COMMAND_NAME:
237-
commandHelp = InstallApksCommand.help();
238-
break;
239-
case InstallMultiApksCommand.COMMAND_NAME:
240-
commandHelp = InstallMultiApksCommand.help();
241-
break;
242-
case ValidateBundleCommand.COMMAND_NAME:
243-
commandHelp = ValidateBundleCommand.help();
244-
break;
245-
case DumpCommand.COMMAND_NAME:
246-
commandHelp = DumpCommand.help();
247-
break;
248-
case GetSizeCommand.COMMAND_NAME:
249-
commandHelp = GetSizeCommand.help();
250-
break;
251-
case AddTransparencyCommand.COMMAND_NAME:
252-
commandHelp = AddTransparencyCommand.help();
253-
break;
254-
case CheckTransparencyCommand.COMMAND_NAME:
255-
commandHelp = CheckTransparencyCommand.help();
256-
break;
257-
default:
258-
System.err.printf("Error: Unrecognized command '%s'.%n%n%n", commandName);
259-
help();
260-
runtime.exit(1);
208+
for (CommandHelp commandHelp : COMMAND_HELPS) {
209+
if (commandHelp.getCommandName().equals(commandName)) {
210+
commandHelp.printDetails(System.out);
261211
return;
212+
}
262213
}
263-
264-
commandHelp.printDetails(System.out);
214+
System.err.printf("Error: Unrecognized command '%s'.%n%n%n", commandName);
215+
help();
216+
runtime.exit(1);
265217
}
266218

267219
private BundleToolMain() {}

0 commit comments

Comments
 (0)