Align fallback result query with result text
screenshot: https://screenshot.googleplex.com/6Daj5vdmz2jmznX bug: 169438169 test: Manual Change-Id: Ie621ed3c834aec5e9467607da4f685d05d152183
This commit is contained in:
parent
73ae75474e
commit
4b7f38b8fa
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Project
|
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Projectza
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -67,7 +67,6 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start|center_vertical"
|
android:layout_gravity="start|center_vertical"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:text="@string/search_action_try_now">
|
android:text="@string/search_action_try_now">
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
|
@ -12,27 +12,24 @@
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<com.android.launcher3.views.SearchResultSuggestRow xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.android.launcher3.views.SearchResultIconRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
style="@style/TextHeadline"
|
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/section_title"
|
style="@style/BaseIcon"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="horizontal"
|
||||||
android:gravity="center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
android:padding="4dp"
|
android:textAlignment="viewStart"
|
||||||
android:minHeight="48dp"
|
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp">
|
android:textSize="16sp"
|
||||||
|
android:padding="@dimen/dynamic_grid_edge_margin"
|
||||||
|
launcher:iconDisplay="hero_app"
|
||||||
|
android:drawableTint="?android:attr/textColorPrimary"
|
||||||
|
launcher:customIcon="@drawable/ic_allapps_search"
|
||||||
|
launcher:iconSizeOverride="24dp"
|
||||||
|
launcher:matchTextInsetWithQuery="true"
|
||||||
|
launcher:layoutHorizontal="true"
|
||||||
|
android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
|
||||||
|
>
|
||||||
|
|
||||||
<TextView
|
</com.android.launcher3.views.SearchResultIconRow>
|
||||||
android:id="@+id/title"
|
|
||||||
style="@style/TextTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
</com.android.launcher3.views.SearchResultSuggestRow>
|
|
|
@ -18,6 +18,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="@style/TextHeadline"
|
android:fontFamily="@style/TextHeadline"
|
||||||
|
android:textStyle="bold"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
|
@ -69,6 +69,13 @@
|
||||||
<attr name="folderDotColor" />
|
<attr name="folderDotColor" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="SearchResultIconRow">
|
||||||
|
<attr name="customIcon" format="reference" />
|
||||||
|
<attr name="matchTextInsetWithQuery" format="boolean" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<declare-styleable name="ShadowInfo">
|
<declare-styleable name="ShadowInfo">
|
||||||
<attr name="ambientShadowColor" format="color" />
|
<attr name="ambientShadowColor" format="color" />
|
||||||
<attr name="ambientShadowBlur" format="dimension" />
|
<attr name="ambientShadowBlur" format="dimension" />
|
||||||
|
|
|
@ -627,7 +627,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
/**
|
/**
|
||||||
* Sets the icon for this view based on the layout direction.
|
* Sets the icon for this view based on the layout direction.
|
||||||
*/
|
*/
|
||||||
private void setIcon(Drawable icon) {
|
protected void setIcon(Drawable icon) {
|
||||||
if (mIsIconVisible) {
|
if (mIsIconVisible) {
|
||||||
applyCompoundDrawables(icon);
|
applyCompoundDrawables(icon);
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
||||||
* in xml-based animations which also handle updating the appropriate UI.
|
* in xml-based animations which also handle updating the appropriate UI.
|
||||||
*
|
*
|
||||||
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
|
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
|
||||||
*
|
|
||||||
* @see #setState(LauncherState)
|
* @see #setState(LauncherState)
|
||||||
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
|
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -96,8 +96,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||||
launcher.getItemOnClickListener().onClick(view);
|
launcher.getItemOnClickListener().onClick(view);
|
||||||
});
|
});
|
||||||
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
|
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
|
||||||
setLayoutParams(
|
|
||||||
new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx));
|
|
||||||
|
|
||||||
|
|
||||||
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
|
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
|
||||||
|
|
|
@ -22,16 +22,19 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||||
import android.app.RemoteAction;
|
import android.app.RemoteAction;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
|
@ -53,25 +56,51 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||||
AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
||||||
|
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
private final Object[] mTargetInfo = createTargetInfo();
|
||||||
|
private final int mCustomIconResId;
|
||||||
|
private final boolean mMatchesInset;
|
||||||
|
|
||||||
private ShortcutInfo mShortcutInfo;
|
private ShortcutInfo mShortcutInfo;
|
||||||
private AllAppsSearchPlugin mPlugin;
|
private AllAppsSearchPlugin mPlugin;
|
||||||
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
||||||
|
|
||||||
|
|
||||||
public SearchResultIconRow(@NonNull Context context) {
|
public SearchResultIconRow(@NonNull Context context) {
|
||||||
super(context);
|
this(context, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchResultIconRow(@NonNull Context context,
|
public SearchResultIconRow(@NonNull Context context,
|
||||||
@Nullable AttributeSet attrs) {
|
@Nullable AttributeSet attrs) {
|
||||||
super(context, attrs);
|
this(context, attrs, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||||
int defStyleAttr) {
|
int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
|
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||||
|
R.styleable.SearchResultIconRow, defStyleAttr, 0);
|
||||||
|
mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
|
||||||
|
mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery,
|
||||||
|
false);
|
||||||
|
|
||||||
|
a.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
|
if (mMatchesInset && launcher.getAppsView() != null && getParent() != null) {
|
||||||
|
EditText editText = launcher.getAppsView().getSearchUiManager().getEditText();
|
||||||
|
if (editText != null) {
|
||||||
|
int counterOffset = getIconSize() + getCompoundDrawablePadding() / 2;
|
||||||
|
setPadding(editText.getLeft() - counterOffset, getPaddingTop(),
|
||||||
|
getPaddingRight(), getPaddingBottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
||||||
if (mAdapterItem != null) {
|
if (mAdapterItem != null) {
|
||||||
|
@ -98,19 +127,23 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||||
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
|
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
|
||||||
applyFromWorkspaceItem(workspaceItemInfo);
|
applyFromWorkspaceItem(workspaceItemInfo);
|
||||||
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
|
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
|
||||||
|
if (!loadIconFromResource()) {
|
||||||
MODEL_EXECUTOR.execute(() -> {
|
MODEL_EXECUTOR.execute(() -> {
|
||||||
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
|
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
|
||||||
reapplyItemInfoAsync(workspaceItemInfo);
|
reapplyItemInfoAsync(workspaceItemInfo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start,
|
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start,
|
||||||
boolean useIconToBadge) {
|
boolean useIconToBadge) {
|
||||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
|
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
|
||||||
|
|
||||||
applyFromRemoteActionInfo(itemInfo);
|
applyFromRemoteActionInfo(itemInfo);
|
||||||
|
if (!loadIconFromResource()) {
|
||||||
UI_HELPER_EXECUTOR.post(() -> {
|
UI_HELPER_EXECUTOR.post(() -> {
|
||||||
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
|
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not
|
||||||
|
// work.
|
||||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||||
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
||||||
BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
||||||
|
@ -127,9 +160,16 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||||
reapplyItemInfoAsync(itemInfo);
|
reapplyItemInfoAsync(itemInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean loadIconFromResource() {
|
||||||
|
if (mCustomIconResId == 0) return false;
|
||||||
|
setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
|
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
|
||||||
MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
|
MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 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 com.android.launcher3.views;
|
|
||||||
|
|
||||||
import static com.android.systemui.plugins.shared.SearchTarget.ItemType.SUGGEST;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.android.launcher3.Launcher;
|
|
||||||
import com.android.launcher3.R;
|
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
|
||||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
|
||||||
import com.android.launcher3.touch.ItemClickHandler;
|
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A view representing a fallback search suggestion row.
|
|
||||||
*/
|
|
||||||
public class SearchResultSuggestRow extends LinearLayout implements
|
|
||||||
View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
|
||||||
|
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
|
||||||
private AllAppsSearchPlugin mPlugin;
|
|
||||||
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
|
||||||
private TextView mTitle;
|
|
||||||
|
|
||||||
|
|
||||||
public SearchResultSuggestRow(@NonNull Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SearchResultSuggestRow(@NonNull Context context,
|
|
||||||
@Nullable AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SearchResultSuggestRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
|
||||||
int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFinishInflate() {
|
|
||||||
super.onFinishInflate();
|
|
||||||
mTitle = findViewById(R.id.title);
|
|
||||||
setOnClickListener(this);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
|
||||||
mAdapterItem = adapterItemWithPayload;
|
|
||||||
SearchTarget payload = adapterItemWithPayload.getPayload();
|
|
||||||
mPlugin = adapterItemWithPayload.getPlugin();
|
|
||||||
|
|
||||||
if (payload.mRemoteAction != null) {
|
|
||||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(payload.mRemoteAction,
|
|
||||||
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
|
||||||
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
|
||||||
setTag(itemInfo);
|
|
||||||
}
|
|
||||||
showIfAvailable(mTitle, payload.mRemoteAction.getTitle().toString());
|
|
||||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
|
||||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getTargetInfo() {
|
|
||||||
return mTargetInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSelection(int eventType) {
|
|
||||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
|
||||||
|
|
||||||
if (!(itemInfo instanceof RemoteActionItemInfo)) return;
|
|
||||||
|
|
||||||
RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
|
|
||||||
ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
|
|
||||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SUGGEST, eventType);
|
|
||||||
searchTargetEvent.bundle = new Bundle();
|
|
||||||
searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
|
|
||||||
searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
|
|
||||||
remoteItemInfo.shouldStartInLauncher());
|
|
||||||
searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
|
|
||||||
remoteItemInfo.getToken());
|
|
||||||
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
handleSelection(SearchTargetEvent.SELECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showIfAvailable(TextView view, @Nullable String string) {
|
|
||||||
System.out.println("Plugin suggest string:" + string);
|
|
||||||
if (TextUtils.isEmpty(string)) {
|
|
||||||
view.setVisibility(GONE);
|
|
||||||
} else {
|
|
||||||
System.out.println("Plugin suggest string:" + string);
|
|
||||||
view.setVisibility(VISIBLE);
|
|
||||||
view.setText(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue