Add a collapsible checkbox preference for deletion helper.
This adds a custom preference group which has both a checkbox and collapse/expand behavior. This is intended to be used in the deletion helper for apps deletion and downloads folder deletion. This patch implements the apps deletion integration. Bug: 28769691 Change-Id: I9fb28a1baa4067841742b5dbeaf2083728c16144
This commit is contained in:
25
res/drawable/ic_keyboard_arrow_down_black_32.xml
Normal file
25
res/drawable/ic_keyboard_arrow_down_black_32.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
|
||||||
|
</vector>
|
25
res/drawable/ic_keyboard_arrow_up_black_32.xml
Normal file
25
res/drawable/ic_keyboard_arrow_up_black_32.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
||||||
|
</vector>
|
@@ -7620,4 +7620,10 @@
|
|||||||
<!-- Button to activate the storage manager on the storage manager upsell. [CHAR LIMIT=20]-->
|
<!-- Button to activate the storage manager on the storage manager upsell. [CHAR LIMIT=20]-->
|
||||||
<string name="deletion_helper_upsell_activate">Turn on</string>
|
<string name="deletion_helper_upsell_activate">Turn on</string>
|
||||||
|
|
||||||
|
<!-- Title for the apps category in the deletion helper, showing how many apps to delete. [CHAR LIMIT=40]-->
|
||||||
|
<string name="deletion_helper_apps_group_title">Apps (<xliff:g id="num_items">%1$d</xliff:g>)</string>
|
||||||
|
|
||||||
|
<!-- Summary for the apps category in the deletion helper, showing how many space to clear. [CHAR LIMIT=NONE]-->
|
||||||
|
<string name="deletion_helper_apps_group_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g></string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
<com.android.settings.deletionhelper.DownloadsDeletionPreference
|
<com.android.settings.deletionhelper.DownloadsDeletionPreference
|
||||||
android:key="delete_downloads" />
|
android:key="delete_downloads" />
|
||||||
|
|
||||||
<PreferenceCategory
|
<com.android.settings.CollapsibleCheckboxPreferenceGroup
|
||||||
android:key="apps_group"
|
android:key="apps_group"
|
||||||
android:title="@string/deletion_helper_apps_title" />
|
android:icon="@drawable/ic_keyboard_arrow_down_black_32"/>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
147
src/com/android/settings/CollapsibleCheckboxPreferenceGroup.java
Normal file
147
src/com/android/settings/CollapsibleCheckboxPreferenceGroup.java
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.settings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.preference.Preference;
|
||||||
|
import android.support.v7.preference.PreferenceGroup;
|
||||||
|
import android.support.v7.preference.PreferenceViewHolder;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Checkable;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CollapsibleCheckboxPreferenceGroup is a preference group that can be expanded or collapsed and
|
||||||
|
* also has a checkbox.
|
||||||
|
*/
|
||||||
|
public class CollapsibleCheckboxPreferenceGroup extends PreferenceGroup implements
|
||||||
|
View.OnClickListener {
|
||||||
|
private boolean mCollapsed;
|
||||||
|
private boolean mChecked;
|
||||||
|
|
||||||
|
public CollapsibleCheckboxPreferenceGroup(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollapsibleCheckboxPreferenceGroup(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setWidgetLayoutResource(com.android.settings.R.layout.preference_widget_checkbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
View checkbox = holder.findViewById(com.android.internal.R.id.checkbox);
|
||||||
|
if (checkbox != null && checkbox instanceof Checkable) {
|
||||||
|
((Checkable) checkbox).setChecked(mChecked);
|
||||||
|
checkbox.setClickable(true);
|
||||||
|
checkbox.setFocusable(true);
|
||||||
|
checkbox.setOnClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
|
||||||
|
if (titleView != null) {
|
||||||
|
Context context = getContext();
|
||||||
|
TypedValue value = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.colorAccent, value, true);
|
||||||
|
titleView.setTextColor(context.getColor(value.resourceId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addPreference(Preference p) {
|
||||||
|
super.addPreference(p);
|
||||||
|
p.setVisible(!isCollapsed());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The preference click handler.
|
||||||
|
@Override
|
||||||
|
protected void onClick() {
|
||||||
|
super.onClick();
|
||||||
|
setCollapse(!isCollapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The checkbox view click handler.
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
setChecked(!isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the view is collapsed.
|
||||||
|
*/
|
||||||
|
public boolean isCollapsed() {
|
||||||
|
return mCollapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the checked state of the preference.
|
||||||
|
*/
|
||||||
|
public boolean isChecked() {
|
||||||
|
return mChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the checked state and notifies listeners of the state change.
|
||||||
|
*/
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
if (mChecked != checked) {
|
||||||
|
mChecked = checked;
|
||||||
|
|
||||||
|
callChangeListener(checked);
|
||||||
|
notifyDependencyChange(shouldDisableDependents());
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCollapse(boolean isCollapsed) {
|
||||||
|
if (mCollapsed == isCollapsed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCollapsed = isCollapsed;
|
||||||
|
if (isCollapsed) {
|
||||||
|
hideDropdownPreferences();
|
||||||
|
} else {
|
||||||
|
showDropdownPreferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDropdownPreferences() {
|
||||||
|
setAllPreferencesVisibility(true);
|
||||||
|
setIcon(R.drawable.ic_keyboard_arrow_down_black_32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideDropdownPreferences() {
|
||||||
|
setAllPreferencesVisibility(false);
|
||||||
|
setIcon(R.drawable.ic_keyboard_arrow_up_black_32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAllPreferencesVisibility(boolean visible) {
|
||||||
|
for (int i = 0; i < getPreferenceCount(); i++) {
|
||||||
|
Preference p = getPreference(i);
|
||||||
|
p.setVisible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,17 +16,18 @@
|
|||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
package com.android.settings.deletionhelper;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.preference.Preference;
|
import android.support.v7.preference.Preference;
|
||||||
import android.support.v7.preference.PreferenceGroup;
|
|
||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import com.android.settings.deletionhelper.DownloadsDeletionPreference;
|
import com.android.settings.deletionhelper.DownloadsDeletionPreference;
|
||||||
|
import com.android.settings.CollapsibleCheckboxPreferenceGroup;
|
||||||
import com.android.settings.PhotosDeletionPreference;
|
import com.android.settings.PhotosDeletionPreference;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
@@ -65,7 +66,7 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
private static final int DOWNLOADS_LOADER_ID = 1;
|
private static final int DOWNLOADS_LOADER_ID = 1;
|
||||||
|
|
||||||
private Button mCancel, mFree;
|
private Button mCancel, mFree;
|
||||||
private PreferenceGroup mApps;
|
private CollapsibleCheckboxPreferenceGroup mApps;
|
||||||
private PhotosDeletionPreference mPhotoPreference;
|
private PhotosDeletionPreference mPhotoPreference;
|
||||||
private DownloadsDeletionPreference mDownloadsPreference;
|
private DownloadsDeletionPreference mDownloadsPreference;
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setAnimationAllowed(true);
|
||||||
Application app = getActivity().getApplication();
|
Application app = getActivity().getApplication();
|
||||||
mState = ApplicationsState.getInstance(app);
|
mState = ApplicationsState.getInstance(app);
|
||||||
mSession = mState.newSession(this);
|
mSession = mState.newSession(this);
|
||||||
@@ -89,7 +91,7 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
mDataUsageBridge = new AppStateUsageStatsBridge(getActivity(), mState, this);
|
mDataUsageBridge = new AppStateUsageStatsBridge(getActivity(), mState, this);
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.deletion_helper_list);
|
addPreferencesFromResource(R.xml.deletion_helper_list);
|
||||||
mApps = (PreferenceGroup) findPreference(KEY_APPS_GROUP);
|
mApps = (CollapsibleCheckboxPreferenceGroup) findPreference(KEY_APPS_GROUP);
|
||||||
mPhotoPreference = (PhotosDeletionPreference) findPreference(KEY_PHOTOS_VIDEOS_PREFERENCE);
|
mPhotoPreference = (PhotosDeletionPreference) findPreference(KEY_PHOTOS_VIDEOS_PREFERENCE);
|
||||||
mDownloadsPreference =
|
mDownloadsPreference =
|
||||||
(DownloadsDeletionPreference) findPreference(KEY_DOWNLOADS_PREFERENCE);
|
(DownloadsDeletionPreference) findPreference(KEY_DOWNLOADS_PREFERENCE);
|
||||||
@@ -131,6 +133,7 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
|
|
||||||
mDownloadsPreference.registerFreeableChangedListener(this);
|
mDownloadsPreference.registerFreeableChangedListener(this);
|
||||||
mDownloadsPreference.registerDeletionService(mDownloadsDeletion);
|
mDownloadsPreference.registerDeletionService(mDownloadsDeletion);
|
||||||
|
mApps.setOnPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,24 +194,7 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
ApplicationsState.SIZE_COMPARATOR);
|
ApplicationsState.SIZE_COMPARATOR);
|
||||||
if (apps == null) return;
|
if (apps == null) return;
|
||||||
mAppEntries = apps;
|
mAppEntries = apps;
|
||||||
cacheRemoveAllPrefs(mApps);
|
refreshAppGroup(apps);
|
||||||
int entryCount = apps.size();
|
|
||||||
for (int i = 0; i < entryCount; i++) {
|
|
||||||
AppEntry entry = apps.get(i);
|
|
||||||
final String packageName = entry.label;
|
|
||||||
AppDeletionPreference preference =
|
|
||||||
(AppDeletionPreference) getCachedPreference(entry.label);
|
|
||||||
if (preference == null) {
|
|
||||||
preference = new AppDeletionPreference(getActivity(), entry,
|
|
||||||
mState);
|
|
||||||
preference.setKey(packageName);
|
|
||||||
preference.setChecked(mCheckedApplications.contains(packageName));
|
|
||||||
preference.setOnPreferenceChangeListener(this);
|
|
||||||
mApps.addPreference(preference);
|
|
||||||
}
|
|
||||||
preference.setOrder(i);
|
|
||||||
}
|
|
||||||
removeCachedPrefs(mApps);
|
|
||||||
|
|
||||||
// All applications should be filled in if we've received the sizes.
|
// All applications should be filled in if we've received the sizes.
|
||||||
// setLoading being called multiple times causes flickering, so we only do it once.
|
// setLoading being called multiple times causes flickering, so we only do it once.
|
||||||
@@ -277,11 +263,20 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
boolean checked = (boolean) newValue;
|
boolean checked = (boolean) newValue;
|
||||||
|
if (preference.getKey().equals(mApps.getKey())) {
|
||||||
|
return toggleAllApps(checked);
|
||||||
|
}
|
||||||
|
|
||||||
String packageName = ((AppDeletionPreference) preference).getPackageName();
|
String packageName = ((AppDeletionPreference) preference).getPackageName();
|
||||||
if (checked) {
|
if (checked) {
|
||||||
mCheckedApplications.add(packageName);
|
mCheckedApplications.add(packageName);
|
||||||
} else {
|
} else {
|
||||||
mCheckedApplications.remove(packageName);
|
mCheckedApplications.remove(packageName);
|
||||||
|
|
||||||
|
// We remove the preference change listener to avoid toggling every app on and off.
|
||||||
|
mApps.setOnPreferenceChangeListener(null);
|
||||||
|
mApps.setChecked(false);
|
||||||
|
mApps.setOnPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
updateFreeButtonText();
|
updateFreeButtonText();
|
||||||
return true;
|
return true;
|
||||||
@@ -346,17 +341,7 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
|
|
||||||
private long getTotalFreeableSpace() {
|
private long getTotalFreeableSpace() {
|
||||||
long freeableSpace = 0;
|
long freeableSpace = 0;
|
||||||
if (mAppEntries != null) {
|
freeableSpace += getTotalAppsFreeableSpace(false);
|
||||||
|
|
||||||
for (int i = 0; i < mAppEntries.size(); i++) {
|
|
||||||
final AppEntry entry = mAppEntries.get(i);
|
|
||||||
long entrySize = mAppEntries.get(i).size;
|
|
||||||
// If the entrySize is negative, it is either an unknown size or an error occurred.
|
|
||||||
if (mCheckedApplications.contains(entry.label) && entrySize > 0) {
|
|
||||||
freeableSpace += entrySize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mPhotoPreference != null) {
|
if (mPhotoPreference != null) {
|
||||||
freeableSpace += mPhotoPreference.getFreeableBytes();
|
freeableSpace += mPhotoPreference.getFreeableBytes();
|
||||||
}
|
}
|
||||||
@@ -365,4 +350,67 @@ public class DeletionHelperFragment extends SettingsPreferenceFragment implement
|
|||||||
}
|
}
|
||||||
return freeableSpace;
|
return freeableSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshAppGroup(ArrayList<AppEntry> apps) {
|
||||||
|
int entryCount = apps.size();
|
||||||
|
cacheRemoveAllPrefs(mApps);
|
||||||
|
for (int i = 0; i < entryCount; i++) {
|
||||||
|
AppEntry entry = apps.get(i);
|
||||||
|
final String packageName = entry.label;
|
||||||
|
AppDeletionPreference preference =
|
||||||
|
(AppDeletionPreference) getCachedPreference(entry.label);
|
||||||
|
if (preference == null) {
|
||||||
|
preference = new AppDeletionPreference(getActivity(), entry, mState);
|
||||||
|
preference.setKey(packageName);
|
||||||
|
preference.setOnPreferenceChangeListener(this);
|
||||||
|
mApps.addPreference(preference);
|
||||||
|
}
|
||||||
|
preference.setChecked(mCheckedApplications.contains(packageName));
|
||||||
|
preference.setOrder(i);
|
||||||
|
}
|
||||||
|
removeCachedPrefs(mApps);
|
||||||
|
updateAppsGroupText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getTotalAppsFreeableSpace(boolean countUnchecked) {
|
||||||
|
long freeableSpace = 0;
|
||||||
|
if (mAppEntries != null) {
|
||||||
|
for (int i = 0; i < mAppEntries.size(); i++) {
|
||||||
|
final AppEntry entry = mAppEntries.get(i);
|
||||||
|
long entrySize = mAppEntries.get(i).size;
|
||||||
|
// If the entrySize is negative, it is either an unknown size or an error occurred.
|
||||||
|
if ((countUnchecked ||
|
||||||
|
mCheckedApplications.contains(entry.label)) && entrySize > 0) {
|
||||||
|
freeableSpace += entrySize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return freeableSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAppsGroupText() {
|
||||||
|
if (mAppEntries != null) {
|
||||||
|
Activity app = getActivity();
|
||||||
|
mApps.setTitle(app.getString(R.string.deletion_helper_apps_group_title,
|
||||||
|
mAppEntries.size()));
|
||||||
|
mApps.setSummary(app.getString(R.string.deletion_helper_apps_group_summary,
|
||||||
|
Formatter.formatFileSize(app,
|
||||||
|
getTotalAppsFreeableSpace(true))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean toggleAllApps(boolean checked) {
|
||||||
|
for (AppEntry entry : mAppEntries) {
|
||||||
|
final String packageName = entry.label;
|
||||||
|
if (checked) {
|
||||||
|
mCheckedApplications.add(packageName);
|
||||||
|
} else {
|
||||||
|
mCheckedApplications.remove(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refreshAppGroup(mAppEntries);
|
||||||
|
updateFreeButtonText();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user