Commit 87a771c7 authored by Grigory Fedorov's avatar Grigory Fedorov

Status editor changed significantly. Full screen dialog style, bottom action bar, new layout.

parent fe36595f
......@@ -20,8 +20,8 @@ import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
......@@ -40,61 +40,63 @@ import com.xabber.android.data.account.StatusMode;
import com.xabber.android.data.intent.AccountIntentBuilder;
import com.xabber.android.ui.adapter.StatusEditorAdapter;
import com.xabber.android.ui.adapter.StatusModeAdapter;
import com.xabber.android.ui.helper.ActionBarPainter;
import com.xabber.android.ui.helper.ManagedListActivity;
import com.xabber.androiddev.R;
public class StatusEditor extends ManagedListActivity implements
View.OnClickListener, OnItemClickListener {
public class StatusEditor extends ManagedListActivity implements OnItemClickListener, Toolbar.OnMenuItemClickListener {
private static final String SAVED_TEXT = "com.xabber.android.ui.StatusEditor.SAVED_TEXT";
private static final String SAVED_MODE = "com.xabber.android.ui.StatusEditor.SAVED_MODE";
static final public int CONTEXT_MENU_SELECT_STATUS_ID = 10;
static final public int CONTEXT_MENU_EDIT_STATUS_ID = 11;
static final public int CONTEXT_MENU_REMOVE_STATUS_ID = 12;
private String account;
private Spinner statusModeView;
private EditText statusTextView;
private SavedStatus actionWithItem;
private StatusEditorAdapter adapter;
private View savedStatusesTextView;
private Toolbar bottomToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFinishing())
if (isFinishing()) {
return;
}
actionWithItem = null;
setContentView(R.layout.status_editor);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar_default));
setSupportActionBar((Toolbar) findViewById(R.id.top_toolbar));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_clear_white_24dp);
getSupportActionBar().setTitle(null);
bottomToolbar = (Toolbar) findViewById(R.id.bottom_toolbar);
bottomToolbar.inflateMenu(R.menu.clear_status_history);
bottomToolbar.setOnMenuItemClickListener(this);
Intent intent = getIntent();
account = StatusEditor.getAccount(intent);
if (account == null)
setTitle(getString(R.string.status_editor));
else
setTitle(getString(R.string.status_editor_for, AccountManager
.getInstance().getVerboseName(account)));
if (account != null) {
ActionBarPainter actionBarPainter = new ActionBarPainter(this);
actionBarPainter.updateWithAccountName(account);
bottomToolbar.setBackgroundColor(actionBarPainter.getAccountColor(account));
}
ListView listView = getListView();
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View header = inflater.inflate(R.layout.status_editor_header, listView,
false);
listView.addHeaderView(header, null, false);
listView.setOnItemClickListener(this);
registerForContextMenu(listView);
adapter = new StatusEditorAdapter(this);
setListAdapter(adapter);
statusTextView = (EditText) header.findViewById(R.id.status_text);
statusModeView = (Spinner) header.findViewById(R.id.status_icon);
statusTextView = (EditText) findViewById(R.id.status_text);
statusModeView = (Spinner) findViewById(R.id.status_icon);
statusModeView.setAdapter(new StatusModeAdapter(this));
findViewById(R.id.ok).setOnClickListener(this);
savedStatusesTextView = findViewById(R.id.saved_statuses_textview);
StatusMode statusMode;
String statusText;
......@@ -103,8 +105,7 @@ public class StatusEditor extends ManagedListActivity implements
statusMode = SettingsManager.statusMode();
statusText = SettingsManager.statusText();
} else {
AccountItem accountItem = AccountManager.getInstance()
.getAccount(account);
AccountItem accountItem = AccountManager.getInstance().getAccount(account);
if (accountItem == null) {
Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT);
finish();
......@@ -114,8 +115,7 @@ public class StatusEditor extends ManagedListActivity implements
statusText = accountItem.getStatusText();
}
} else {
statusMode = StatusMode.valueOf(savedInstanceState
.getString(SAVED_MODE));
statusMode = StatusMode.valueOf(savedInstanceState.getString(SAVED_MODE));
statusText = savedInstanceState.getString(SAVED_TEXT);
}
showStatus(statusMode, statusText);
......@@ -131,17 +131,19 @@ public class StatusEditor extends ManagedListActivity implements
private void setStatus(StatusMode statusMode, String statusText) {
AccountManager accountManager = AccountManager.getInstance();
if (account != null)
if (account != null) {
accountManager.setStatus(account, statusMode, statusText);
else {
} else {
accountManager.setStatus(statusMode, statusText);
}
}
private void showStatus(StatusMode statusMode, String statusText) {
for (int index = 0; index < statusModeView.getCount(); index++)
if (statusMode == statusModeView.getAdapter().getItem(index))
for (int index = 0; index < statusModeView.getCount(); index++) {
if (statusMode == statusModeView.getAdapter().getItem(index)) {
statusModeView.setSelection(index);
}
}
statusTextView.setText(statusText);
}
......@@ -149,14 +151,22 @@ public class StatusEditor extends ManagedListActivity implements
protected void onResume() {
super.onResume();
adapter.onChange();
setStatusHistoryVisibility();
}
private void setStatusHistoryVisibility() {
boolean isHistoryEmpty = AccountManager.getInstance().getSavedStatuses().isEmpty();
int visibility = isHistoryEmpty ? View.GONE : View.VISIBLE;
getListView().setVisibility(visibility);
savedStatusesTextView.setVisibility(visibility);
bottomToolbar.setVisibility(visibility);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.status, menu);
getMenuInflater().inflate(R.menu.set_status, menu);
return true;
}
......@@ -168,78 +178,55 @@ public class StatusEditor extends ManagedListActivity implements
changeStatus();
return true;
case R.id.action_delete_status_message:
case R.id.action_clear_status_history:
AccountManager.getInstance().clearSavedStatuses();
adapter.onChange();
setStatusHistoryVisibility();
return true;
}
return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
actionWithItem = (SavedStatus) getListView().getItemAtPosition(
info.position);
if (actionWithItem == null) // Header
return;
menu.add(0, CONTEXT_MENU_SELECT_STATUS_ID, 0,
getResources().getText(R.string.select_status));
menu.add(0, CONTEXT_MENU_EDIT_STATUS_ID, 0,
getResources().getText(R.string.edit_status));
menu.add(0, CONTEXT_MENU_REMOVE_STATUS_ID, 0,
getResources().getText(R.string.remove_status));
actionWithItem = (SavedStatus) getListView().getItemAtPosition(info.position);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.status_context_menu, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
switch (item.getItemId()) {
case CONTEXT_MENU_SELECT_STATUS_ID:
setStatus(actionWithItem.getStatusMode(),
actionWithItem.getStatusText());
case R.id.action_select_status:
setStatus(actionWithItem.getStatusMode(), actionWithItem.getStatusText());
finish();
return true;
case CONTEXT_MENU_EDIT_STATUS_ID:
showStatus(actionWithItem.getStatusMode(),
actionWithItem.getStatusText());
case R.id.action_edit_status:
showStatus(actionWithItem.getStatusMode(), actionWithItem.getStatusText());
return true;
case CONTEXT_MENU_REMOVE_STATUS_ID:
case R.id.action_remove_status:
AccountManager.getInstance().removeSavedStatus(actionWithItem);
adapter.onChange();
setStatusHistoryVisibility();
return true;
}
return false;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ok:
changeStatus();
break;
default:
break;
}
}
private void changeStatus() {
StatusMode statusMode = (StatusMode) statusModeView
.getSelectedItem();
StatusMode statusMode = (StatusMode) statusModeView.getSelectedItem();
String statusText = statusTextView.getText().toString();
setStatus(statusMode, statusText);
finish();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
SavedStatus savedStatus = (SavedStatus) parent.getAdapter().getItem(
position);
if (savedStatus == null) // Header
return;
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SavedStatus savedStatus = (SavedStatus) parent.getAdapter().getItem(position);
setStatus(savedStatus.getStatusMode(), savedStatus.getStatusText());
finish();
}
......@@ -249,11 +236,15 @@ public class StatusEditor extends ManagedListActivity implements
}
public static Intent createIntent(Context context, String account) {
return new AccountIntentBuilder(context, StatusEditor.class)
.setAccount(account).build();
return new AccountIntentBuilder(context, StatusEditor.class).setAccount(account).build();
}
private static String getAccount(Intent intent) {
return AccountIntentBuilder.getAccount(intent);
}
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return onOptionsItemSelected(menuItem);
}
}
......@@ -33,8 +33,7 @@ import com.xabber.androiddev.R;
*
* @author alexander.ivanov
*/
public class StatusEditorAdapter extends BaseAdapter implements
UpdatableAdapter {
public class StatusEditorAdapter extends BaseAdapter implements UpdatableAdapter {
private final Activity activity;
private final ArrayList<SavedStatus> statuses;
......@@ -42,7 +41,7 @@ public class StatusEditorAdapter extends BaseAdapter implements
public StatusEditorAdapter(Activity activity) {
super();
this.activity = activity;
statuses = new ArrayList<SavedStatus>();
statuses = new ArrayList<>();
}
@Override
......@@ -64,17 +63,16 @@ public class StatusEditorAdapter extends BaseAdapter implements
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = activity.getLayoutInflater().inflate(
R.layout.status_editor_item, parent, false);
view = activity.getLayoutInflater().inflate(R.layout.status_editor_item, parent, false);
} else {
view = convertView;
}
final SavedStatus status = (SavedStatus) getItem(position);
((ImageView) view.findViewById(R.id.icon)).setImageLevel(status
.getStatusMode().getStatusLevel());
((ImageView) view.findViewById(R.id.icon)).setImageLevel(status.getStatusMode().getStatusLevel());
String text = status.getStatusText();
if ("".equals(text))
if ("".equals(text)) {
text = activity.getString(R.string.empty_status);
}
((TextView) view.findViewById(R.id.name)).setText(text);
return view;
}
......
......@@ -38,13 +38,12 @@ public class StatusModeAdapter extends BaseAdapter {
public StatusModeAdapter(Activity activity) {
super();
this.activity = activity;
statusModes = new ArrayList<StatusMode>();
statusModes = new ArrayList<>();
statusModes.add(StatusMode.chat);
statusModes.add(StatusMode.available);
statusModes.add(StatusMode.away);
statusModes.add(StatusMode.xa);
statusModes.add(StatusMode.dnd);
// statusModes.add(StatusMode.invisible);
statusModes.add(StatusMode.unavailable);
}
......@@ -65,18 +64,15 @@ public class StatusModeAdapter extends BaseAdapter {
private void updateView(int position, View view) {
StatusMode statusMode = (StatusMode) getItem(position);
((ImageView) view.findViewById(R.id.icon)).setImageLevel(statusMode
.getStatusLevel());
((TextView) view.findViewById(R.id.name)).setText(statusMode
.getStringID());
((ImageView) view.findViewById(R.id.icon)).setImageLevel(statusMode.getStatusLevel());
((TextView) view.findViewById(R.id.name)).setText(statusMode.getStringID());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = activity.getLayoutInflater().inflate(
R.layout.status_mode_item, parent, false);
view = activity.getLayoutInflater().inflate(R.layout.status_mode_item, parent, false);
} else {
view = convertView;
}
......@@ -86,14 +82,6 @@ public class StatusModeAdapter extends BaseAdapter {
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = activity.getLayoutInflater().inflate(
R.layout.status_mode_dropdown, parent, false);
} else {
view = convertView;
}
updateView(position, view);
return view;
return getView(position, convertView, parent);
}
}
......@@ -68,4 +68,9 @@ public class ActionBarPainter {
}
}
}
public int getAccountColor(String account) {
return accountActionBarColors[AccountManager.getInstance().getColorLevel(account)];
}
}
......@@ -12,20 +12,92 @@
You should have received a copy of the GNU General Public License,
along with this program. If not, see http://www.gnu.org/licenses/.
-->
<LinearLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/toolbar_default"/>
<include layout="@layout/toolbar_default"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_alignParentTop="true"
android:id="@+id/top_toolbar"
/>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
<include layout="@layout/toolbar_default"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:id="@+id/bottom_toolbar"
/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="4dip" />
android:layout_below="@id/top_toolbar"
android:layout_above="@id/bottom_toolbar"
android:padding="8dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"
android:layout_alignParentTop="true"
android:id="@+id/top_panel"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/new_status"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<Spinner
android:id="@+id/status_icon"
android:prompt="@string/status_editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<EditText
android:id="@+id/status_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/status_text_hint"
android:paddingLeft="34dp"
android:singleLine="true"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/saved_statuses"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:id="@+id/saved_statuses_textview"
/>
</LinearLayout>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/top_panel"
/>
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2013, Redsolution LTD. All rights reserved.
This file is part of Xabber project; you can redistribute it and/or
modify it under the terms of the GNU General Public License, Version 3.
Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/status_icon"
android:prompt="@string/status_editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<EditText
android:id="@+id/status_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/status_text_hint"
/>
<Button
android:id="@+id/ok"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/status_editor"
/>
</LinearLayout>
......@@ -13,24 +13,29 @@
along with this program. If not, see http://www.gnu.org/licenses/.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="48dp"
android:gravity="center_vertical"
android:padding="4dip">
>
<ImageView
android:id="@+id/icon"
android:layout_width="24dip"
android:layout_height="24dip"
android:src="@drawable/ic_status" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_status"
android:paddingLeft="8dp"
android:paddingRight="8dp"
/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="42dip"
android:layout_marginLeft="6dip"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:text="online"
android:text="online"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2013, Redsolution LTD. All rights reserved.
This file is part of Xabber project; you can redistribute it and/or
modify it under the terms of the GNU General Public License, Version 3.
Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="4dip">
<ImageView
android:id="@+id/icon"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_marginLeft="10dip"
android:src="@drawable/ic_status"
/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="42dip"
android:layout_marginLeft="6dip"
android:singleLine="true"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:text="online"
/>
</LinearLayout>
......@@ -15,26 +15,31 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="48dp"
android:gravity="center_vertical"
android:padding="4dip">
>
<ImageView
android:id="@+id/icon"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_status"
android:paddingLeft="8dp"
android:paddingRight="8dp"
/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dip"
android:singleLine="true"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:text="online"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_clear_status_history"
android:title="@string/clear_status_history"
app:showAsAction="always"
android:orderInCategory="90"
/>
</menu>
\ No newline at end of file
......@@ -4,15 +4,7 @@
<item android:id="@+id/action_change_status"
android:title="@string/status_editor"
app:showAsAction="ifRoom"
android:icon="@drawable/ic_done_white_24dp"
app:showAsAction="always"
android:orderInCategory="90"
/>
<item android:id="@+id/action_delete_status_message"
android:title="@string/clear_statuses"
app:showAsAction="never"
android:icon="@drawable/ic_delete_white_24dp"
android:orderInCategory="100"
/>
</menu>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_select_status"
android:title="@string/select_status" />
<item android:id="@+id/action_edit_status"
android:title="@string/edit_status" />
<item android:id="@+id/action_remove_status"
android:title="@string/remove_status" />
</menu>
\ No newline at end of file
......@@ -21,15 +21,19 @@
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_context_menu.png -->
<string name="select_status">Установить статус</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor.png -->
<string name="status_editor">Сменить статус</string>
<string name="status_editor">Установить статус</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor.png -->
<string name="status_editor_for">Сменить статус для %s</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor.png -->
<string name="status_text_hint">Введите статус</string>
<string name="status_text_hint">Введите статус-сообщение</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_status.png -->
<string name="unavailable">Не в сети</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_status.png -->
<string name="unsubscribed">Не авторизован</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_status.png -->
<string name="xa">Давно отошел</string>
<string name="new_status">Новый статус</string>
<string name="saved_statuses">Сохранённые статусы</string>
<string name="clear_status_history">Очистить историю статусов</string>
</resources>
\ No newline at end of file
......@@ -21,15 +21,20 @@
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_context_menu.png -->
<string name="select_status">Set status</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor.png -->
<string name="status_editor">Change status</string>
<string name="status_editor">Set status</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor.png -->
<string name="status_editor_for">Change status for %s</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor.png -->
<string name="status_text_hint">Set your status</string>
<string name="status_text_hint">Enter status message</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_status.png -->
<string name="unavailable">Offline</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_status.png -->
<string name="unsubscribed">Not authorized</string>
<!-- http://dl.dropbox.com/u/1029995/com.xabber.android/status_editor_status.png -->
<string name="xa">Away for a long time</string>
<string name="new_status">New status</string>
<string name="saved_statuses">Saved statuses</string>
<string name="clear_status_history">Clear status history</string>
</resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment