show message in bottom bar if KP2A autofill service is not enabled (#9)
This commit is contained in:
@@ -34,7 +34,9 @@ using keepass2android.Io;
|
|||||||
using keepass2android.database.edit;
|
using keepass2android.database.edit;
|
||||||
using keepass2android.view;
|
using keepass2android.view;
|
||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
|
using Android.Provider;
|
||||||
using Android.Support.V4.View;
|
using Android.Support.V4.View;
|
||||||
|
using Android.Views.Autofill;
|
||||||
using CursorAdapter = Android.Support.V4.Widget.CursorAdapter;
|
using CursorAdapter = Android.Support.V4.Widget.CursorAdapter;
|
||||||
using Object = Java.Lang.Object;
|
using Object = Java.Lang.Object;
|
||||||
|
|
||||||
@@ -46,6 +48,17 @@ namespace keepass2android
|
|||||||
public const String KeyEntry = "entry";
|
public const String KeyEntry = "entry";
|
||||||
public const String KeyMode = "mode";
|
public const String KeyMode = "mode";
|
||||||
|
|
||||||
|
static readonly Dictionary<int /*resource id*/, int /*prio*/> bottomBarElementsPriority = new Dictionary<int, int>()
|
||||||
|
{
|
||||||
|
{ Resource.Id.cancel_insert_element, 20 },
|
||||||
|
{ Resource.Id.insert_element, 20 },
|
||||||
|
{ Resource.Id.autofill_infotext, 10 },
|
||||||
|
{ Resource.Id.select_other_entry, 20},
|
||||||
|
{ Resource.Id.add_url_entry, 20},
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly HashSet<int /*resource id*/> showableBottomBarElements = new HashSet<int>();
|
||||||
|
|
||||||
private ActivityDesign _design;
|
private ActivityDesign _design;
|
||||||
|
|
||||||
public virtual void LaunchActivityForEntry(PwEntry pwEntry, int pos)
|
public virtual void LaunchActivityForEntry(PwEntry pwEntry, int pos)
|
||||||
@@ -87,14 +100,6 @@ namespace keepass2android
|
|||||||
|
|
||||||
public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry)
|
public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry)
|
||||||
{
|
{
|
||||||
//check for null in the following because the "empty" layouts may not have all views
|
|
||||||
|
|
||||||
if (FindViewById(Resource.Id.bottom_bar) != null)
|
|
||||||
FindViewById(Resource.Id.bottom_bar).Visibility = BottomBarAlwaysVisible ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
|
|
||||||
if (FindViewById(Resource.Id.divider2) != null)
|
|
||||||
FindViewById(Resource.Id.divider2).Visibility = BottomBarAlwaysVisible ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
|
|
||||||
if (FindViewById(Resource.Id.fabCancelAddNew) != null)
|
if (FindViewById(Resource.Id.fabCancelAddNew) != null)
|
||||||
{
|
{
|
||||||
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
|
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
|
||||||
@@ -104,12 +109,49 @@ namespace keepass2android
|
|||||||
FindViewById(Resource.Id.fabAddNew).Visibility = (showAddGroup || showAddEntry) ? ViewStates.Visible : ViewStates.Gone;
|
FindViewById(Resource.Id.fabAddNew).Visibility = (showAddGroup || showAddEntry) ? ViewStates.Visible : ViewStates.Gone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateBottomBarElementVisibility(Resource.Id.insert_element, false);
|
||||||
|
UpdateBottomBarElementVisibility(Resource.Id.cancel_insert_element, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool BottomBarAlwaysVisible
|
void UpdateBottomBarVisibility()
|
||||||
{
|
{
|
||||||
get { return false; }
|
var bottomBar = FindViewById<RelativeLayout>(Resource.Id.bottom_bar);
|
||||||
|
//check for null because the "empty" layouts may not have all views
|
||||||
|
int highestPrio = -1;
|
||||||
|
HashSet<int> highestPrioElements = new HashSet<int>();
|
||||||
|
if (bottomBar != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bottomBar.ChildCount; i++)
|
||||||
|
{
|
||||||
|
int id = bottomBar.GetChildAt(i).Id;
|
||||||
|
if (!showableBottomBarElements.Contains(id))
|
||||||
|
continue;
|
||||||
|
int myPrio = bottomBarElementsPriority[id];
|
||||||
|
|
||||||
|
if (!highestPrioElements.Any() || highestPrio < myPrio)
|
||||||
|
{
|
||||||
|
highestPrioElements.Clear();
|
||||||
|
highestPrio = myPrio;
|
||||||
|
}
|
||||||
|
if (highestPrio == myPrio)
|
||||||
|
{
|
||||||
|
highestPrioElements.Add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomBar.Visibility = highestPrioElements.Any() ? ViewStates.Visible : ViewStates.Gone;
|
||||||
|
|
||||||
|
for (int i = 0; i < bottomBar.ChildCount; i++)
|
||||||
|
{
|
||||||
|
int id = bottomBar.GetChildAt(i).Id;
|
||||||
|
bottomBar.GetChildAt(i).Visibility =
|
||||||
|
highestPrioElements.Contains(id) ? ViewStates.Visible : ViewStates.Gone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FindViewById(Resource.Id.divider2) != null)
|
||||||
|
FindViewById(Resource.Id.divider2).Visibility = highestPrioElements.Any() ? ViewStates.Visible : ViewStates.Gone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -194,6 +236,8 @@ namespace keepass2android
|
|||||||
AppTask.StartInGroupActivity(this);
|
AppTask.StartInGroupActivity(this);
|
||||||
AppTask.SetupGroupBaseActivityButtons(this);
|
AppTask.SetupGroupBaseActivityButtons(this);
|
||||||
|
|
||||||
|
UpdateAutofillInfo();
|
||||||
|
|
||||||
RefreshIfDirty();
|
RefreshIfDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +291,16 @@ namespace keepass2android
|
|||||||
|
|
||||||
SetContentView(ContentResourceId);
|
SetContentView(ContentResourceId);
|
||||||
|
|
||||||
|
if (FindViewById(Resource.Id.enable_autofill) != null)
|
||||||
|
{
|
||||||
|
FindViewById(Resource.Id.enable_autofill).Click += (sender, args) =>
|
||||||
|
{
|
||||||
|
var intent = new Intent(Settings.ActionRequestSetAutofillService);
|
||||||
|
intent.SetData(Android.Net.Uri.Parse("package:" + PackageName));
|
||||||
|
StartActivity(intent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (FindViewById(Resource.Id.fabCancelAddNew) != null)
|
if (FindViewById(Resource.Id.fabCancelAddNew) != null)
|
||||||
{
|
{
|
||||||
FindViewById(Resource.Id.fabAddNew).Click += (sender, args) =>
|
FindViewById(Resource.Id.fabAddNew).Click += (sender, args) =>
|
||||||
@@ -277,12 +331,49 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SetResult(KeePass.ExitNormal);
|
SetResult(KeePass.ExitNormal);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateAutofillInfo()
|
||||||
|
{
|
||||||
|
bool canShowAutofillInfo = false;
|
||||||
|
|
||||||
|
if (!((Android.OS.Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.O) ||
|
||||||
|
!((AutofillManager) GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager))))
|
||||||
|
.IsAutofillSupported))
|
||||||
|
{
|
||||||
|
const string autofillservicewasenabled = "AutofillServiceWasEnabled";
|
||||||
|
if (!((AutofillManager) GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager))))
|
||||||
|
.HasEnabledAutofillServices)
|
||||||
|
{
|
||||||
|
//if (!_prefs.GetBoolean(autofillservicewasenabled, false))
|
||||||
|
canShowAutofillInfo = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_prefs.Edit().PutBoolean(autofillservicewasenabled, true).Commit();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateBottomBarElementVisibility(Resource.Id.autofill_infotext, canShowAutofillInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void UpdateBottomBarElementVisibility(int resourceId, bool canShow)
|
||||||
|
{
|
||||||
|
if (canShow)
|
||||||
|
showableBottomBarElements.Add(resourceId);
|
||||||
|
else
|
||||||
|
showableBottomBarElements.Remove(resourceId);
|
||||||
|
UpdateBottomBarVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual int ContentResourceId
|
protected virtual int ContentResourceId
|
||||||
{
|
{
|
||||||
get { return Resource.Layout.group; }
|
get { return Resource.Layout.group; }
|
||||||
@@ -732,8 +823,9 @@ namespace keepass2android
|
|||||||
FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
|
FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone;
|
||||||
FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone;
|
FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone;
|
||||||
|
|
||||||
FindViewById(Resource.Id.bottom_bar).Visibility = ViewStates.Visible;
|
UpdateBottomBarElementVisibility(Resource.Id.insert_element, true);
|
||||||
FindViewById(Resource.Id.divider2).Visibility = ViewStates.Visible;
|
UpdateBottomBarElementVisibility(Resource.Id.cancel_insert_element, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopMovingElements()
|
public void StopMovingElements()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:orientation="horizontal" />
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/bottom_bar"
|
android:id="@+id/bottom_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -17,10 +18,12 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:baselineAligned="false">
|
android:baselineAligned="false">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/insert_element"
|
android:id="@+id/insert_element"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:text="@string/insert_element_here"
|
android:text="@string/insert_element_here"
|
||||||
style="@style/BottomBarButton" />
|
style="@style/BottomBarButton" />
|
||||||
@@ -28,16 +31,45 @@
|
|||||||
android:id="@+id/cancel_insert_element"
|
android:id="@+id/cancel_insert_element"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:text="@string/cancel"
|
android:text="@string/cancel"
|
||||||
style="@style/BottomBarButton" />
|
style="@style/BottomBarButton" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/autofill_infotext"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView android:id="@+id/myinfotext" android:text="@string/autofill_hint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_margin="6dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/enable_autofill"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:text="@string/autofill_enable"
|
||||||
|
style="@style/BottomBarButton" />
|
||||||
|
</LinearLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider2"
|
android:id="@+id/divider2"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_above="@id/bottom_bar"
|
android:layout_above="@id/bottom_bar"
|
||||||
android:background="#b8b8b8" />
|
android:background="#b8b8b8" />
|
||||||
|
|
||||||
<android.support.design.widget.CoordinatorLayout
|
<android.support.design.widget.CoordinatorLayout
|
||||||
android:id="@+id/main_content"
|
android:id="@+id/main_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1040,6 +1040,8 @@ Initial public release
|
|||||||
<string name="ClearClipboardWarning">Make sure this works on your system and consider using the built-in keyboard if not.</string>
|
<string name="ClearClipboardWarning">Make sure this works on your system and consider using the built-in keyboard if not.</string>
|
||||||
<string name="PluginDescription">Description provided by the plugin:</string>
|
<string name="PluginDescription">Description provided by the plugin:</string>
|
||||||
|
|
||||||
|
<string name="autofill_hint">Keepass2Android supports Android\'s Autofill feature but it looks like you haven\'t enabled it yet.</string>
|
||||||
|
<string name="autofill_enable">Enable Autofill</string>
|
||||||
<string name="autofill_sign_in_prompt">Fill with Keepass2Android</string>
|
<string name="autofill_sign_in_prompt">Fill with Keepass2Android</string>
|
||||||
<string name="invalid_link_association">Could not associate web domain %1$s with app %2$s</string>
|
<string name="invalid_link_association">Could not associate web domain %1$s with app %2$s</string>
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,12 @@ namespace keepass2android
|
|||||||
SetResult(Result.Canceled);
|
SetResult(Result.Canceled);
|
||||||
|
|
||||||
_db = App.Kp2a.GetDb();
|
_db = App.Kp2a.GetDb();
|
||||||
|
|
||||||
|
|
||||||
|
UpdateBottomBarElementVisibility(Resource.Id.select_other_entry, true);
|
||||||
|
UpdateBottomBarElementVisibility(Resource.Id.add_url_entry, true);
|
||||||
|
|
||||||
|
|
||||||
if (App.Kp2a.DatabaseIsUnlocked)
|
if (App.Kp2a.DatabaseIsUnlocked)
|
||||||
{
|
{
|
||||||
var searchUrlTask = ((SearchUrlTask)AppTask);
|
var searchUrlTask = ((SearchUrlTask)AppTask);
|
||||||
@@ -86,6 +92,8 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
// else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished.
|
// else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSaveInstanceState(Bundle outState)
|
protected override void OnSaveInstanceState(Bundle outState)
|
||||||
@@ -181,11 +189,6 @@ namespace keepass2android
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool BottomBarAlwaysVisible
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override int ContentResourceId
|
protected override int ContentResourceId
|
||||||
{
|
{
|
||||||
get { return Resource.Layout.searchurlresults; }
|
get { return Resource.Layout.searchurlresults; }
|
||||||
|
|||||||
@@ -267,7 +267,6 @@
|
|||||||
<Compile Include="Utils\Util.cs" />
|
<Compile Include="Utils\Util.cs" />
|
||||||
<Compile Include="views\ClickView.cs" />
|
<Compile Include="views\ClickView.cs" />
|
||||||
<Compile Include="views\GroupListItemView.cs" />
|
<Compile Include="views\GroupListItemView.cs" />
|
||||||
<Compile Include="views\GroupView.cs" />
|
|
||||||
<Compile Include="PwGroupListAdapter.cs" />
|
<Compile Include="PwGroupListAdapter.cs" />
|
||||||
<Compile Include="views\Kp2aShortHelpView.cs" />
|
<Compile Include="views\Kp2aShortHelpView.cs" />
|
||||||
<Compile Include="views\PwGroupView.cs" />
|
<Compile Include="views\PwGroupView.cs" />
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
|
||||||
|
|
||||||
Keepass2Android is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Keepass2Android is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.Runtime;
|
|
||||||
using Android.Util;
|
|
||||||
using Android.Views;
|
|
||||||
using Android.Widget;
|
|
||||||
|
|
||||||
namespace keepass2android.view
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
public class GroupView : RelativeLayout
|
|
||||||
{
|
|
||||||
public GroupView (IntPtr javaReference, JniHandleOwnership transfer)
|
|
||||||
: base(javaReference, transfer)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public GroupView(Context context): base(context) {
|
|
||||||
Inflate(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GroupView(Context context, IAttributeSet attrs): base(context, attrs) {
|
|
||||||
Inflate(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListView ListView
|
|
||||||
{
|
|
||||||
get { return (ListView) FindViewById(Android.Resource.Id.List); }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Inflate(Context context) {
|
|
||||||
LayoutInflater inflater = (LayoutInflater) context.GetSystemService(Context.LayoutInflaterService);
|
|
||||||
inflater.Inflate(Resource.Layout.group_add_entry, this);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry)
|
|
||||||
{
|
|
||||||
|
|
||||||
View insertElement = FindViewById(Resource.Id.insert_element);
|
|
||||||
insertElement.Visibility = ViewStates.Gone;
|
|
||||||
|
|
||||||
View insertElementCancel = FindViewById(Resource.Id.cancel_insert_element);
|
|
||||||
insertElementCancel.Visibility = ViewStates.Gone;
|
|
||||||
|
|
||||||
View addGroup = FindViewById(Resource.Id.add_group);
|
|
||||||
addGroup.Visibility = showAddGroup? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
|
|
||||||
|
|
||||||
View addEntry = FindViewById(Resource.Id.add_entry);
|
|
||||||
addEntry.Visibility = showAddEntry ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
|
|
||||||
|
|
||||||
if (!showAddEntry && !showAddGroup)
|
|
||||||
{
|
|
||||||
View divider2 = FindViewById(Resource.Id.divider2);
|
|
||||||
divider2.Visibility = ViewStates.Gone;
|
|
||||||
|
|
||||||
FindViewById(Resource.Id.bottom_bar).Visibility = ViewStates.Gone;
|
|
||||||
|
|
||||||
View list = FindViewById(Android.Resource.Id.List);
|
|
||||||
LayoutParams lp = (RelativeLayout.LayoutParams) list.LayoutParameters;
|
|
||||||
|
|
||||||
lp.AddRule(LayoutRules.AlignParentBottom, (int) LayoutRules.True);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowInsertButtons()
|
|
||||||
{
|
|
||||||
View addGroup = FindViewById(Resource.Id.add_group);
|
|
||||||
addGroup.Visibility = ViewStates.Gone;
|
|
||||||
|
|
||||||
View addEntry = FindViewById(Resource.Id.add_entry);
|
|
||||||
addEntry.Visibility = ViewStates.Gone;
|
|
||||||
|
|
||||||
View insertElement = FindViewById(Resource.Id.insert_element);
|
|
||||||
insertElement.Visibility = ViewStates.Visible;
|
|
||||||
|
|
||||||
View insertElementCancel = FindViewById(Resource.Id.cancel_insert_element);
|
|
||||||
insertElementCancel.Visibility = ViewStates.Visible;
|
|
||||||
|
|
||||||
View divider2 = FindViewById(Resource.Id.divider2);
|
|
||||||
divider2.Visibility = ViewStates.Visible;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user