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 static androidx.window.DeviceState.POSTURE_MAX_KNOWN;
import static androidx.window.DeviceState.POSTURE_UNKNOWN;
import static androidx.window.ExtensionHelper.DEBUG;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.extensions.ExtensionDeviceState;
import androidx.window.extensions.ExtensionDisplayFeature;
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 {
private static final String TAG = "ExtensionVersionCompat";
private ExtensionInterface mWindowExtension;
ExtensionCompat(Context context) {
mWindowExtension = ExtensionProvider.getExtensionImpl(context);
if (mWindowExtension == null) {
throw new IllegalArgumentException("Extension provider returned null");
}
}
@Override
public void setExtensionCallback(@NonNull ExtensionCallbackInterface extensionCallback) {
mWindowExtension.setExtensionCallback(new ExtensionInterface.ExtensionCallback() {
@Override
@SuppressLint("SyntheticAccessor")
public void onDeviceStateChanged(@NonNull ExtensionDeviceState newDeviceState) {
extensionCallback.onDeviceStateChanged(deviceStateFromExtension(newDeviceState));
}
@Override
@SuppressLint("SyntheticAccessor")
public void onWindowLayoutChanged(@NonNull IBinder windowToken,
@NonNull ExtensionWindowLayoutInfo newLayout) {
extensionCallback.onWindowLayoutChanged(windowToken,
windowLayoutInfoFromExtension(newLayout));
}
});
}
@NonNull
@Override
public WindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
ExtensionWindowLayoutInfo windowLayoutInfo =
mWindowExtension.getWindowLayoutInfo(windowToken);
return windowLayoutInfoFromExtension(windowLayoutInfo);
}
@Override
public void onWindowLayoutChangeListenerAdded(@NonNull IBinder windowToken) {
mWindowExtension.onWindowLayoutChangeListenerAdded(windowToken);
}
@Override
public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder windowToken) {
mWindowExtension.onWindowLayoutChangeListenerRemoved(windowToken);
}
@NonNull
@Override
public DeviceState getDeviceState() {
ExtensionDeviceState deviceState = mWindowExtension.getDeviceState();
return deviceStateFromExtension(deviceState);
}
@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;
}
}
private static DisplayFeature displayFeatureFromExtension(ExtensionDisplayFeature feature) {
return new DisplayFeature(feature.getBounds(), feature.getType());
}
@NonNull
private static List<DisplayFeature> displayFeatureListFromExtension(
ExtensionWindowLayoutInfo extensionWindowLayoutInfo) {
List<DisplayFeature> displayFeatures = new ArrayList<>();
List<ExtensionDisplayFeature> extensionFeatures =
extensionWindowLayoutInfo.getDisplayFeatures();
if (extensionFeatures == null) {
return displayFeatures;
}
for (ExtensionDisplayFeature extensionFeature : extensionFeatures) {
displayFeatures.add(displayFeatureFromExtension(extensionFeature));
}
return displayFeatures;
}
@NonNull
private static WindowLayoutInfo windowLayoutInfoFromExtension(
@Nullable ExtensionWindowLayoutInfo extensionInfo) {
if (extensionInfo == null) {
return new WindowLayoutInfo(new ArrayList<>());
}
List<DisplayFeature> displayFeatures = displayFeatureListFromExtension(extensionInfo);
return new WindowLayoutInfo(displayFeatures);
}
@DeviceState.Posture
private static int postureFromExtension(ExtensionDeviceState extensionDeviceState) {
int extensionPosture = extensionDeviceState.getPosture();
if (extensionPosture > POSTURE_MAX_KNOWN) {
if (DEBUG) {
Log.d(TAG, "Unknown posture reported, WindowManager library should be updated");
}
return POSTURE_UNKNOWN;
}
return extensionPosture;
}
@NonNull
private static DeviceState deviceStateFromExtension(
@Nullable ExtensionDeviceState extensionDeviceState) {
if (extensionDeviceState == null) {
return new DeviceState(POSTURE_UNKNOWN);
}
int posture = postureFromExtension(extensionDeviceState);
return new DeviceState(posture);
}
/** Verify 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.getDeviceState()
ExtensionDeviceState tmpDeviceState = mWindowExtension.getDeviceState();
// extension.onDeviceStateListenersChanged(boolean);
mWindowExtension.onDeviceStateListenersChanged(true /* empty */);
// extension.getWindowLayoutInfo(IBinder)
Method methodGetWindowLayoutInfo = mWindowExtension.getClass()
.getMethod("getWindowLayoutInfo", IBinder.class);
Class<?> rtGetWindowLayoutInfo = methodGetWindowLayoutInfo.getReturnType();
if (!rtGetWindowLayoutInfo.equals(ExtensionWindowLayoutInfo.class)) {
throw new NoSuchMethodException(
"Illegal return type for 'getWindowLayoutInfo': "
+ rtGetWindowLayoutInfo);
}
// extension.onWindowLayoutChangeListenerAdded(IBinder);
Method methodRegisterWindowLayoutChangeListener = mWindowExtension.getClass()
.getMethod("onWindowLayoutChangeListenerAdded", IBinder.class);
Class<?> rtRegisterWindowLayoutChangeListener =
methodRegisterWindowLayoutChangeListener.getReturnType();
if (!rtRegisterWindowLayoutChangeListener.equals(void.class)) {
throw new NoSuchMethodException(
"Illegal return type for 'onWindowLayoutChangeListenerAdded': "
+ rtRegisterWindowLayoutChangeListener);
}
// extension.onWindowLayoutChangeListenerRemoved(IBinder);
Method methodUnregisterWindowLayoutChangeListener = mWindowExtension.getClass()
.getMethod("onWindowLayoutChangeListenerRemoved", IBinder.class);
Class<?> rtUnregisterWindowLayoutChangeListener =
methodUnregisterWindowLayoutChangeListener.getReturnType();
if (!rtUnregisterWindowLayoutChangeListener.equals(void.class)) {
throw new NoSuchMethodException(
"Illegal return type for 'onWindowLayoutChangeListenerRemoved': "
+ rtUnregisterWindowLayoutChangeListener);
}
// ExtensionDeviceState constructor
ExtensionDeviceState deviceState = new ExtensionDeviceState(
ExtensionDeviceState.POSTURE_UNKNOWN);
// deviceState.getPosture();
int tmpPosture = deviceState.getPosture();
// ExtensionDisplayFeature constructor
ExtensionDisplayFeature displayFeature =
new ExtensionDisplayFeature(new Rect(0, 0, 0, 0),
ExtensionDisplayFeature.TYPE_FOLD);
// displayFeature.getBounds()
Rect tmpRect = displayFeature.getBounds();
// displayFeature.getType()
int tmpType = displayFeature.getType();
// ExtensionWindowLayoutInfo constructor
ExtensionWindowLayoutInfo windowLayoutInfo =
new ExtensionWindowLayoutInfo(new ArrayList<>());
// windowLayoutInfo.getDisplayFeatures()
List<ExtensionDisplayFeature> tmpDisplayFeatures =
windowLayoutInfo.getDisplayFeatures();
return true;
} catch (Exception e) {
if (DEBUG) {
Log.e(TAG, "Extension implementation doesn't conform to interface version "
+ getExtensionVersion() + ", error: " + e);
}
return false;
}
}
}