Merge "Modify pin widget dialog open-close animation." into sc-dev
This commit is contained in:
commit
f0e11cbbc3
|
@ -107,7 +107,7 @@
|
|||
android:value="true" />
|
||||
|
||||
<activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
|
||||
android:theme="@style/AppItemActivityTheme"
|
||||
android:theme="@style/AddItemActivityTheme"
|
||||
android:excludeFromRecents="true"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:exported="true">
|
||||
|
|
|
@ -48,7 +48,7 @@ import java.util.List;
|
|||
* User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in
|
||||
* the workspace and shows predictions on the whole hotseat
|
||||
*/
|
||||
public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
|
||||
public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements Insettable {
|
||||
|
||||
private static final int DEFAULT_CLOSE_DURATION = 200;
|
||||
protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
|
||||
|
@ -84,7 +84,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
|
||||
mSampleHotseat = findViewById(R.id.sample_prediction);
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
DeviceProfile grid = mActivityContext.getDeviceProfile();
|
||||
Rect padding = grid.getHotseatLayoutPadding();
|
||||
|
||||
mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
|
||||
|
@ -110,13 +110,13 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
|
||||
mHotseatEduController.moveHotseatItems();
|
||||
mHotseatEduController.finishOnboarding();
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
|
||||
mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
|
||||
}
|
||||
|
||||
private void onDismiss(View v) {
|
||||
mHotseatEduController.showDimissTip();
|
||||
mHotseatEduController.finishOnboarding();
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
|
||||
mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
|
||||
handleClose(true);
|
||||
}
|
||||
|
||||
|
@ -131,12 +131,12 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
int rightInset = insets.right - mInsets.right;
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
if (mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
|
||||
if (mActivityContext.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
|
||||
setPadding(leftInset, getPaddingTop(), rightInset, 0);
|
||||
mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
|
||||
mHotseatWrapper.getPaddingRight(), bottomInset);
|
||||
mHotseatWrapper.getLayoutParams().height =
|
||||
mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
|
||||
mActivityContext.getDeviceProfile().hotseatBarSizePx + insets.bottom;
|
||||
|
||||
} else {
|
||||
setPadding(0, getPaddingTop(), 0, 0);
|
||||
|
@ -178,7 +178,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
}
|
||||
|
||||
private void populatePreview(List<WorkspaceItemInfo> predictions) {
|
||||
for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
|
||||
for (int i = 0; i < mActivityContext.getDeviceProfile().numShownHotseatIcons; i++) {
|
||||
WorkspaceItemInfo info = predictions.get(i);
|
||||
PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
|
||||
icon.setEnabled(false);
|
||||
|
@ -194,13 +194,13 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
|||
*/
|
||||
public void show(List<WorkspaceItemInfo> predictions) {
|
||||
if (getParent() != null
|
||||
|| predictions.size() < mLauncher.getDeviceProfile().numShownHotseatIcons
|
||||
|| predictions.size() < mActivityContext.getDeviceProfile().numShownHotseatIcons
|
||||
|| mHotseatEduController == null) {
|
||||
return;
|
||||
}
|
||||
AbstractFloatingView.closeAllOpenViews(mLauncher);
|
||||
AbstractFloatingView.closeAllOpenViews(mActivityContext);
|
||||
attachToContainer();
|
||||
mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
|
||||
mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
|
||||
animateOpen();
|
||||
populatePreview(predictions);
|
||||
}
|
||||
|
|
|
@ -16,69 +16,85 @@
|
|||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/add_item_confirmation"
|
||||
<com.android.launcher3.dragndrop.AddItemDragLayer
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/add_item_drag_layer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="24dp"
|
||||
android:theme="?attr/widgetsTheme"
|
||||
android:orientation="vertical">
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:importantForAccessibility="no">
|
||||
|
||||
<TextView
|
||||
style="@style/TextHeadline"
|
||||
android:id="@+id/widget_appName"
|
||||
<com.android.launcher3.widget.AddItemWidgetsBottomSheet
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/add_item_bottom_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:ellipsize="end"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1" />
|
||||
android:background="@drawable/add_item_dialog_background"
|
||||
android:padding="24dp"
|
||||
android:theme="?attr/widgetsTheme"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingVertical="8dp"
|
||||
android:text="@string/add_item_request_drag_hint"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<include layout="@layout/widget_cell"
|
||||
android:id="@+id/widget_cell"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginVertical="16dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:padding="8dp"
|
||||
android:orientation="horizontal">
|
||||
<Button
|
||||
style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
<TextView
|
||||
style="@style/TextHeadline"
|
||||
android:id="@+id/widget_appName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:onClick="onCancelClick"
|
||||
android:text="@android:string/cancel" />
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:ellipsize="end"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1" />
|
||||
|
||||
<Space
|
||||
android:layout_width="4dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:onClick="onPlaceAutomaticallyClick"
|
||||
android:text="@string/add_to_home_screen" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingVertical="8dp"
|
||||
android:text="@string/add_item_request_drag_hint"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:alpha="0.7"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<include layout="@layout/widget_cell"
|
||||
android:id="@+id/widget_cell"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginVertical="16dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:padding="8dp"
|
||||
android:orientation="horizontal">
|
||||
<Button
|
||||
style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:onClick="onCancelClick"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<Space
|
||||
android:layout_width="4dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:onClick="onPlaceAutomaticallyClick"
|
||||
android:text="@string/add_to_home_screen"/>
|
||||
</LinearLayout>
|
||||
</com.android.launcher3.widget.AddItemWidgetsBottomSheet>
|
||||
|
||||
</com.android.launcher3.dragndrop.AddItemDragLayer>
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* Copyright (C) 2008 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.
|
||||
|
@ -18,11 +18,7 @@
|
|||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
|
||||
<style name="AddItemActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
|
||||
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
|
||||
<item name="android:windowBackground">@drawable/add_item_dialog_background</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -149,12 +149,6 @@
|
|||
<style name="AppTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark.DarkMainColor" />
|
||||
<style name="AppTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark.DarkText" />
|
||||
|
||||
<style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
|
||||
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
|
||||
<item name="android:windowBackground">@drawable/add_item_dialog_background</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
|
||||
<item name="android:navigationBarColor">?android:colorPrimaryDark</item>
|
||||
<item name="preferenceTheme">@style/HomeSettingsPreferenceTheme</item>
|
||||
|
@ -291,4 +285,8 @@
|
|||
<style name="Widget.DeviceDefault.Button.Rounded.Colored" parent="@android:style/Widget.DeviceDefault.Button.Colored">
|
||||
<item name="android:background">@drawable/add_item_dialog_button_background</item>
|
||||
</style>
|
||||
|
||||
<style name="AddItemActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
|
||||
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -63,7 +63,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
|||
TYPE_DRAG_DROP_POPUP,
|
||||
TYPE_TASK_MENU,
|
||||
TYPE_OPTIONS_POPUP,
|
||||
TYPE_ICON_SURFACE
|
||||
TYPE_ICON_SURFACE,
|
||||
TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FloatingViewType {}
|
||||
|
@ -84,11 +85,13 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
|||
public static final int TYPE_OPTIONS_POPUP = 1 << 12;
|
||||
public static final int TYPE_ICON_SURFACE = 1 << 13;
|
||||
|
||||
public static final int TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP = 1 << 14;
|
||||
|
||||
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
|
||||
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
|
||||
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
|
||||
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
|
||||
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP;
|
||||
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP;
|
||||
|
||||
// Type of popups which should be kept open during launcher rebind
|
||||
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
|
||||
|
|
|
@ -30,6 +30,7 @@ import android.content.ClipData;
|
|||
import android.content.ClipDescription;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps.PinItemRequest;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
|
@ -37,7 +38,6 @@ import android.graphics.Rect;
|
|||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.DragShadowBuilder;
|
||||
|
@ -56,6 +56,8 @@ import com.android.launcher3.model.ItemInstallQueue;
|
|||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.pm.PinRequestHelper;
|
||||
import com.android.launcher3.util.SystemUiController;
|
||||
import com.android.launcher3.views.AbstractSlideInView;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||
|
@ -69,8 +71,12 @@ import com.android.launcher3.widget.WidgetManagerHelper;
|
|||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Activity to show pin widget dialog.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
|
||||
public class AddItemActivity extends BaseActivity
|
||||
implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener {
|
||||
|
||||
private static final int SHADOW_SIZE = 10;
|
||||
|
||||
|
@ -82,6 +88,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
private PinItemRequest mRequest;
|
||||
private LauncherAppState mApp;
|
||||
private InvariantDeviceProfile mIdp;
|
||||
private BaseDragLayer<AddItemActivity> mDragLayer;
|
||||
|
||||
private WidgetCell mWidgetCell;
|
||||
|
||||
|
@ -111,6 +118,14 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext());
|
||||
|
||||
setContentView(R.layout.add_item_confirmation_activity);
|
||||
// Set flag to allow activity to draw over navigation and status bar.
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
mDragLayer = findViewById(R.id.add_item_drag_layer);
|
||||
mDragLayer.recreateControllers();
|
||||
mDragLayer.setInsets(mDeviceProfile.getInsets());
|
||||
AbstractSlideInView<AddItemActivity> slideInView = findViewById(R.id.add_item_bottom_sheet);
|
||||
slideInView.addOnCloseListener(this);
|
||||
mWidgetCell = findViewById(R.id.widget_cell);
|
||||
|
||||
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
|
||||
|
@ -135,6 +150,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
|
||||
TextView widgetAppName = findViewById(R.id.widget_appName);
|
||||
widgetAppName.setText(getApplicationInfo().labelRes);
|
||||
|
||||
setupNavBarColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -338,7 +355,20 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
|
||||
@Override
|
||||
public BaseDragLayer getDragLayer() {
|
||||
throw new UnsupportedOperationException();
|
||||
return mDragLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlideInViewClosed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
protected void setupNavBarColor() {
|
||||
boolean isSheetDark = (getApplicationContext().getResources().getConfiguration().uiMode
|
||||
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
||||
getSystemUiController().updateUiState(
|
||||
SystemUiController.UI_STATE_BASE_WINDOW,
|
||||
isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
|
||||
}
|
||||
|
||||
private void logCommand(StatsLogManager.EventEnum command) {
|
||||
|
@ -346,15 +376,4 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
|
||||
.log(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
View view = getWindow().getDecorView();
|
||||
WindowManager.LayoutParams layoutParams =
|
||||
(WindowManager.LayoutParams) view.getLayoutParams();
|
||||
layoutParams.gravity = Gravity.BOTTOM;
|
||||
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
getWindowManager().updateViewLayout(view, layoutParams);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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 com.android.launcher3.dragndrop;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
|
||||
/**
|
||||
* Drag layer for {@link AddItemActivity}.
|
||||
*/
|
||||
public class AddItemDragLayer extends BaseDragLayer<AddItemActivity> {
|
||||
|
||||
public AddItemDragLayer(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, /*alphaChannelCount= */ 1);
|
||||
}
|
||||
|
||||
public AddItemDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
|
||||
super(context, attrs, alphaChannelCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateControllers() {
|
||||
mControllers = new TouchController[] {};
|
||||
}
|
||||
}
|
|
@ -31,17 +31,21 @@ import android.view.View;
|
|||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.touch.BaseSwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extension of AbstractFloatingView with common methods for sliding in from bottom
|
||||
* Extension of {@link AbstractFloatingView} with common methods for sliding in from bottom.
|
||||
*
|
||||
* @param <T> Type of ActivityContext inflating this view.
|
||||
*/
|
||||
public abstract class AbstractSlideInView extends AbstractFloatingView
|
||||
implements SingleAxisSwipeDetector.Listener {
|
||||
public abstract class AbstractSlideInView<T extends Context & ActivityContext>
|
||||
extends AbstractFloatingView implements SingleAxisSwipeDetector.Listener {
|
||||
|
||||
protected static final Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
|
||||
new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
|
||||
|
@ -59,7 +63,8 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
|
||||
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
|
||||
|
||||
protected final Launcher mLauncher;
|
||||
protected final T mActivityContext;
|
||||
|
||||
protected final SingleAxisSwipeDetector mSwipeDetector;
|
||||
protected final ObjectAnimator mOpenCloseAnimator;
|
||||
|
||||
|
@ -71,10 +76,11 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
|
||||
|
||||
protected boolean mNoIntercept;
|
||||
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
|
||||
|
||||
public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mActivityContext = ActivityContext.lookupContext(context);
|
||||
|
||||
mScrollInterpolator = Interpolators.SCROLL_CUBIC;
|
||||
mSwipeDetector = new SingleAxisSwipeDetector(context, this,
|
||||
|
@ -88,8 +94,8 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
announceAccessibilityChanges();
|
||||
}
|
||||
});
|
||||
int scrimColor = getScrimColor(mLauncher);
|
||||
mColorScrim = scrimColor != -1 ? createColorScrim(mLauncher, scrimColor) : null;
|
||||
int scrimColor = getScrimColor(context);
|
||||
mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
|
||||
}
|
||||
|
||||
protected void attachToContainer() {
|
||||
|
@ -120,8 +126,8 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
return false;
|
||||
}
|
||||
|
||||
int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
|
||||
SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
|
||||
int directionsToDetectScroll = mSwipeDetector.isIdleState()
|
||||
? SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
|
||||
mSwipeDetector.setDetectableScrollConditions(
|
||||
directionsToDetectScroll, false);
|
||||
mSwipeDetector.onTouchEvent(ev);
|
||||
|
@ -176,6 +182,11 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
}
|
||||
}
|
||||
|
||||
/** Registers an {@link OnCloseListener}. */
|
||||
public void addOnCloseListener(OnCloseListener listener) {
|
||||
mOnCloseListeners.add(listener);
|
||||
}
|
||||
|
||||
protected void handleClose(boolean animate, long defaultDuration) {
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
|
@ -210,10 +221,11 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
if (mColorScrim != null) {
|
||||
getPopupContainer().removeView(mColorScrim);
|
||||
}
|
||||
mOnCloseListeners.forEach(OnCloseListener::onSlideInViewClosed);
|
||||
}
|
||||
|
||||
protected BaseDragLayer getPopupContainer() {
|
||||
return mLauncher.getDragLayer();
|
||||
return mActivityContext.getDragLayer();
|
||||
}
|
||||
|
||||
protected View createColorScrim(Context context, int bgColor) {
|
||||
|
@ -227,4 +239,15 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to report that the {@link AbstractSlideInView} has closed.
|
||||
*/
|
||||
public interface OnCloseListener {
|
||||
|
||||
/**
|
||||
* Called when {@link AbstractSlideInView} closes.
|
||||
*/
|
||||
void onSlideInViewClosed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import com.android.launcher3.statemanager.StateManager.StateListener;
|
|||
/**
|
||||
* On boarding flow for users right after setting up work profile
|
||||
*/
|
||||
public class WorkEduView extends AbstractSlideInView
|
||||
public class WorkEduView extends AbstractSlideInView<Launcher>
|
||||
implements Insettable, StateListener<LauncherState> {
|
||||
|
||||
private static final int DEFAULT_CLOSE_DURATION = 200;
|
||||
|
@ -76,14 +76,15 @@ public class WorkEduView extends AbstractSlideInView
|
|||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
mLauncher.getSharedPrefs().edit().putInt(KEY_WORK_EDU_STEP, mNextWorkEduStep).apply();
|
||||
mActivityContext.getSharedPrefs().edit()
|
||||
.putInt(KEY_WORK_EDU_STEP, mNextWorkEduStep).apply();
|
||||
handleClose(true, DEFAULT_CLOSE_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCloseComplete() {
|
||||
super.onCloseComplete();
|
||||
mLauncher.getStateManager().removeStateListener(this);
|
||||
mActivityContext.getStateManager().removeStateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,13 +117,14 @@ public class WorkEduView extends AbstractSlideInView
|
|||
animator.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mContentText.setText(mLauncher.getString(R.string.work_profile_edu_work_apps));
|
||||
mContentText.setText(
|
||||
mActivityContext.getString(R.string.work_profile_edu_work_apps));
|
||||
ObjectAnimator.ofFloat(mContentText, ALPHA, 1).start();
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} else {
|
||||
mContentText.setText(mLauncher.getString(R.string.work_profile_edu_work_apps));
|
||||
mContentText.setText(mActivityContext.getString(R.string.work_profile_edu_work_apps));
|
||||
}
|
||||
mNextWorkEduStep = WORK_EDU_WORK_APPS;
|
||||
mProceedButton.setOnClickListener(v -> handleClose(true));
|
||||
|
@ -142,7 +144,7 @@ public class WorkEduView extends AbstractSlideInView
|
|||
private void show() {
|
||||
attachToContainer();
|
||||
animateOpen();
|
||||
mLauncher.getStateManager().addStateListener(this);
|
||||
mActivityContext.getStateManager().addStateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,7 +170,7 @@ public class WorkEduView extends AbstractSlideInView
|
|||
}
|
||||
|
||||
private AllAppsPagedView getAllAppsPagedView() {
|
||||
View v = mLauncher.getAppsView().getContentView();
|
||||
View v = mActivityContext.getAppsView().getContentView();
|
||||
return (v instanceof AllAppsPagedView) ? (AllAppsPagedView) v : null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 com.android.launcher3.widget;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.dragndrop.AddItemActivity;
|
||||
import com.android.launcher3.views.AbstractSlideInView;
|
||||
|
||||
/**
|
||||
* Bottom sheet for the pin widget.
|
||||
*/
|
||||
public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivity>
|
||||
implements Insettable {
|
||||
|
||||
private static final int DEFAULT_CLOSE_DURATION = 200;
|
||||
|
||||
private Rect mInsets;
|
||||
private Configuration mCurrentConfiguration;
|
||||
|
||||
public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mContent = this;
|
||||
mInsets = new Rect();
|
||||
mCurrentConfiguration = new Configuration(getResources().getConfiguration());
|
||||
animateOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
setTranslationShift(mTranslationShift);
|
||||
}
|
||||
|
||||
private void animateOpen() {
|
||||
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
|
||||
return;
|
||||
}
|
||||
mIsOpen = true;
|
||||
mOpenCloseAnimator.setValues(
|
||||
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
|
||||
mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
|
||||
mOpenCloseAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
handleClose(animate, DEFAULT_CLOSE_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(@FloatingViewType int type) {
|
||||
return (type & TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
// Extend behind left, right, and bottom insets.
|
||||
int leftInset = insets.left - mInsets.left;
|
||||
int rightInset = insets.right - mInsets.right;
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
setPadding(leftInset, getPaddingTop(), rightInset, bottomInset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
if (mCurrentConfiguration.orientation != newConfig.orientation) {
|
||||
mInsets.setEmpty();
|
||||
}
|
||||
mCurrentConfiguration.updateFrom(newConfig);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.android.launcher3.DragSource;
|
||||
import com.android.launcher3.DropTarget.DragObject;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
|
@ -42,11 +43,10 @@ import com.android.launcher3.views.AbstractSlideInView;
|
|||
/**
|
||||
* Base class for various widgets popup
|
||||
*/
|
||||
public abstract class BaseWidgetSheet extends AbstractSlideInView
|
||||
public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
implements OnClickListener, OnLongClickListener, DragSource,
|
||||
PopupDataProvider.PopupDataChangeListener {
|
||||
|
||||
|
||||
/* Touch handling related member variables. */
|
||||
private Toast mWidgetInstructionToast;
|
||||
|
||||
|
@ -62,13 +62,13 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView
|
|||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mLauncher.getPopupDataProvider().setChangeListener(this);
|
||||
mActivityContext.getPopupDataProvider().setChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mLauncher.getPopupDataProvider().setChangeListener(null);
|
||||
mActivityContext.getPopupDataProvider().setChangeListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,7 +91,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView
|
|||
public boolean onLongClick(View v) {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
|
||||
v.cancelLongPress();
|
||||
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
|
||||
if (!ItemLongClickListener.canStartDrag(mActivityContext)) return false;
|
||||
|
||||
if (v instanceof WidgetCell) {
|
||||
return beginDraggingWidget((WidgetCell) v);
|
||||
|
@ -160,7 +160,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView
|
|||
}
|
||||
|
||||
protected SystemUiController getSystemUiController() {
|
||||
return mLauncher.getSystemUiController();
|
||||
return mActivityContext.getSystemUiController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -83,7 +83,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
|
|||
setWillNotDraw(false);
|
||||
mInsets = new Rect();
|
||||
mContent = this;
|
||||
DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
// Set the max table height to 2 / 3 of the grid height so that the bottom picker won't
|
||||
// take over the entire view vertically.
|
||||
mMaxTableHeight = deviceProfile.inv.numRows * 2 / 3 * deviceProfile.cellHeightPx;
|
||||
|
@ -97,7 +97,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
|
|||
int paddingPx = 2 * getResources().getDimensionPixelOffset(
|
||||
R.dimen.widget_cell_horizontal_padding);
|
||||
int maxHorizontalSpan = findViewById(R.id.widgets_table).getMeasuredWidth()
|
||||
/ (mLauncher.getDeviceProfile().cellWidthPx + paddingPx);
|
||||
/ (mActivityContext.getDeviceProfile().cellWidthPx + paddingPx);
|
||||
|
||||
if (mMaxHorizontalSpan != maxHorizontalSpan) {
|
||||
// Ensure the table layout is showing widgets in the right column after measure.
|
||||
|
@ -134,7 +134,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
|
|||
|
||||
@Override
|
||||
public void onWidgetsBound() {
|
||||
List<WidgetItem> widgets = mLauncher.getPopupDataProvider().getWidgetsForPackageUser(
|
||||
List<WidgetItem> widgets = mActivityContext.getPopupDataProvider().getWidgetsForPackageUser(
|
||||
new PackageUserKey(
|
||||
mOriginalItemInfo.getTargetComponent().getPackageName(),
|
||||
mOriginalItemInfo.user));
|
||||
|
@ -148,7 +148,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
|
|||
row.forEach(widgetItem -> {
|
||||
WidgetCell widget = addItemCell(tableRow);
|
||||
widget.setPreviewSize(widgetItem.spanX, widgetItem.spanY);
|
||||
widget.applyFromCellItem(widgetItem, LauncherAppState.getInstance(mLauncher)
|
||||
widget.applyFromCellItem(widgetItem, LauncherAppState.getInstance(mActivityContext)
|
||||
.getWidgetCache());
|
||||
widget.ensurePreview();
|
||||
widget.setVisibility(View.VISIBLE);
|
||||
|
|
|
@ -207,7 +207,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
onWidgetsBound();
|
||||
|
||||
mSearchAndRecommendationViewHolder.mSearchBar.initialize(
|
||||
mLauncher.getPopupDataProvider(), /* searchModeListener= */ this);
|
||||
mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
|
||||
|
||||
if (!hasSeenEducationTip()) {
|
||||
addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
|
||||
|
@ -271,7 +271,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mLauncher.getAppWidgetHost().addProviderChangeListener(this);
|
||||
mActivityContext.getAppWidgetHost().addProviderChangeListener(this);
|
||||
notifyWidgetProvidersChanged();
|
||||
onRecommendedWidgetsBound();
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
|
||||
mActivityContext.getAppWidgetHost().removeProviderChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -327,7 +327,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
}
|
||||
|
||||
private void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
int widthUsed;
|
||||
if (mInsets.bottom > 0) {
|
||||
widthUsed = mInsets.left + mInsets.right;
|
||||
|
@ -350,7 +350,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
|
||||
int previousMaxSpansPerRow = mMaxSpansPerRow;
|
||||
mMaxSpansPerRow = getMeasuredWidth()
|
||||
/ (mLauncher.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
|
||||
/ (mActivityContext.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
|
||||
|
||||
if (previousMaxSpansPerRow != mMaxSpansPerRow) {
|
||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
|
||||
|
@ -383,7 +383,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
|
||||
@Override
|
||||
public void notifyWidgetProvidersChanged() {
|
||||
mLauncher.refreshAndBindWidgetsForPackageUser(null);
|
||||
mActivityContext.refreshAndBindWidgetsForPackageUser(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -391,7 +391,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
if (mIsInSearchMode) {
|
||||
return;
|
||||
}
|
||||
List<WidgetsListBaseEntry> allWidgets = mLauncher.getPopupDataProvider().getAllWidgets();
|
||||
List<WidgetsListBaseEntry> allWidgets =
|
||||
mActivityContext.getPopupDataProvider().getAllWidgets();
|
||||
|
||||
AdapterHolder primaryUserAdapterHolder = mAdapters.get(AdapterHolder.PRIMARY);
|
||||
primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
|
||||
|
@ -468,12 +469,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
return;
|
||||
}
|
||||
List<WidgetItem> recommendedWidgets =
|
||||
mLauncher.getPopupDataProvider().getRecommendedWidgets();
|
||||
mActivityContext.getPopupDataProvider().getRecommendedWidgets();
|
||||
WidgetsRecommendationTableLayout table =
|
||||
mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
|
||||
if (recommendedWidgets.size() > 0) {
|
||||
float maxTableHeight =
|
||||
(mLauncher.getDeviceProfile().availableHeightPx - mTabsHeight
|
||||
(mActivityContext.getDeviceProfile().availableHeightPx - mTabsHeight
|
||||
- getHeaderViewHeight()) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
|
||||
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
|
||||
|
@ -614,10 +615,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
}
|
||||
|
||||
private void showEducationTipOnView(View view) {
|
||||
mLauncher.getSharedPrefs().edit().putBoolean(WIDGETS_EDUCATION_TIP_SEEN, true).apply();
|
||||
mActivityContext.getSharedPrefs().edit()
|
||||
.putBoolean(WIDGETS_EDUCATION_TIP_SEEN, true).apply();
|
||||
int[] coords = new int[2];
|
||||
view.getLocationOnScreen(coords);
|
||||
ArrowTipView arrowTipView = new ArrowTipView(mLauncher);
|
||||
ArrowTipView arrowTipView = new ArrowTipView(mActivityContext);
|
||||
arrowTipView.showAtLocation(
|
||||
getContext().getString(R.string.long_press_widget_to_add),
|
||||
/* arrowXCoord= */coords[0] + view.getWidth() / 2,
|
||||
|
@ -653,7 +655,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||
}
|
||||
|
||||
private boolean hasSeenEducationTip() {
|
||||
return mLauncher.getSharedPrefs().getBoolean(WIDGETS_EDUCATION_TIP_SEEN, false)
|
||||
return mActivityContext.getSharedPrefs().getBoolean(WIDGETS_EDUCATION_TIP_SEEN, false)
|
||||
|| Utilities.IS_RUNNING_IN_TEST_HARNESS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue