Skip to content

Commit

Permalink
Make dependency on AndroidX appcompat optional for dialog builder.
Browse files Browse the repository at this point in the history
The dependency is only used to create a dialog in
TrackSelectionDialogBuilder that is compatible with newer styling
options.

This dependendy adds over 500Kb to the apk (even if unused) and we
shoudn't force this on an app. Instead make the dependency optional by
automatically falling back to the platform version if the AndroidX one
doesn't exist.

Issue: #7357
PiperOrigin-RevId: 322143005
  • Loading branch information
tonihei authored and ojw28 committed Jul 24, 2020
1 parent ee222f7 commit 953db78
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 15 deletions.
1 change: 0 additions & 1 deletion library/ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ dependencies {
implementation project(modulePrefix + 'library-core')
api 'androidx.media:media:' + androidxMediaVersion
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion
implementation 'androidx.recyclerview:recyclerview:' + androidxRecyclerViewVersion
implementation 'com.google.guava:guava:' + guavaVersion
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
Expand Down
18 changes: 18 additions & 0 deletions library/ui/proguard-rules.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Proguard rules specific to the UI module.

# Constructor method accessed via reflection in TrackSelectionDialogBuilder
-dontnote androidx.appcompat.app.AlertDialog.Builder
-keepclassmembers class androidx.appcompat.app.AlertDialog$Builder {
<init>(android.content.Context);
public android.content.Context getContext();
public androidx.appcompat.app.AlertDialog$Builder setTitle(java.lang.CharSequence);
public androidx.appcompat.app.AlertDialog$Builder setView(android.view.View);
public androidx.appcompat.app.AlertDialog$Builder setPositiveButton(int, android.content.DialogInterface$OnClickListener);
public androidx.appcompat.app.AlertDialog$Builder setNegativeButton(int, android.content.DialogInterface$OnClickListener);
public androidx.appcompat.app.AlertDialog create();
}

# Don't warn about checkerframework and Kotlin annotations
-dontwarn org.checkerframework.**
-dontwarn kotlin.annotations.jvm.**
-dontwarn javax.annotation.**
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@
*/
package com.google.android.exoplayer2.ui;

import static com.google.android.exoplayer2.util.Assertions.checkNotNull;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.trackselection.TrackSelectionUtil;
import com.google.android.exoplayer2.util.Assertions;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -97,7 +100,7 @@ public TrackSelectionDialogBuilder(
Context context, CharSequence title, DefaultTrackSelector trackSelector, int rendererIndex) {
this.context = context;
this.title = title;
this.mappedTrackInfo = Assertions.checkNotNull(trackSelector.getCurrentMappedTrackInfo());
this.mappedTrackInfo = checkNotNull(trackSelector.getCurrentMappedTrackInfo());
this.rendererIndex = rendererIndex;

TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
Expand Down Expand Up @@ -205,13 +208,65 @@ public TrackSelectionDialogBuilder setTrackNameProvider(
}

/** Builds the dialog. */
public AlertDialog build() {
public Dialog build() {
@Nullable Dialog dialog = buildForAndroidX();
return dialog == null ? buildForPlatform() : dialog;
}

private Dialog buildForPlatform() {
AlertDialog.Builder builder = new AlertDialog.Builder(context);

// Inflate with the builder's context to ensure the correct style is used.
LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
View dialogView = dialogInflater.inflate(R.layout.exo_track_selection_dialog, /* root= */ null);
Dialog.OnClickListener okClickListener = setUpDialogView(dialogView);

return builder
.setTitle(title)
.setView(dialogView)
.setPositiveButton(android.R.string.ok, okClickListener)
.setNegativeButton(android.R.string.cancel, null)
.create();
}

// Reflection calls can't verify null safety of return values or parameters.
@SuppressWarnings("nullness:argument.type.incompatible")
@Nullable
private Dialog buildForAndroidX() {
try {
// This method uses reflection to avoid a dependency on AndroidX appcompat that adds 800KB to
// the APK size even with shrinking. See https://issuetracker.google.com/161514204.
// LINT.IfChange
Class<?> builderClazz = Class.forName("androidx.appcompat.app.AlertDialog$Builder");
Constructor<?> builderConstructor = builderClazz.getConstructor(Context.class);
Object builder = builderConstructor.newInstance(context);

// Inflate with the builder's context to ensure the correct style is used.
Context builderContext = (Context) builderClazz.getMethod("getContext").invoke(builder);
LayoutInflater dialogInflater = LayoutInflater.from(builderContext);
View dialogView =
dialogInflater.inflate(R.layout.exo_track_selection_dialog, /* root= */ null);
Dialog.OnClickListener okClickListener = setUpDialogView(dialogView);

builderClazz.getMethod("setTitle", CharSequence.class).invoke(builder, title);
builderClazz.getMethod("setView", View.class).invoke(builder, dialogView);
builderClazz
.getMethod("setPositiveButton", int.class, DialogInterface.OnClickListener.class)
.invoke(builder, android.R.string.ok, okClickListener);
builderClazz
.getMethod("setNegativeButton", int.class, DialogInterface.OnClickListener.class)
.invoke(builder, android.R.string.cancel, null);
return (Dialog) builderClazz.getMethod("create").invoke(builder);
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
} catch (ClassNotFoundException e) {
// Expected if the AndroidX compat library is not available.
return null;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}

private Dialog.OnClickListener setUpDialogView(View dialogView) {
TrackSelectionView selectionView = dialogView.findViewById(R.id.exo_track_selection_view);
selectionView.setAllowMultipleOverrides(allowMultipleOverrides);
selectionView.setAllowAdaptiveSelections(allowAdaptiveSelections);
Expand All @@ -220,15 +275,7 @@ public AlertDialog build() {
selectionView.setTrackNameProvider(trackNameProvider);
}
selectionView.init(mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ null);
Dialog.OnClickListener okClickListener =
(dialog, which) ->
callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides());

return builder
.setTitle(title)
.setView(dialogView)
.setPositiveButton(android.R.string.ok, okClickListener)
.setNegativeButton(android.R.string.cancel, null)
.create();
return (dialog, which) ->
callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides());
}
}
1 change: 1 addition & 0 deletions library/ui/src/main/proguard-rules.txt

0 comments on commit 953db78

Please sign in to comment.