ExtensionCompat.java
/*
* Copyright 2020 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 androidx.window;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.window.extensions.ExtensionDisplayFeature;
import androidx.window.extensions.ExtensionFoldingFeature;
import androidx.window.extensions.ExtensionInterface;
import androidx.window.extensions.ExtensionProvider;
import androidx.window.extensions.ExtensionWindowLayoutInfo;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/** Compatibility wrapper for extension versions v1.0+. */
final class ExtensionCompat implements ExtensionInterfaceCompat {
static final boolean DEBUG = false;
private static final String TAG = "ExtensionVersionCompat";
@VisibleForTesting
final ExtensionInterface mWindowExtension;
final ExtensionAdapter mAdapter;
ExtensionCompat(Context context) {
this(ExtensionProvider.getExtensionImpl(context), new ExtensionAdapter());
if (mWindowExtension == null) {
throw new IllegalArgumentException("Extension provider returned null");
}
}
@VisibleForTesting
ExtensionCompat(ExtensionInterface extension, ExtensionAdapter adapter) {
// Empty implementation to avoid null checks
mWindowExtension = extension;
mAdapter = adapter;
}
@Override
public void setExtensionCallback(@NonNull ExtensionCallbackInterface extensionCallback) {
ExtensionTranslatingCallback translatingCallback = new ExtensionTranslatingCallback(
extensionCallback, mAdapter);
mWindowExtension.setExtensionCallback(translatingCallback);
}
@Override
public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
mWindowExtension.onWindowLayoutChangeListenerAdded(activity);
}
@Override
public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
mWindowExtension.onWindowLayoutChangeListenerRemoved(activity);
}
@Override
public void onDeviceStateListenersChanged(boolean isEmpty) {
mWindowExtension.onDeviceStateListenersChanged(isEmpty);
}
@Nullable
static Version getExtensionVersion() {
try {
String vendorVersion = ExtensionProvider.getApiVersion();
return !TextUtils.isEmpty(vendorVersion) ? Version.parse(vendorVersion) : null;
} catch (NoClassDefFoundError e) {
if (DEBUG) {
Log.d(TAG, "Extension version not found");
}
return null;
} catch (UnsupportedOperationException e) {
if (DEBUG) {
Log.d(TAG, "Stub Extension");
}
return null;
}
}
/** Verifies that extension implementation corresponds to the interface of the version. */
@Override
@SuppressWarnings("unused")
public boolean validateExtensionInterface() {
try {
// extension.setExtensionCallback(ExtensionInterface.ExtensionCallback);
Method methodSetExtensionCallback = mWindowExtension.getClass().getMethod(
"setExtensionCallback", ExtensionInterface.ExtensionCallback.class);
Class<?> rSetExtensionCallback = methodSetExtensionCallback.getReturnType();
if (!rSetExtensionCallback.equals(void.class)) {
throw new NoSuchMethodException("Illegal return type for 'setExtensionCallback': "
+ rSetExtensionCallback);
}
// extension.onDeviceStateListenersChanged(boolean);
mWindowExtension.onDeviceStateListenersChanged(true /* empty */);
// extension.onWindowLayoutChangeListenerAdded(Activity);
Method methodRegisterWindowLayoutChangeListener = mWindowExtension.getClass()
.getMethod("onWindowLayoutChangeListenerAdded", Activity.class);
Class<?> rtRegisterWindowLayoutChangeListener =
methodRegisterWindowLayoutChangeListener.getReturnType();
if (!rtRegisterWindowLayoutChangeListener.equals(void.class)) {
throw new NoSuchMethodException(
"Illegal return type for 'onWindowLayoutChangeListenerAdded': "
+ rtRegisterWindowLayoutChangeListener);
}
// extension.onWindowLayoutChangeListenerRemoved(Activity);
Method methodUnregisterWindowLayoutChangeListener = mWindowExtension.getClass()
.getMethod("onWindowLayoutChangeListenerRemoved", Activity.class);
Class<?> rtUnregisterWindowLayoutChangeListener =
methodUnregisterWindowLayoutChangeListener.getReturnType();
if (!rtUnregisterWindowLayoutChangeListener.equals(void.class)) {
throw new NoSuchMethodException(
"Illegal return type for 'onWindowLayoutChangeListenerRemoved': "
+ rtUnregisterWindowLayoutChangeListener);
}
// {@link ExtensionFoldingFeature} constructor
ExtensionFoldingFeature displayFoldingFeature =
new ExtensionFoldingFeature(new Rect(0, 0, 100, 0),
ExtensionFoldingFeature.TYPE_FOLD,
ExtensionFoldingFeature.STATE_FLAT);
// displayFoldFeature.getBounds()
Rect tmpRect = displayFoldingFeature.getBounds();
// displayFoldFeature.getState()
int tmpState = displayFoldingFeature.getState();
// displayFoldFeature.getType()
int tmpType = displayFoldingFeature.getType();
// ExtensionWindowLayoutInfo constructor
ExtensionWindowLayoutInfo windowLayoutInfo =
new ExtensionWindowLayoutInfo(new ArrayList<>());
// windowLayoutInfo.getDisplayFeatures()
List<ExtensionDisplayFeature> tmpDisplayFeatures =
windowLayoutInfo.getDisplayFeatures();
return true;
} catch (Throwable t) {
if (DEBUG) {
Log.e(TAG, "Extension implementation doesn't conform to interface version "
+ getExtensionVersion() + ", error: " + t);
}
return false;
}
}
}