Merge "Embed branch in project name display"
diff --git a/build.gradle b/build.gradle
index 84753dd..0307507 100644
--- a/build.gradle
+++ b/build.gradle
@@ -49,6 +49,8 @@
init.setupRelease()
+apply from: 'buildSrc/jetify.gradle'
+
init.enableDoclavaAndJDiff(this, new DacOptions("android/support", "SUPPORT_DATA"))
///// FLATFOOT START
diff --git a/buildSrc/jetify.gradle b/buildSrc/jetify.gradle
new file mode 100644
index 0000000..6fdc5eb
--- /dev/null
+++ b/buildSrc/jetify.gradle
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+def standaloneProject = project(":jetifier-standalone")
+def jetifierBin = file("${standaloneProject.buildDir}/install/jetifier-standalone/bin/jetifier-standalone")
+
+task jetifyArchive(type: Exec) {
+ description "Produces a zip of jetified artifacts by running Jetifier against top-of-tree*.zip" +
+ " This task only exists to enable dejetifyArchive to have a realistic archive to test " +
+ " with. This task should be deleted once Jetpack exists."
+
+ dependsOn tasks['createArchive']
+ dependsOn ':jetifier-standalone:installDist'
+ inputs.file project.tasks['createArchive'].archivePath
+
+ outputs.file "${buildDir}/top-of-tree-m2-repository-jetified-${project.ext.buildNumber}.zip"
+
+ commandLine ("bash", "-c",
+ "if ${jetifierBin} -s -outputfile ${outputs.files.singleFile} -i ${inputs.files.singleFile}; then\n" +
+ " echo success;\n" +
+ "else\n" +
+ " echo jetifyArchive was not expected to work anyway. Ask jeffrygaston@ or pavlis@ to make it work.\n" +
+ " exit 1\n" +
+ "fi")
+}
+
+task dejetifyArchive(type: Exec) {
+ description "Produces a zip of dejetified artifacts by running Dejetifier against jetified" +
+ " artifacts, for temporary usage by external clients that haven't upgraded to Jetpack" +
+ " yet."
+
+ dependsOn tasks['jetifyArchive']
+ inputs.file project.tasks['jetifyArchive'].outputs.files.singleFile
+
+ outputs.file "${buildDir}/top-of-tree-m2-repository-dejetified-${project.ext.buildNumber}.zip"
+
+
+ commandLine ("${jetifierBin}", "-s", "-outputfile", "${outputs.files.singleFile}", "-i", "${inputs.files.singleFile}")
+}
+
diff --git a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
index 97706a0..8371485 100644
--- a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
+++ b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
@@ -22,6 +22,7 @@
import static android.support.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
@@ -68,6 +69,7 @@
private PagedListViewTestActivity mActivity;
private PagedListView mPagedListView;
+ private ListItemAdapter mAdapter;
@Before
public void setUp() {
@@ -79,8 +81,9 @@
ListItemProvider provider = new ListItemProvider.ListProvider(
new ArrayList<>(items));
try {
+ mAdapter = new ListItemAdapter(mActivity, provider);
mActivityRule.runOnUiThread(() -> {
- mPagedListView.setAdapter(new ListItemAdapter(mActivity, provider));
+ mPagedListView.setAdapter(mAdapter);
});
} catch (Throwable throwable) {
throwable.printStackTrace();
@@ -204,6 +207,45 @@
}
@Test
+ public void testSetSwitchState() {
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSwitch(true, true, null);
+
+ setupPagedListView(Arrays.asList(item0));
+
+ item0.setSwitchState(false);
+ try {
+ mActivityRule.runOnUiThread(() -> mAdapter.notifyItemChanged(0));
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ // Wait for paged list view to layout by using espresso to scroll to a position.
+ onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
+
+ TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
+ assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
+ assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(false)));
+ }
+
+ @Test
+ public void testSetSwitchStateHasNoEffectIfSwitchIsNotEnabled() {
+ TextListItem item0 = new TextListItem(mActivity);
+ setupPagedListView(Arrays.asList(item0));
+
+ item0.setSwitchState(false);
+ try {
+ mActivityRule.runOnUiThread(() -> mAdapter.notifyItemChanged(0));
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ // Wait for paged list view to layout by using espresso to scroll to a position.
+ onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
+
+ TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
+ assertThat(viewHolder.getSwitch().getVisibility(), is(not(equalTo(View.VISIBLE))));
+ }
+
+ @Test
public void testDividersAreOptional() {
TextListItem item0 = new TextListItem(mActivity);
item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
diff --git a/car/src/main/java/androidx/car/widget/TextListItem.java b/car/src/main/java/androidx/car/widget/TextListItem.java
index 871c14b..c87321c 100644
--- a/car/src/main/java/androidx/car/widget/TextListItem.java
+++ b/car/src/main/java/androidx/car/widget/TextListItem.java
@@ -705,6 +705,18 @@
}
/**
+ * Sets the state of {@code Switch}. For this method to take effect,
+ * {@link #setSwitch(boolean, boolean, CompoundButton.OnCheckedChangeListener)} must be called
+ * first to set {@code Supplemental Action} as a {@code Switch}.
+ *
+ * @param isChecked sets the "checked/unchecked, namely on/off" state of switch.
+ */
+ public void setSwitchState(boolean isChecked) {
+ mSwitchChecked = isChecked;
+ markDirty();
+ }
+
+ /**
* Holds views of TextListItem.
*/
public static class ViewHolder extends ListItem.ViewHolder {
diff --git a/compat/api/current.txt b/compat/api/current.txt
index 7902a1e..e97db8c 100644
--- a/compat/api/current.txt
+++ b/compat/api/current.txt
@@ -138,10 +138,13 @@
public final class AppOpsManagerCompat {
method public static int noteOp(android.content.Context, java.lang.String, int, java.lang.String);
+ method public static int noteOpNoThrow(android.content.Context, java.lang.String, int, java.lang.String);
method public static int noteProxyOp(android.content.Context, java.lang.String, java.lang.String);
+ method public static int noteProxyOpNoThrow(android.content.Context, java.lang.String, java.lang.String);
method public static java.lang.String permissionToOp(java.lang.String);
field public static final int MODE_ALLOWED = 0; // 0x0
field public static final int MODE_DEFAULT = 3; // 0x3
+ field public static final int MODE_ERRORED = 2; // 0x2
field public static final int MODE_IGNORED = 1; // 0x1
}
diff --git a/compat/src/main/java/android/support/v4/app/AppOpsManagerCompat.java b/compat/src/main/java/android/support/v4/app/AppOpsManagerCompat.java
index 7e97199..796e81f 100644
--- a/compat/src/main/java/android/support/v4/app/AppOpsManagerCompat.java
+++ b/compat/src/main/java/android/support/v4/app/AppOpsManagerCompat.java
@@ -32,14 +32,21 @@
* Result from {@link #noteOp}: the given caller is allowed to
* perform the given operation.
*/
- public static final int MODE_ALLOWED = 0;
+ public static final int MODE_ALLOWED = AppOpsManager.MODE_ALLOWED;
/**
* Result from {@link #noteOp}: the given caller is not allowed to perform
* the given operation, and this attempt should <em>silently fail</em> (it
* should not cause the app to crash).
*/
- public static final int MODE_IGNORED = 1;
+ public static final int MODE_IGNORED = AppOpsManager.MODE_IGNORED;
+
+ /**
+ * Result from {@link #noteOpNoThrow}: the
+ * given caller is not allowed to perform the given operation, and this attempt should
+ * cause it to have a fatal error, typically a {@link SecurityException}.
+ */
+ public static final int MODE_ERRORED = AppOpsManager.MODE_ERRORED;
/**
* Result from {@link #noteOp}: the given caller should use its default
@@ -47,12 +54,17 @@
* with appop permissions, and callers must explicitly check for it and
* deal with it.
*/
- public static final int MODE_DEFAULT = 3;
+ public static final int MODE_DEFAULT = AppOpsManager.MODE_DEFAULT;
private AppOpsManagerCompat() {}
/**
* Gets the app op name associated with a given permission.
+ * <p>
+ * <strong>Compatibility</strong>
+ * <ul>
+ * <li>On API 22 and lower, this method always returns {@code null}
+ * </ul>
*
* @param permission The permission.
* @return The app op associated with the permission or null.
@@ -72,6 +84,11 @@
* that these two match, and if not, return {@link #MODE_IGNORED}. If this call
* succeeds, the last execution time of the operation for this app will be updated to
* the current time.
+ * <p>
+ * <strong>Compatibility</strong>
+ * <ul>
+ * <li>On API 18 and lower, this method always returns {@link #MODE_IGNORED}
+ * </ul>
* @param context Your context.
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
@@ -83,8 +100,9 @@
*/
public static int noteOp(@NonNull Context context, @NonNull String op, int uid,
@NonNull String packageName) {
- if (SDK_INT >= 23) {
- AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ if (SDK_INT >= 19) {
+ AppOpsManager appOpsManager =
+ (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
return appOpsManager.noteOp(op, uid, packageName);
} else {
return MODE_IGNORED;
@@ -92,6 +110,26 @@
}
/**
+ * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
+ * returns {@link #MODE_ERRORED}.
+ * <p>
+ * <strong>Compatibility</strong>
+ * <ul>
+ * <li>On API 18 and lower, this method always returns {@link #MODE_IGNORED}
+ * </ul>
+ */
+ public static int noteOpNoThrow(@NonNull Context context, @NonNull String op, int uid,
+ @NonNull String packageName) {
+ if (SDK_INT >= 19) {
+ AppOpsManager appOpsManager =
+ (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ return appOpsManager.noteOpNoThrow(op, uid, packageName);
+ } else {
+ return MODE_IGNORED;
+ }
+ }
+
+ /**
* Make note of an application performing an operation on behalf of another
* application when handling an IPC. Note that you must pass the package name
* of the application that is being proxied while its UID will be inferred from
@@ -99,6 +137,11 @@
* package name match, and if not, return {@link #MODE_IGNORED}. If this call
* succeeds, the last execution time of the operation for the proxied app and
* your app will be updated to the current time.
+ * <p>
+ * <strong>Compatibility</strong>
+ * <ul>
+ * <li>On API 22 and lower, this method always returns {@link #MODE_IGNORED}
+ * </ul>
* @param context Your context.
* @param op The operation to note. One of the OPSTR_* constants.
* @param proxiedPackageName The name of the application calling into the proxy application.
@@ -116,4 +159,23 @@
return MODE_IGNORED;
}
}
+
+ /**
+ * Like {@link #noteProxyOp(Context, String, String)} but instead
+ * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+ * <p>
+ * <strong>Compatibility</strong>
+ * <ul>
+ * <li>On API 22 and lower, this method always returns {@link #MODE_IGNORED}
+ * </ul>
+ */
+ public static int noteProxyOpNoThrow(@NonNull Context context, @NonNull String op,
+ @NonNull String proxiedPackageName) {
+ if (SDK_INT >= 23) {
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ return appOpsManager.noteProxyOpNoThrow(op, proxiedPackageName);
+ } else {
+ return MODE_IGNORED;
+ }
+ }
}
diff --git a/compat/src/main/java/android/support/v4/app/RemoteInput.java b/compat/src/main/java/android/support/v4/app/RemoteInput.java
index 0ae3e81..e50b878 100644
--- a/compat/src/main/java/android/support/v4/app/RemoteInput.java
+++ b/compat/src/main/java/android/support/v4/app/RemoteInput.java
@@ -33,7 +33,7 @@
/**
* Helper for using the {@link android.app.RemoteInput}.
*/
-public final class RemoteInput extends RemoteInputCompatBase.RemoteInput {
+public final class RemoteInput {
private static final String TAG = "RemoteInput";
/** Label used to denote the clip data type used for remote input transport */
@@ -67,7 +67,6 @@
* Get the key that the result of this input will be set in from the Bundle returned by
* {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent.
*/
- @Override
public String getResultKey() {
return mResultKey;
}
@@ -75,7 +74,6 @@
/**
* Get the label to display to users when collecting this input.
*/
- @Override
public CharSequence getLabel() {
return mLabel;
}
@@ -83,12 +81,10 @@
/**
* Get possible input choices. This can be {@code null} if there are no choices to present.
*/
- @Override
public CharSequence[] getChoices() {
return mChoices;
}
- @Override
public Set<String> getAllowedDataTypes() {
return mAllowedDataTypes;
}
@@ -111,7 +107,6 @@
* choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown
* if you set this to false and {@link #getChoices} returns {@code null} or empty.
*/
- @Override
public boolean getAllowFreeFormInput() {
return mAllowFreeFormTextInput;
}
@@ -119,7 +114,6 @@
/**
* Get additional metadata carried around with this remote input.
*/
- @Override
public Bundle getExtras() {
return mExtras;
}
diff --git a/compat/src/main/java/android/support/v4/app/RemoteInputCompatBase.java b/compat/src/main/java/android/support/v4/app/RemoteInputCompatBase.java
deleted file mode 100644
index fa7f7b6..0000000
--- a/compat/src/main/java/android/support/v4/app/RemoteInputCompatBase.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * 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 android.support.v4.app;
-
-import android.os.Bundle;
-
-import java.util.Set;
-
-/**
- * @deprecated This class was not meant to be made public.
- */
-@Deprecated
-class RemoteInputCompatBase {
-
- /**
- * @deprecated This class was not meant to be made public.
- */
- @Deprecated
- public abstract static class RemoteInput {
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- public RemoteInput() {}
-
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- protected abstract String getResultKey();
-
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- protected abstract CharSequence getLabel();
-
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- protected abstract CharSequence[] getChoices();
-
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- protected abstract boolean getAllowFreeFormInput();
-
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- protected abstract Bundle getExtras();
-
- /**
- * @deprecated This method was not meant to be made public.
- */
- @Deprecated
- protected abstract Set<String> getAllowedDataTypes();
-
- /**
- * @deprecated This class was not meant to be made public.
- */
- @Deprecated
- public interface Factory {
- RemoteInput build(String resultKey, CharSequence label,
- CharSequence[] choices, boolean allowFreeFormInput, Bundle extras,
- Set<String> allowedDataTypes);
- RemoteInput[] newArray(int length);
- }
- }
-}
diff --git a/core-utils/java/android/support/v4/content/PermissionChecker.java b/core-utils/java/android/support/v4/content/PermissionChecker.java
index c9f18a9..e4274d4 100644
--- a/core-utils/java/android/support/v4/content/PermissionChecker.java
+++ b/core-utils/java/android/support/v4/content/PermissionChecker.java
@@ -110,7 +110,7 @@
packageName = packageNames[0];
}
- if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)
+ if (AppOpsManagerCompat.noteProxyOpNoThrow(context, op, packageName)
!= AppOpsManagerCompat.MODE_ALLOWED) {
return PERMISSION_DENIED_APP_OP;
}
diff --git a/core-utils/tests/java/android/support/v4/content/PermissionCheckerTest.java b/core-utils/tests/java/android/support/v4/content/PermissionCheckerTest.java
new file mode 100644
index 0000000..298624b
--- /dev/null
+++ b/core-utils/tests/java/android/support/v4/content/PermissionCheckerTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 android.support.v4.content;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link PermissionChecker}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PermissionCheckerTest {
+ private Context mContext;
+
+ @Before
+ public void setup() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testCheckPermission() throws Exception {
+ assertEquals(PermissionChecker.PERMISSION_DENIED, PermissionChecker.checkSelfPermission(
+ mContext, Manifest.permission.ANSWER_PHONE_CALLS));
+ assertEquals(PermissionChecker.PERMISSION_GRANTED, PermissionChecker.checkSelfPermission(
+ mContext, Manifest.permission.VIBRATE));
+ }
+}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
index e116b23..caebb50 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
@@ -94,7 +94,17 @@
* - [XmlResourcesTransformer] for java native code
* - [ProGuardTransformer] for PorGuard files
*/
- fun transform(inputLibraries: Set<File>, outputPath: Path): TransformationResult {
+ fun transform(inputLibraries: Set<File>,
+ outputPath: Path,
+ outputIsDir: Boolean
+ ): TransformationResult {
+ // 0) Validate arguments
+ if (!outputIsDir && inputLibraries.size > 1) {
+ throw IllegalArgumentException("Cannot process more than 1 library (" + inputLibraries +
+ ") when it is requested tha the destination (" + outputPath +
+ ") be made a file")
+ }
+
// 1) Extract and load all libraries
val libraries = loadLibraries(inputLibraries)
@@ -116,7 +126,13 @@
transformPomFiles(pomFiles)
// 5) Repackage the libraries back to archives
- val outputLibraries = libraries.map { it.writeSelfToDir(outputPath) }.toSet()
+ val outputLibraries = libraries.map {
+ if (outputIsDir) {
+ it.writeSelfToDir(outputPath)
+ } else {
+ it.writeSelfToFile(outputPath)
+ }
+ }.toSet()
// TODO: Filter out only the libraries that have been really changed
return TransformationResult(
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
index 70ea68c..bac2ffb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
@@ -53,16 +53,21 @@
}
@Throws(IOException::class)
- fun writeSelfToDir(outputDirPath: Path) : File {
+ fun writeSelfToDir(outputDirPath: Path): File {
val outputPath = Paths.get(outputDirPath.toString(), fileName)
+ return writeSelfToFile(outputPath)
+ }
+
+ @Throws(IOException::class)
+ fun writeSelfToFile(outputPath: Path): File {
if (Files.exists(outputPath)) {
Log.i(TAG, "Deleting old output file")
Files.delete(outputPath)
}
// Create directories if they don't exist yet
- Files.createDirectories(outputDirPath)
+ Files.createDirectories(outputPath.parent)
Log.i(TAG, "Writing archive: %s", outputPath.toUri())
val file = outputPath.toFile()
@@ -88,7 +93,6 @@
out.finish()
}
-
object Builder {
@Throws(IOException::class)
@@ -102,8 +106,7 @@
}
@Throws(IOException::class)
- private fun extractArchive(inputStream: InputStream, relativePath: Path)
- : Archive {
+ private fun extractArchive(inputStream: InputStream, relativePath: Path): Archive {
val zipIn = ZipInputStream(inputStream)
val files = mutableListOf<ArchiveItem>()
@@ -136,9 +139,8 @@
return ArchiveFile(relativePath, data)
}
- private fun isArchive(zipEntry: ZipEntry) : Boolean {
+ private fun isArchive(zipEntry: ZipEntry): Boolean {
return ARCHIVE_EXTENSIONS.any { zipEntry.name.endsWith(it, ignoreCase = true) }
}
-
}
}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
index ba126a6..3b189e6 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
@@ -85,7 +85,7 @@
val inputFile = File(javaClass.getResource(inputZipPath).file)
val tempDir = createTempDir()
- val result = processor.transform(setOf(inputFile), tempDir.toPath())
+ val result = processor.transform(setOf(inputFile), tempDir.toPath(), true)
Truth.assertThat(result.filesToAdd).hasSize(1)
testArchivesAreSame(result.filesToAdd.first(),
@@ -106,7 +106,7 @@
val inputFile = File(javaClass.getResource(inputZipPath).file)
val tempDir = createTempDir()
- val result = processor.transform(setOf(inputFile), tempDir.toPath())
+ val result = processor.transform(setOf(inputFile), tempDir.toPath(), true)
Truth.assertThat(result.filesToAdd).hasSize(1)
testArchivesAreSame(result.filesToAdd.first(),
@@ -129,13 +129,13 @@
rewritingSupportLib = true)
val inputFile = File(javaClass.getResource(inputZipPath).file)
val tempDir = createTempDir()
- val result = processor.transform(setOf(inputFile), tempDir.toPath())
+ val result = processor.transform(setOf(inputFile), tempDir.toPath(), true)
// Take previous result & reverse it
val processor2 = Processor.createProcessor(TEST_CONFIG,
rewritingSupportLib = true,
reversedMode = true)
- val result2 = processor2.transform(setOf(result.filesToAdd.first()), tempDir.toPath())
+ val result2 = processor2.transform(setOf(result.filesToAdd.first()), tempDir.toPath(), true)
testArchivesAreSame(result2.filesToAdd.first(),
File(javaClass.getResource(inputZipPath).file))
@@ -154,7 +154,7 @@
val inputFile = File(javaClass.getResource(inputZipPath).file)
val tempDir = createTempDir()
- val result = processor.transform(setOf(inputFile), tempDir.toPath())
+ val result = processor.transform(setOf(inputFile), tempDir.toPath(), true)
Truth.assertThat(result.filesToAdd).hasSize(1)
testArchivesAreSame(result.filesToAdd.first(),
@@ -197,4 +197,4 @@
archive.files.forEach { it.accept(this) }
}
}
-}
\ No newline at end of file
+}
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
index 7b516ec..e0eb60c 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
@@ -16,6 +16,7 @@
package android.support.tools.jetifier.plugin.gradle
+import groovy.lang.Closure
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
@@ -31,7 +32,6 @@
/**
* Adds dependency defined via string notation to be processed by jetifyLibs task.
*
- *
* Example usage in Gradle:
* dependencies {
* compile jetifier.process('groupId:artifactId:1.0')
@@ -42,21 +42,20 @@
}
/**
- * Adds dependency defined via string notation to be processed by jetifyLibs task while also
- * applying the given exclude rules.
- *
+ * Adds dependency defined via string notation to be processed by jetifyLibs task. This version
+ * supports Gradle's configuration closure that is passed to the Gradle's DependencyHandler.
*
* Example usage in Gradle:
* dependencies {
- * compile jetifier.processAndExclude('groupId:artifactId:1.0',
- * [group: 'some.package', module: 'moduleName'])
+ * compile jetifier.process('groupId:artifactId:1.0') {
+ * exclude group: 'groupId'
+ *
+ * transitive = false
+ * }
* }
*/
- fun processAndExclude(
- dependencyNotation: String,
- vararg excludes: Map<String, String>
- ): FileCollection {
- return processAndExclude(project.dependencies.create(dependencyNotation), *excludes)
+ fun process(dependencyNotation: String, closure: Closure<Any>): FileCollection {
+ return process(project.dependencies.create(dependencyNotation, closure))
}
/**
@@ -69,20 +68,6 @@
}
/**
- * Adds dependency to be processed by jetifyLibs task while also applying the given excludes
- * rules.
- */
- fun processAndExclude(
- dependency: Dependency,
- vararg excludes: Map<String, String>
- ): FileCollection {
- val configuration = project.configurations.detachedConfiguration()
- configuration.dependencies.add(dependency)
- excludes.forEach { configuration.exclude(it) }
- return process(configuration)
- }
-
- /**
* Adds dependencies defined via file collection to be processed by jetifyLibs task.
*
* Example usage in Gradle for a single file:
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
index 9114e70..082034fb 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
@@ -46,7 +46,7 @@
Log.logConsumer = JetifierLoggerAdapter(logger)
val processor = Processor.createProcessor(config)
- return processor.transform(filesToProcess, outputDir.toPath())
+ return processor.transform(filesToProcess, outputDir.toPath(), true)
}
fun shouldSkipArtifact(artifactId: String, groupId: String?, config: Config): Boolean {
diff --git a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt b/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
index ee2b1d9..8fdb78e 100644
--- a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
+++ b/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
@@ -27,6 +27,7 @@
import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException
import java.io.File
+import java.nio.file.Path
import java.nio.file.Paths
class Main {
@@ -37,7 +38,9 @@
val OPTIONS = Options()
val OPTION_INPUT = createOption("i", "Input libraries paths", multiple = true)
- val OPTION_OUTPUT = createOption("o", "Output directory path")
+ val OPTION_OUTPUT_DIR = createOption("outputdir", "Output directory path",
+ isRequired = false)
+ val OPTION_OUTPUT_FILE = createOption("outputfile", "Output file", isRequired = false)
val OPTION_CONFIG = createOption("c", "Input config path", isRequired = false)
val OPTION_LOG_LEVEL = createOption("l", "Logging level. debug, verbose, error, info " +
"(default)", isRequired = false)
@@ -75,7 +78,29 @@
Log.setLevel(cmd.getOptionValue(OPTION_LOG_LEVEL.opt))
val inputLibraries = cmd.getOptionValues(OPTION_INPUT.opt).map { File(it) }.toSet()
- val outputPath = Paths.get(cmd.getOptionValue(OPTION_OUTPUT.opt))
+ val outputDir = cmd.getOptionValue(OPTION_OUTPUT_DIR.opt)
+ val outputFile = cmd.getOptionValue(OPTION_OUTPUT_FILE.opt)
+ if (outputDir == null && outputFile == null) {
+ throw IllegalArgumentException("Must specify -outputdir or -outputfile")
+ }
+ if (outputDir != null && outputFile != null) {
+ throw IllegalArgumentException("Cannot specify both -outputdir and -outputfile")
+ }
+ if (inputLibraries.size > 1 && outputFile != null) {
+ throw IllegalArgumentException(
+ "Cannot specify -outputfile when multiple input libraries are given")
+ }
+
+ var outputIsDir = false
+ fun chooseOutputPath(): Path {
+ if (outputFile == null) {
+ outputIsDir = true
+ return Paths.get(outputDir)
+ } else {
+ return Paths.get(outputFile)
+ }
+ }
+ val outputPath = chooseOutputPath()
val config: Config?
if (cmd.hasOption(OPTION_CONFIG.opt)) {
@@ -97,7 +122,7 @@
config = config,
reversedMode = isReversed,
rewritingSupportLib = rewriteSupportLib)
- processor.transform(inputLibraries, outputPath)
+ processor.transform(inputLibraries, outputPath, outputIsDir)
}
private fun parseCmdLine(args: Array<String>): CommandLine? {
diff --git a/slices/core/src/main/java/androidx/app/slice/Slice.java b/slices/core/src/main/java/androidx/app/slice/Slice.java
index 153a34f..b528dfb 100644
--- a/slices/core/src/main/java/androidx/app/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/app/slice/Slice.java
@@ -256,7 +256,9 @@
*/
public Slice.Builder addAction(@NonNull PendingIntent action,
@NonNull Slice s, @Nullable String subType) {
- mItems.add(new SliceItem(action, s, FORMAT_ACTION, subType, new String[0]));
+ @SliceHint String[] hints = s != null
+ ? s.getHints().toArray(new String[s.getHints().size()]) : new String[0];
+ mItems.add(new SliceItem(action, s, FORMAT_ACTION, subType, hints));
return this;
}