Java tutorial
/* * Copyright 2016 OpenMarket Ltd * Copyright 2017 Vector Creations Ltd * * 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 im.vector.fragments; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.support.v4.content.ContextCompat; import android.text.Html; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.PopupMenu; import android.widget.Toast; import org.matrix.androidsdk.MXSession; import org.matrix.androidsdk.crypto.MXCryptoAlgorithms; import org.matrix.androidsdk.data.Room; import org.matrix.androidsdk.data.RoomAccountData; import org.matrix.androidsdk.data.RoomState; import org.matrix.androidsdk.data.RoomTag; import org.matrix.androidsdk.listeners.IMXNetworkEventListener; import org.matrix.androidsdk.listeners.MXEventListener; import org.matrix.androidsdk.listeners.MXMediaUploadListener; import org.matrix.androidsdk.rest.callback.ApiCallback; import org.matrix.androidsdk.rest.callback.SimpleApiCallback; import org.matrix.androidsdk.rest.model.Event; import org.matrix.androidsdk.rest.model.MatrixError; import org.matrix.androidsdk.rest.model.PowerLevels; import org.matrix.androidsdk.rest.model.RoomMember; import org.matrix.androidsdk.util.BingRulesManager; import org.matrix.androidsdk.util.Log; import org.matrix.androidsdk.util.ResourceUtils; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import im.vector.Matrix; import im.vector.R;; import im.vector.VectorApp; import im.vector.activity.CommonActivityUtils; import im.vector.activity.VectorMediasPickerActivity; import im.vector.activity.VectorMemberDetailsActivity; import im.vector.preference.AddressPreference; import im.vector.preference.RoomAvatarPreference; import im.vector.preference.VectorCustomActionEditTextPreference; import im.vector.preference.VectorListPreference; import im.vector.preference.VectorSwitchPreference; import im.vector.util.ThemeUtils; import im.vector.util.VectorUtils; import static android.preference.PreferenceManager.getDefaultSharedPreferences; public class VectorRoomSettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { // internal constants values private static final String LOG_TAG = VectorRoomSettingsFragment.class.getSimpleName(); private static final boolean UPDATE_UI = true; private static final boolean DO_NOT_UPDATE_UI = false; private static final int REQ_CODE_UPDATE_ROOM_AVATAR = 0x10; // Room access rules values private static final String ACCESS_RULES_ONLY_PEOPLE_INVITED = "1"; private static final String ACCESS_RULES_ANYONE_WITH_LINK_APART_GUEST = "2"; private static final String ACCESS_RULES_ANYONE_WITH_LINK_INCLUDING_GUEST = "3"; // fragment extra args keys private static final String EXTRA_MATRIX_ID = "KEY_EXTRA_MATRIX_ID"; private static final String EXTRA_ROOM_ID = "KEY_EXTRA_ROOM_ID"; // preference keys: public API to access preference private static final String PREF_KEY_ROOM_PHOTO_AVATAR = "roomPhotoAvatar"; private static final String PREF_KEY_ROOM_NAME = "roomNameEditText"; private static final String PREF_KEY_ROOM_TOPIC = "roomTopicEditText"; private static final String PREF_KEY_ROOM_DIRECTORY_VISIBILITY_SWITCH = "roomNameListedInDirectorySwitch"; private static final String PREF_KEY_ROOM_TAG_LIST = "roomTagList"; private static final String PREF_KEY_ROOM_ACCESS_RULES_LIST = "roomAccessRulesList"; private static final String PREF_KEY_ROOM_HISTORY_READABILITY_LIST = "roomReadHistoryRulesList"; private static final String PREF_KEY_ROOM_NOTIFICATIONS_LIST = "roomNotificationPreference"; private static final String PREF_KEY_ROOM_LEAVE = "roomLeave"; private static final String PREF_KEY_ROOM_INTERNAL_ID = "roomInternalId"; private static final String PREF_KEY_ADDRESSES = "addresses"; private static final String PREF_KEY_ADVANCED = "advanced"; private static final String PREF_KEY_BANNED = "banned"; private static final String PREF_KEY_BANNED_DIVIDER = "banned_divider"; private static final String PREF_KEY_ENCRYPTION = "encryptionKey"; private static final String PREF_KEY_FLAIR = "flair"; private static final String PREF_KEY_FLAIR_DIVIDER = "flair_divider"; private static final String ADDRESSES_PREFERENCE_KEY_BASE = "ADDRESSES_PREFERENCE_KEY_BASE"; private static final String NO_LOCAL_ADDRESS_PREFERENCE_KEY = "NO_LOCAL_ADDRESS_PREFERENCE_KEY"; private static final String ADD_ADDRESSES_PREFERENCE_KEY = "ADD_ADDRESSES_PREFERENCE_KEY"; private static final String BANNED_PREFERENCE_KEY_BASE = "BANNED_PREFERENCE_KEY_BASE"; private static final String FLAIR_PREFERENCE_KEY_BASE = "FLAIR_PREFERENCE_KEY_BASE"; private static final String UNKNOWN_VALUE = "UNKNOWN_VALUE"; // business code private MXSession mSession; private Room mRoom; private BingRulesManager mBingRulesManager; private boolean mIsUiUpdateSkipped; // addresses private PreferenceCategory mAddressesSettingsCategory; // other private PreferenceCategory mAdvandceSettingsCategory; // banned members private PreferenceCategory mBannedMembersSettingsCategory; private PreferenceCategory mBannedMembersSettingsCategoryDivider; // flair private PreferenceCategory mFlairSettingsCategory; // UI elements private RoomAvatarPreference mRoomPhotoAvatar; private EditTextPreference mRoomNameEditTxt; private EditTextPreference mRoomTopicEditTxt; private CheckBoxPreference mRoomDirectoryVisibilitySwitch; private ListPreference mRoomTagListPreference; private VectorListPreference mRoomAccessRulesListPreference; private ListPreference mRoomHistoryReadabilityRulesListPreference; private View mParentLoadingView; private View mParentFragmentContainerView; private ListPreference mRoomNotificationsPreference; // disable some updates if there is private final IMXNetworkEventListener mNetworkListener=new IMXNetworkEventListener(){@Override public void onNetworkConnectionUpdate(boolean isConnected){updateUi();}}; // update field listener private final ApiCallback<Void> mUpdateCallback = new ApiCallback<Void>() { /** * refresh the fragment. * @param mode true to force refresh */ private void onDone(final String message, final boolean mode) { if (null != getActivity()) { if (!TextUtils.isEmpty(message)) { Toast.makeText(getActivity(), message,Toast.LENGTH_LONG).show();} // ensure that the response has been sent in the UI thread getActivity().runOnUiThread(new Runnable(){@Override public void run(){hideLoadingView(mode);}});}} @Override public void onSuccess(Void info){Log.d(LOG_TAG,"##update succeed");onDone(null,UPDATE_UI);} @Override public void onNetworkError(Exception e){Log.w(LOG_TAG,"##NetworkError "+e.getLocalizedMessage());onDone(e.getLocalizedMessage(),DO_NOT_UPDATE_UI);} @Override public void onMatrixError(MatrixError e){Log.w(LOG_TAG,"##MatrixError "+e.getLocalizedMessage());onDone(e.getLocalizedMessage(),DO_NOT_UPDATE_UI);} @Override public void onUnexpectedError(Exception e){Log.w(LOG_TAG,"##UnexpectedError "+e.getLocalizedMessage());onDone(e.getLocalizedMessage(),DO_NOT_UPDATE_UI);}}; // MX system events listener private final MXEventListener mEventListener=new MXEventListener(){@Override public void onLiveEvent(final Event event,RoomState roomState){getActivity().runOnUiThread(new Runnable(){ @Override public void run(){String eventType=event.getType(); // The various events that could possibly change the fragment items if(Event.EVENT_TYPE_STATE_ROOM_NAME.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_ALIASES.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_MEMBER.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_AVATAR.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_TOPIC.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_POWER_LEVELS.equals(eventType)||Event.EVENT_TYPE_STATE_HISTORY_VISIBILITY.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_JOIN_RULES.equals(eventType) // room access rules ||Event.EVENT_TYPE_STATE_ROOM_GUEST_ACCESS.equals(eventType) // room access rules ){Log.d(LOG_TAG,"## onLiveEvent() event = "+eventType);updateUi();} if(Event.EVENT_TYPE_MESSAGE_ENCRYPTION.equals(eventType)){refreshEndToEnd();} // aliases if(Event.EVENT_TYPE_STATE_CANONICAL_ALIAS.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_ALIASES.equals(eventType)||Event.EVENT_TYPE_STATE_ROOM_POWER_LEVELS.equals(eventType)){Log.d(LOG_TAG,"## onLiveEvent() refresh the addresses list");refreshAddresses();} if(Event.EVENT_TYPE_STATE_ROOM_MEMBER.equals(eventType)){Log.d(LOG_TAG,"## onLiveEvent() refresh the banned members list");refreshBannedMembersList();}}});} @Override public void onRoomFlush(String roomId){getActivity().runOnUiThread(new Runnable(){@Override public void run(){updateUi();}});} @Override public void onRoomTagEvent(String roomId){Log.d(LOG_TAG,"## onRoomTagEvent()");updateUi();} @Override public void onBingRulesUpdate(){updateUi();}}; public static VectorRoomSettingsFragment newInstance(String aMatrixId, String aRoomId) { VectorRoomSettingsFragment theFragment = new VectorRoomSettingsFragment(); Bundle args = new Bundle(); args.putString(EXTRA_MATRIX_ID, aMatrixId); args.putString(EXTRA_ROOM_ID, aRoomId); theFragment.setArguments(args); return theFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(LOG_TAG, "## onCreate() IN"); // retrieve fragment extras String matrixId = getArguments().getString(EXTRA_MATRIX_ID); String roomId = getArguments().getString(EXTRA_ROOM_ID); if (TextUtils.isEmpty(matrixId) || TextUtils.isEmpty(roomId)) { Log.e(LOG_TAG, "## onCreate(): fragment extras (MatrixId or RoomId) are missing"); getActivity().finish(); } else { mSession = Matrix.getInstance(getActivity()).getSession(matrixId); if ((null != mSession) && mSession.isAlive()) { mRoom = mSession.getDataHandler().getRoom(roomId); mBingRulesManager = mSession.getDataHandler().getBingRulesManager(); } if (null == mRoom) { Log.e(LOG_TAG, "## onCreate(): unable to retrieve Room object"); getActivity().finish(); } } // load preference xml file addPreferencesFromResource(R.xml.vector_room_settings_preferences); // init preference fields mRoomPhotoAvatar = (RoomAvatarPreference) findPreference(PREF_KEY_ROOM_PHOTO_AVATAR); mRoomNameEditTxt = (EditTextPreference) findPreference(PREF_KEY_ROOM_NAME); mRoomTopicEditTxt = (EditTextPreference) findPreference(PREF_KEY_ROOM_TOPIC); mRoomDirectoryVisibilitySwitch = (CheckBoxPreference) findPreference( PREF_KEY_ROOM_DIRECTORY_VISIBILITY_SWITCH); mRoomTagListPreference = (ListPreference) findPreference(PREF_KEY_ROOM_TAG_LIST); mRoomAccessRulesListPreference = (VectorListPreference) findPreference(PREF_KEY_ROOM_ACCESS_RULES_LIST); mRoomHistoryReadabilityRulesListPreference = (ListPreference) findPreference( PREF_KEY_ROOM_HISTORY_READABILITY_LIST); mAddressesSettingsCategory = (PreferenceCategory) getPreferenceManager().findPreference(PREF_KEY_ADDRESSES); mAdvandceSettingsCategory = (PreferenceCategory) getPreferenceManager().findPreference(PREF_KEY_ADVANCED); mBannedMembersSettingsCategory = (PreferenceCategory) getPreferenceManager() .findPreference(PREF_KEY_BANNED); mBannedMembersSettingsCategoryDivider = (PreferenceCategory) getPreferenceManager() .findPreference(PREF_KEY_BANNED_DIVIDER); mFlairSettingsCategory = (PreferenceCategory) getPreferenceManager().findPreference(PREF_KEY_FLAIR); mRoomNotificationsPreference = (ListPreference) getPreferenceManager() .findPreference(PREF_KEY_ROOM_NOTIFICATIONS_LIST); mRoomAccessRulesListPreference.setOnPreferenceWarningIconClickListener( new VectorListPreference.OnPreferenceWarningIconClickListener() { @Override public void onWarningIconClick(Preference preference) { displayAccessRoomWarning(); } }); // display the room Id. EditTextPreference roomInternalIdPreference = (EditTextPreference) findPreference( PREF_KEY_ROOM_INTERNAL_ID); if (null != roomInternalIdPreference) { roomInternalIdPreference.setSummary(mRoom.getRoomId()); roomInternalIdPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { VectorUtils.copyToClipboard(getActivity(), mRoom.getRoomId()); return false; } }); } // leave room EditTextPreference leaveRoomPreference = (EditTextPreference) findPreference(PREF_KEY_ROOM_LEAVE); if (null != leaveRoomPreference) { leaveRoomPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { // leave room new AlertDialog.Builder(getActivity()).setTitle(R.string.room_participants_leave_prompt_title) .setMessage(getString(R.string.room_participants_leave_prompt_msg)) .setPositiveButton(R.string.leave, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); displayLoadingView(); mRoom.leave(new ApiCallback<Void>() { @Override public void onSuccess(Void info) { if (null != getActivity()) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { getActivity().finish(); } }); } } private void onError(final String errorMessage) { if (null != getActivity()) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { hideLoadingView(true); Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show(); } }); } } @Override public void onNetworkError(Exception e) { onError(e.getLocalizedMessage()); } @Override public void onMatrixError(MatrixError e) { onError(e.getLocalizedMessage()); } @Override public void onUnexpectedError(Exception e) { onError(e.getLocalizedMessage()); } }); } }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }).create().show(); return true; } }); } // init the room avatar: session and room mRoomPhotoAvatar.setConfiguration(mSession, mRoom); mRoomPhotoAvatar.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { if ((null != mRoomPhotoAvatar) && mRoomPhotoAvatar.isEnabled()) { onRoomAvatarPreferenceChanged(); return true; //True if the click was handled. } else return false; } }); // listen to preference changes enableSharedPreferenceListener(true); setRetainInstance(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); View listView = view.findViewById(android.R.id.list); if (null != listView) { listView.setPadding(0, 0, 0, 0); } // seems known issue that the preferences screen does not use the activity theme view.setBackgroundColor(ThemeUtils.getColor(getActivity(), R.attr.riot_primary_background_color)); return view; } /** * This method expects a view with the id "settings_loading_layout", * that is present in the parent activity layout. * * @param view fragment view * @param savedInstanceState bundle instance state */ @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // retrieve the loading screen in the parent view View parent = getView(); if (null == mParentLoadingView) { while ((null != parent) && (null == mParentLoadingView)) { mParentLoadingView = parent.findViewById(R.id.settings_loading_layout); parent = (View) parent.getParent(); } } // retrieve the parent fragment container view to disable access to the settings // while the loading screen is enabled parent = getView(); if (null == mParentFragmentContainerView) { while ((null != parent) && (null == mParentFragmentContainerView)) { mParentFragmentContainerView = parent.findViewById(R.id.room_details_fragment_container); parent = (View) parent.getParent(); } } } @Override public void onPause() { super.onPause(); if (null != mRoom) { Matrix.getInstance(getActivity()).removeNetworkEventListener(mNetworkListener); mRoom.removeEventListener(mEventListener); } // remove preference changes listener enableSharedPreferenceListener(false); } @Override public void onResume() { super.onResume(); if (null != mRoom) { Matrix.getInstance(getActivity()).addNetworkEventListener(mNetworkListener); mRoom.addEventListener(mEventListener); updateUi(); updateRoomDirectoryVisibilityAsync(); refreshAddresses(); refreshFlair(); refreshBannedMembersList(); refreshEndToEnd(); } } /** * Enable the preference listener according to the aIsListenerEnabled value. * * @param aIsListenerEnabled true to enable the listener, false otherwise */ private void enableSharedPreferenceListener(boolean aIsListenerEnabled) { Log.d(LOG_TAG, "## enableSharedPreferenceListener(): aIsListenerEnabled=" + aIsListenerEnabled); mIsUiUpdateSkipped = !aIsListenerEnabled; try { //SharedPreferences prefMgr = getActivity().getSharedPreferences("VectorSettingsFile", Context.MODE_PRIVATE); SharedPreferences prefMgr = getDefaultSharedPreferences(getActivity()); if (aIsListenerEnabled) { prefMgr.registerOnSharedPreferenceChangeListener(this); } else { prefMgr.unregisterOnSharedPreferenceChangeListener(this); } } catch (Exception ex) { Log.e(LOG_TAG, "## enableSharedPreferenceListener(): Exception Msg=" + ex.getMessage()); } } /** * Update the preferences according to the power levels and its values. * To prevent the preference change listener to be triggered, the listener * is removed when the preferences are updated. */ private void updateUi() { // configure the preferences that are allowed to be modified by the user updatePreferenceAccessFromPowerLevel(); // need to run on the UI thread to be taken into account // when updatePreferenceUiValues() will be performed getActivity().runOnUiThread(new Runnable() { @Override public void run() { // disable listener during preferences update, otherwise it will // be seen as a user action.. enableSharedPreferenceListener(false); } }); getActivity().runOnUiThread(new Runnable() { @Override public void run() { // set settings UI values updatePreferenceUiValues(); // re enable preferences listener.. enableSharedPreferenceListener(true); } }); } /** * delayed refresh the preferences items. */ private void updateUiOnUiThread() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { updateUi(); } }); } /** * Retrieve the room visibility directory value and update the corresponding preference. * This is an asynchronous request: the call-back response will be processed on the UI thread. * For now, the room visibility directory value is not provided in the sync API, a specific request * must performed. */ private void updateRoomDirectoryVisibilityAsync() { if ((null == mRoom) || (null == mRoomDirectoryVisibilitySwitch)) { Log.w(LOG_TAG, "## updateRoomDirectoryVisibilityUi(): not processed due to invalid parameters"); } else { displayLoadingView(); // server request: is the room listed in the room directory? mRoom.getDirectoryVisibility(mRoom.getRoomId(), new ApiCallback<String>() { private void handleResponseOnUiThread(final String aVisibilityValue) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { // only stop loading screen and do not update UI since the // update is done here below.. hideLoadingView(DO_NOT_UPDATE_UI); // set checked status // Note: the preference listener is disabled when the switch is updated, otherwise it will be seen // as a user action on the preference boolean isChecked = RoomState.DIRECTORY_VISIBILITY_PUBLIC.equals(aVisibilityValue); enableSharedPreferenceListener(false); mRoomDirectoryVisibilitySwitch.setChecked(isChecked); enableSharedPreferenceListener(true); } }); } @Override public void onSuccess(String visibility) { handleResponseOnUiThread(visibility); } @Override public void onNetworkError(Exception e) { Log.w(LOG_TAG, "## getDirectoryVisibility(): onNetworkError Msg=" + e.getLocalizedMessage()); handleResponseOnUiThread(null); } @Override public void onMatrixError(MatrixError matrixError) { Log.w(LOG_TAG, "## getDirectoryVisibility(): onMatrixError Msg=" + matrixError.getLocalizedMessage()); handleResponseOnUiThread(null); } @Override public void onUnexpectedError(Exception e) { Log.w(LOG_TAG, "## getDirectoryVisibility(): onUnexpectedError Msg=" + e.getLocalizedMessage()); handleResponseOnUiThread(null); } }); } } /** * Display the access room warning. */ private void displayAccessRoomWarning() { Toast.makeText(getActivity(), R.string.room_settings_room_access_warning, Toast.LENGTH_SHORT).show(); } /** * Enable / disable preferences according to the power levels. */ private void updatePreferenceAccessFromPowerLevel() { boolean canUpdateAvatar = false; boolean canUpdateName = false; boolean canUpdateTopic = false; boolean isAdmin = false; boolean isConnected = Matrix.getInstance(getActivity()).isConnected(); // cannot refresh if there is no valid session / room if ((null != mRoom) && (null != mSession)) { PowerLevels powerLevels = mRoom.getLiveState().getPowerLevels(); if (null != powerLevels) { int powerLevel = powerLevels.getUserPowerLevel(mSession.getMyUserId()); canUpdateAvatar = powerLevel >= powerLevels .minimumPowerLevelForSendingEventAsStateEvent(Event.EVENT_TYPE_STATE_ROOM_AVATAR); canUpdateName = powerLevel >= powerLevels .minimumPowerLevelForSendingEventAsStateEvent(Event.EVENT_TYPE_STATE_ROOM_NAME); canUpdateTopic = powerLevel >= powerLevels .minimumPowerLevelForSendingEventAsStateEvent(Event.EVENT_TYPE_STATE_ROOM_TOPIC); isAdmin = (powerLevel >= CommonActivityUtils.UTILS_POWER_LEVEL_ADMIN); } } else { Log.w(LOG_TAG, "## updatePreferenceAccessFromPowerLevel(): session or room may be missing"); } if (null != mRoomPhotoAvatar) mRoomPhotoAvatar.setEnabled(canUpdateAvatar && isConnected); if (null != mRoomNameEditTxt) mRoomNameEditTxt.setEnabled(canUpdateName && isConnected); if (null != mRoomTopicEditTxt) mRoomTopicEditTxt.setEnabled(canUpdateTopic && isConnected); // room present in the directory list: admin only if (null != mRoomDirectoryVisibilitySwitch) mRoomDirectoryVisibilitySwitch.setEnabled(isAdmin && isConnected); // room tagging: no power condition if (null != mRoomTagListPreference) mRoomTagListPreference.setEnabled(isConnected); // room access rules: admin only if (null != mRoomAccessRulesListPreference) { mRoomAccessRulesListPreference.setEnabled(isAdmin && isConnected); mRoomAccessRulesListPreference.setWarningIconVisible((0 == mRoom.getAliases().size()) && !TextUtils.equals(RoomState.JOIN_RULE_INVITE, mRoom.getLiveState().join_rule)); } // room read history: admin only if (null != mRoomHistoryReadabilityRulesListPreference) { mRoomHistoryReadabilityRulesListPreference.setEnabled(isAdmin && isConnected); } if (null != mRoomNotificationsPreference) { mRoomNotificationsPreference.setEnabled(isConnected); } } /** * Update the UI preference from the values taken from * the SDK layer. */ private void updatePreferenceUiValues() { String value; String summary; Resources resources; if ((null == mSession) || (null == mRoom)) { Log.w(LOG_TAG, "## updatePreferenceUiValues(): session or room may be missing"); return; } if (null != mRoomPhotoAvatar) { mRoomPhotoAvatar.refreshAvatar(); } // update the room name preference if (null != mRoomNameEditTxt) { value = mRoom.getLiveState().name; mRoomNameEditTxt.setSummary(value); mRoomNameEditTxt.setText(value); } // update the room topic preference if (null != mRoomTopicEditTxt) { value = mRoom.getTopic(); mRoomTopicEditTxt.setSummary(value); mRoomTopicEditTxt.setText(value); } // update room directory visibility // if(null != mRoomDirectoryVisibilitySwitch) { // boolean isRoomPublic = TextUtils.equals(mRoom.getVisibility()/*getLiveState().visibility ou .isPublic()*/, RoomState.DIRECTORY_VISIBILITY_PUBLIC); // if(isRoomPublic !isRoomPublic= mRoomDirectoryVisibilitySwitch.isChecked()) // mRoomDirectoryVisibilitySwitch.setChecked(isRoomPublic); // } // check if fragment is added to its Activity before calling getResources(). // getResources() may throw an exception ".. not attached to Activity" if (!isAdded()) { Log.e(LOG_TAG, "## updatePreferenceUiValues(): fragment not added to Activity - isAdded()=false"); return; } else { // in some weird cases, even if isAdded() = true, sometimes getResources() may fail, // so we need to catch the exception try { resources = getResources(); } catch (Exception ex) { Log.e(LOG_TAG, "## updatePreferenceUiValues(): Exception in getResources() - Msg=" + ex.getLocalizedMessage()); return; } } // room guest access rules if ((null != mRoomAccessRulesListPreference) && (null != resources)) { String joinRule = mRoom.getLiveState().join_rule; String guestAccessRule = mRoom.getLiveState().getGuestAccess(); if (RoomState.JOIN_RULE_INVITE .equals(joinRule)/* && RoomState.GUEST_ACCESS_CAN_JOIN.equals(guestAccessRule)*/) { // "Only people who have been invited" requires: {join_rule: "invite"} and {guest_access: "can_join"} value = ACCESS_RULES_ONLY_PEOPLE_INVITED; summary = resources.getString(R.string.room_settings_room_access_entry_only_invited); } else if (RoomState.JOIN_RULE_PUBLIC.equals(joinRule) && RoomState.GUEST_ACCESS_FORBIDDEN.equals(guestAccessRule)) { // "Anyone who knows the room's link, apart from guests" requires: {join_rule: "public"} and {guest_access: "forbidden"} value = ACCESS_RULES_ANYONE_WITH_LINK_APART_GUEST; summary = resources .getString(R.string.room_settings_room_access_entry_anyone_with_link_apart_guest); } else if (RoomState.JOIN_RULE_PUBLIC.equals(joinRule) && RoomState.GUEST_ACCESS_CAN_JOIN.equals(guestAccessRule)) { // "Anyone who knows the room's link, including guests" requires: {join_rule: "public"} and {guest_access: "can_join"} value = ACCESS_RULES_ANYONE_WITH_LINK_INCLUDING_GUEST; summary = resources .getString(R.string.room_settings_room_access_entry_anyone_with_link_including_guest); } else { // unknown combination value value = null; summary = null; Log.w(LOG_TAG, "## updatePreferenceUiValues(): unknown room access configuration joinRule=" + joinRule + " and guestAccessRule=" + guestAccessRule); } if (null != value) { mRoomAccessRulesListPreference.setValue(value); mRoomAccessRulesListPreference.setSummary(summary); } else { mRoomHistoryReadabilityRulesListPreference.setValue(UNKNOWN_VALUE); mRoomHistoryReadabilityRulesListPreference.setSummary(""); } } if (null != mRoomNotificationsPreference) { BingRulesManager.RoomNotificationState state = mSession.getDataHandler().getBingRulesManager() .getRoomNotificationState(mRoom.getRoomId()); if (state == BingRulesManager.RoomNotificationState.ALL_MESSAGES_NOISY) { value = getString(R.string.room_settings_all_messages_noisy); } else if (state == BingRulesManager.RoomNotificationState.ALL_MESSAGES) { value = getString(R.string.room_settings_all_messages); } else if (state == BingRulesManager.RoomNotificationState.MENTIONS_ONLY) { value = getString(R.string.room_settings_mention_only); } else { value = getString(R.string.room_settings_mute); } mRoomNotificationsPreference.setValue(value); mRoomNotificationsPreference.setSummary(value); } // update the room tag preference if (null != mRoomTagListPreference) { if (null != mRoom.getAccountData() && (null != resources)) { //Set<String> customTagList = mRoom.getAccountData().getKeys(); if (null != mRoom.getAccountData().roomTag(RoomTag.ROOM_TAG_FAVOURITE)) { value = resources.getString(R.string.room_settings_tag_pref_entry_value_favourite); summary = resources.getString(R.string.room_settings_tag_pref_entry_favourite); } else if (null != mRoom.getAccountData().roomTag(RoomTag.ROOM_TAG_LOW_PRIORITY)) { value = resources.getString(R.string.room_settings_tag_pref_entry_value_low_priority); summary = resources.getString(R.string.room_settings_tag_pref_entry_low_priority); /* For further use in case of multiple tags support } else if(!mRoom.getAccountData().getKeys().isEmpty()) { for(String tag : customTagList){ summary += (!summary.isEmpty()?" ":"") + tag; }*/ } else { // no tag associated to the room value = resources.getString(R.string.room_settings_tag_pref_entry_value_none); summary = Html.fromHtml( "<i>" + getResources().getString(R.string.room_settings_tag_pref_no_tag) + "</i>") .toString(); } mRoomTagListPreference.setValue(value); mRoomTagListPreference.setSummary(summary); } } // room history readability if (null != mRoomHistoryReadabilityRulesListPreference) { value = mRoom.getLiveState().getHistoryVisibility(); summary = null; if ((null != value) && (null != resources)) { // get summary value if (value.equals(resources.getString(R.string.room_settings_read_history_entry_value_anyone))) { summary = resources.getString(R.string.room_settings_read_history_entry_anyone); } else if (value.equals(resources.getString( R.string.room_settings_read_history_entry_value_members_only_option_time_shared))) { summary = resources .getString(R.string.room_settings_read_history_entry_members_only_option_time_shared); } else if (value.equals(resources .getString(R.string.room_settings_read_history_entry_value_members_only_invited))) { summary = resources.getString(R.string.room_settings_read_history_entry_members_only_invited); } else if (value.equals( resources.getString(R.string.room_settings_read_history_entry_value_members_only_joined))) { summary = resources.getString(R.string.room_settings_read_history_entry_members_only_joined); } else { // unknown value Log.w(LOG_TAG, "## updatePreferenceUiValues(): unknown room read history value=" + value); summary = null; } } if (null != summary) { mRoomHistoryReadabilityRulesListPreference.setValue(value); mRoomHistoryReadabilityRulesListPreference.setSummary(summary); } else { mRoomHistoryReadabilityRulesListPreference.setValue(UNKNOWN_VALUE); mRoomHistoryReadabilityRulesListPreference.setSummary(""); } } } // OnSharedPreferenceChangeListener implementation /** * Main entry point handler for any preference changes. For each setting a dedicated handler is * called to process the setting. * * @param aSharedPreferences preference instance * @param aKey preference key as it is defined in the XML */ @Override public void onSharedPreferenceChanged(SharedPreferences aSharedPreferences, String aKey) { if (mIsUiUpdateSkipped) { Log.d(LOG_TAG, "## onSharedPreferenceChanged(): Skipped"); return; } if (null == getActivity()) { Log.d(LOG_TAG, "## onSharedPreferenceChanged(): no attached to an activity"); return; } if (aKey.equals(PREF_KEY_ROOM_PHOTO_AVATAR)) { // unused flow: onSharedPreferenceChanged not triggered for room avatar photo onRoomAvatarPreferenceChanged(); } else if (aKey.equals(PREF_KEY_ROOM_NAME)) { onRoomNamePreferenceChanged(); } else if (aKey.equals(PREF_KEY_ROOM_TOPIC)) { onRoomTopicPreferenceChanged(); } else if (aKey.equals(PREF_KEY_ROOM_NOTIFICATIONS_LIST)) { onRoomNotificationsPreferenceChanged(); } else if (aKey.equals(PREF_KEY_ROOM_DIRECTORY_VISIBILITY_SWITCH)) { onRoomDirectoryVisibilityPreferenceChanged(); // TBT } else if (aKey.equals(PREF_KEY_ROOM_TAG_LIST)) { onRoomTagPreferenceChanged(); // TBT } else if (aKey.equals(PREF_KEY_ROOM_ACCESS_RULES_LIST)) { onRoomAccessPreferenceChanged(); } else if (aKey.equals(PREF_KEY_ROOM_HISTORY_READABILITY_LIST)) { onRoomHistoryReadabilityPreferenceChanged(); // TBT } else { Log.w(LOG_TAG, "## onSharedPreferenceChanged(): unknown aKey = " + aKey); } } /** * The room history readability has been updated. */ private void onRoomHistoryReadabilityPreferenceChanged() { // sanity check if ((null == mRoom) || (null == mRoomHistoryReadabilityRulesListPreference)) { Log.w(LOG_TAG, "## onRoomHistoryReadabilityPreferenceChanged(): not processed due to invalid parameters"); return; } // get new and previous values String previousValue = mRoom.getLiveState().history_visibility; String newValue = mRoomHistoryReadabilityRulesListPreference.getValue(); if (!TextUtils.equals(newValue, previousValue)) { String historyVisibility; if (newValue.equals(getResources().getString(R.string.room_settings_read_history_entry_value_anyone))) { historyVisibility = RoomState.HISTORY_VISIBILITY_WORLD_READABLE; } else if (newValue.equals(getResources() .getString(R.string.room_settings_read_history_entry_value_members_only_option_time_shared))) { historyVisibility = RoomState.HISTORY_VISIBILITY_SHARED; } else if (newValue.equals(getResources() .getString(R.string.room_settings_read_history_entry_value_members_only_invited))) { historyVisibility = RoomState.HISTORY_VISIBILITY_INVITED; } else if (newValue.equals(getResources() .getString(R.string.room_settings_read_history_entry_value_members_only_joined))) { historyVisibility = RoomState.HISTORY_VISIBILITY_JOINED; } else { // unknown value Log.w(LOG_TAG, "## onRoomHistoryReadabilityPreferenceChanged(): unknown value:" + newValue); historyVisibility = null; } if (null != historyVisibility) { displayLoadingView(); mRoom.updateHistoryVisibility(historyVisibility, mUpdateCallback); } } } private void onRoomTagPreferenceChanged() { boolean isSupportedTag = true; // sanity check if ((null == mRoom) || (null == mRoomTagListPreference)) { Log.w(LOG_TAG, "## onRoomTagPreferenceChanged(): not processed due to invalid parameters"); } else { String newTag = mRoomTagListPreference.getValue(); String currentTag = null; Double tagOrder = 0.0; // retrieve the tag from the room info RoomAccountData accountData = mRoom.getAccountData(); if ((null != accountData) && accountData.hasTags()) { currentTag = accountData.getKeys().iterator().next(); } if (!newTag.equals(currentTag)) { if (newTag .equals(getResources().getString(R.string.room_settings_tag_pref_entry_value_favourite))) { newTag = RoomTag.ROOM_TAG_FAVOURITE; } else if (newTag.equals( getResources().getString(R.string.room_settings_tag_pref_entry_value_low_priority))) { newTag = RoomTag.ROOM_TAG_LOW_PRIORITY; } else if (newTag .equals(getResources().getString(R.string.room_settings_tag_pref_entry_value_none))) { newTag = null; } else { // unknown tag.. very unlikely isSupportedTag = false; Log.w(LOG_TAG, "## onRoomTagPreferenceChanged() not supported tag = " + newTag); } } if (isSupportedTag) { displayLoadingView(); mRoom.replaceTag(currentTag, newTag, tagOrder, mUpdateCallback); } } } private void onRoomAccessPreferenceChanged() { if ((null == mRoom) || (null == mRoomAccessRulesListPreference)) { Log.w(LOG_TAG, "## onRoomAccessPreferenceChanged(): not processed due to invalid parameters"); } else { String joinRuleToApply = null; String guestAccessRuleToApply = null; // get new and previous values String previousJoinRule = mRoom.getLiveState().join_rule; String previousGuestAccessRule = mRoom.getLiveState().getGuestAccess(); String newValue = mRoomAccessRulesListPreference.getValue(); if (ACCESS_RULES_ONLY_PEOPLE_INVITED.equals(newValue)) { // requires: {join_rule: "invite"} and {guest_access: "can_join"} joinRuleToApply = !RoomState.JOIN_RULE_INVITE.equals(previousJoinRule) ? RoomState.JOIN_RULE_INVITE : null; guestAccessRuleToApply = !RoomState.GUEST_ACCESS_CAN_JOIN.equals(previousGuestAccessRule) ? RoomState.GUEST_ACCESS_CAN_JOIN : null; } else if (ACCESS_RULES_ANYONE_WITH_LINK_APART_GUEST.equals(newValue)) { // requires: {join_rule: "public"} and {guest_access: "forbidden"} joinRuleToApply = !RoomState.JOIN_RULE_PUBLIC.equals(previousJoinRule) ? RoomState.JOIN_RULE_PUBLIC : null; guestAccessRuleToApply = !RoomState.GUEST_ACCESS_FORBIDDEN.equals(previousGuestAccessRule) ? RoomState.GUEST_ACCESS_FORBIDDEN : null; if (0 == mRoom.getAliases().size()) { displayAccessRoomWarning(); } } else if (ACCESS_RULES_ANYONE_WITH_LINK_INCLUDING_GUEST.equals(newValue)) { // requires: {join_rule: "public"} and {guest_access: "can_join"} joinRuleToApply = !RoomState.JOIN_RULE_PUBLIC.equals(previousJoinRule) ? RoomState.JOIN_RULE_PUBLIC : null; guestAccessRuleToApply = !RoomState.GUEST_ACCESS_CAN_JOIN.equals(previousGuestAccessRule) ? RoomState.GUEST_ACCESS_CAN_JOIN : null; if (0 == mRoom.getAliases().size()) { displayAccessRoomWarning(); } } else { // unknown value Log.d(LOG_TAG, "## onRoomAccessPreferenceChanged(): unknown selected value = " + newValue); } if (null != joinRuleToApply) { displayLoadingView(); mRoom.updateJoinRules(joinRuleToApply, mUpdateCallback); } if (null != guestAccessRuleToApply) { displayLoadingView(); mRoom.updateGuestAccess(guestAccessRuleToApply, mUpdateCallback); } } } private void onRoomDirectoryVisibilityPreferenceChanged() { String visibility; if ((null == mRoom) || (null == mRoomDirectoryVisibilitySwitch)) { Log.w(LOG_TAG, "## onRoomDirectoryVisibilityPreferenceChanged(): not processed due to invalid parameters"); visibility = null; } else if (mRoomDirectoryVisibilitySwitch.isChecked()) { visibility = RoomState.DIRECTORY_VISIBILITY_PUBLIC; } else { visibility = RoomState.DIRECTORY_VISIBILITY_PRIVATE; } if (null != visibility) { Log.d(LOG_TAG, "## onRoomDirectoryVisibilityPreferenceChanged(): directory visibility set to " + visibility); displayLoadingView(); mRoom.updateDirectoryVisibility(visibility, mUpdateCallback); } } /** */ private void onRoomNotificationsPreferenceChanged() { // sanity check if ((null == mRoom) || (null == mBingRulesManager)) { return; } String value = mRoomNotificationsPreference.getValue(); BingRulesManager.RoomNotificationState updatedState; if (TextUtils.equals(value, getString(R.string.room_settings_all_messages_noisy))) { updatedState = BingRulesManager.RoomNotificationState.ALL_MESSAGES_NOISY; } else if (TextUtils.equals(value, getString(R.string.room_settings_all_messages))) { updatedState = BingRulesManager.RoomNotificationState.ALL_MESSAGES; } else if (TextUtils.equals(value, getString(R.string.room_settings_mention_only))) { updatedState = BingRulesManager.RoomNotificationState.MENTIONS_ONLY; } else { updatedState = BingRulesManager.RoomNotificationState.MUTE; } // update only, if values are different if (mBingRulesManager.getRoomNotificationState(mRoom.getRoomId()) != updatedState) { displayLoadingView(); mBingRulesManager.updateRoomNotificationState(mRoom.getRoomId(), updatedState, new BingRulesManager.onBingRuleUpdateListener() { @Override public void onBingRuleUpdateSuccess() { Log.d(LOG_TAG, "##onRoomNotificationsPreferenceChanged(): update succeed"); hideLoadingView(UPDATE_UI); } @Override public void onBingRuleUpdateFailure(String errorMessage) { Log.w(LOG_TAG, "##onRoomNotificationsPreferenceChanged(): BingRuleUpdateFailure"); hideLoadingView(DO_NOT_UPDATE_UI); } }); } } /** * Action when updating the room name. */ private void onRoomNamePreferenceChanged() { // sanity check if ((null == mRoom) || (null == mSession) || (null == mRoomNameEditTxt)) { return; } // get new and previous values String previousName = mRoom.getLiveState().name; String newName = mRoomNameEditTxt.getText(); // update only, if values are different if (!TextUtils.equals(previousName, newName)) { displayLoadingView(); Log.d(LOG_TAG, "##onRoomNamePreferenceChanged to " + newName); mRoom.updateName(newName, mUpdateCallback); } } /** * Action when updating the room topic. */ private void onRoomTopicPreferenceChanged() { // sanity check if (null == mRoom) { return; } // get new and previous values String previousTopic = mRoom.getTopic(); String newTopic = mRoomTopicEditTxt.getText(); // update only, if values are different if (!TextUtils.equals(previousTopic, newTopic)) { displayLoadingView(); Log.d(LOG_TAG, "## update topic to " + newTopic); mRoom.updateTopic(newTopic, mUpdateCallback); } } /** * Update the room avatar. * Start the camera activity to take the avatar picture. */ private void onRoomAvatarPreferenceChanged() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { Intent intent = new Intent(getActivity(), VectorMediasPickerActivity.class); intent.putExtra(VectorMediasPickerActivity.EXTRA_AVATAR_MODE, true); startActivityForResult(intent, REQ_CODE_UPDATE_ROOM_AVATAR); } }); } /** * Process the result of the room avatar picture. * * @param aRequestCode request ID * @param aResultCode request status code * @param aData result data */ @Override public void onActivityResult(int aRequestCode, int aResultCode, final Intent aData) { super.onActivityResult(aRequestCode, aResultCode, aData); if (REQ_CODE_UPDATE_ROOM_AVATAR == aRequestCode) { onActivityResultRoomAvatarUpdate(aResultCode, aData); } } /** * Update the avatar from the data provided the medias picker. * * @param aResultCode the result code. * @param aData the provided data. */ private void onActivityResultRoomAvatarUpdate(int aResultCode, final Intent aData) { // sanity check if (null == mSession) { return; } if (aResultCode == Activity.RESULT_OK) { Uri thumbnailUri = VectorUtils.getThumbnailUriFromIntent(getActivity(), aData, mSession.getMediasCache()); if (null != thumbnailUri) { displayLoadingView(); // save the bitmap URL on the server ResourceUtils.Resource resource = ResourceUtils.openResource(getActivity(), thumbnailUri, null); if (null != resource) { mSession.getMediasCache().uploadContent(resource.mContentStream, null, resource.mMimeType, null, new MXMediaUploadListener() { @Override public void onUploadError(String uploadId, int serverResponseCode, String serverErrorMessage) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { Log.e(LOG_TAG, "Fail to upload the avatar"); hideLoadingView(DO_NOT_UPDATE_UI); } }); } @Override public void onUploadComplete(final String uploadId, final String contentUri) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { Log.d(LOG_TAG, "The avatar has been uploaded, update the room avatar"); mRoom.updateAvatarUrl(contentUri, mUpdateCallback); } }); } }); } } } } /** * Display the loading view in the parent activity layout. * This view is disabled/enabled to achieve a waiting screen. */ private void displayLoadingView() { Activity parentActivity = getActivity(); if (null != parentActivity) { parentActivity.runOnUiThread(new Runnable() { public void run() { // disable the fragment container view to disable preferences access //enablePreferenceWidgets(false); // disable preference screen during server updates if (null != mParentFragmentContainerView) mParentFragmentContainerView.setEnabled(false); // display the loading progress bar screen if (null != mParentLoadingView) { mParentLoadingView.setVisibility(View.VISIBLE); } } }); } } /** * Hide the loading progress bar screen and * update the UI if required. */ private void hideLoadingView(boolean aIsUiRefreshRequired) { getActivity().runOnUiThread(new Runnable() { public void run() { // enable preference screen after server updates finished if (null != mParentFragmentContainerView) mParentFragmentContainerView.setEnabled(true); // enable preference widgets //enablePreferenceWidgets(true); if (null != mParentLoadingView) { mParentLoadingView.setVisibility(View.GONE); } } }); if (aIsUiRefreshRequired) { updateUiOnUiThread(); } } //================================================================================ // Banned members management //================================================================================ /** * Refresh the banned users list. */ private void refreshBannedMembersList() { ArrayList<RoomMember> bannedMembers = new ArrayList<>(); Collection<RoomMember> members = mRoom.getMembers(); if (null != members) { for (RoomMember member : members) { if (TextUtils.equals(member.membership, RoomMember.MEMBERSHIP_BAN)) { bannedMembers.add(member); } } } Collections.sort(bannedMembers, new Comparator<RoomMember>() { @Override public int compare(RoomMember m1, RoomMember m2) { return m1.getUserId().toLowerCase(VectorApp.getApplicationLocale()) .compareTo(m2.getUserId().toLowerCase(VectorApp.getApplicationLocale())); } }); PreferenceScreen preferenceScreen = getPreferenceScreen(); preferenceScreen.removePreference(mBannedMembersSettingsCategoryDivider); preferenceScreen.removePreference(mBannedMembersSettingsCategory); mBannedMembersSettingsCategory.removeAll(); if (bannedMembers.size() > 0) { preferenceScreen.addPreference(mBannedMembersSettingsCategoryDivider); preferenceScreen.addPreference(mBannedMembersSettingsCategory); for (RoomMember member : bannedMembers) { VectorCustomActionEditTextPreference preference = new VectorCustomActionEditTextPreference( getActivity()); final String userId = member.getUserId(); preference.setTitle(userId); preference.setKey(BANNED_PREFERENCE_KEY_BASE + userId); preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { Intent startRoomInfoIntent = new Intent(getActivity(), VectorMemberDetailsActivity.class); startRoomInfoIntent.putExtra(VectorMemberDetailsActivity.EXTRA_MEMBER_ID, userId); startRoomInfoIntent.putExtra(VectorMemberDetailsActivity.EXTRA_ROOM_ID, mRoom.getRoomId()); startRoomInfoIntent.putExtra(VectorMemberDetailsActivity.EXTRA_MATRIX_ID, mSession.getCredentials().userId); getActivity().startActivity(startRoomInfoIntent); return false; } }); mBannedMembersSettingsCategory.addPreference(preference); } } } //================================================================================ // flair management //================================================================================ private final ApiCallback mFlairUpdatesCallback=new ApiCallback<Void>(){@Override public void onSuccess(Void info){getActivity().runOnUiThread(new Runnable(){@Override public void run(){hideLoadingView(false);refreshFlair();}});} /** * Error management. * @param errorMessage the error message */ private void onError(final String errorMessage){getActivity().runOnUiThread(new Runnable(){@Override public void run(){Toast.makeText(getActivity(),errorMessage,Toast.LENGTH_SHORT).show();hideLoadingView(false);refreshFlair();}});} @Override public void onNetworkError(Exception e){onError(e.getLocalizedMessage());} @Override public void onMatrixError(MatrixError e){onError(e.getLocalizedMessage());} @Override public void onUnexpectedError(Exception e){onError(e.getLocalizedMessage());}}; /** * Tells if the current user can updates the related group aka flairs * * @return true if the user is allowed. */ private boolean canUpdateFlair() { boolean canUpdateAliases = false; PowerLevels powerLevels = mRoom.getLiveState().getPowerLevels(); if (null != powerLevels) { int powerLevel = powerLevels.getUserPowerLevel(mSession.getMyUserId()); canUpdateAliases = powerLevel >= powerLevels .minimumPowerLevelForSendingEventAsStateEvent(Event.EVENT_TYPE_STATE_RELATED_GROUPS); } return canUpdateAliases; } /** * Refresh the flair list */ private void refreshFlair() { final List<String> groups = mRoom.getLiveState().getRelatedGroups(); Collections.sort(groups, String.CASE_INSENSITIVE_ORDER); mFlairSettingsCategory.removeAll(); if (!groups.isEmpty()) { for (final String groupId : groups) { VectorCustomActionEditTextPreference preference = new VectorCustomActionEditTextPreference(getActivity()); preference.setTitle(groupId); preference.setKey(FLAIR_PREFERENCE_KEY_BASE + groupId); preference.setOnPreferenceLongClickListener(new VectorCustomActionEditTextPreference.OnPreferenceLongClickListener() { @Override public boolean onPreferenceLongClick(Preference preference) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { displayLoadingView(); mRoom.removeRelatedGroup(groupId, mFlairUpdatesCallback); } }); return true; } }); mFlairSettingsCategory.addPreference(preference); } } else { VectorCustomActionEditTextPreference preference = new VectorCustomActionEditTextPreference(getActivity()); preference.setTitle(getString(R.string.room_settings_no_flair)); preference.setKey(FLAIR_PREFERENCE_KEY_BASE + "no_flair"); mFlairSettingsCategory.addPreference(preference); } if (canUpdateFlair()) { // display the "add addresses" entry EditTextPreference addAddressPreference = new EditTextPreference(getActivity()); addAddressPreference.setTitle(R.string.room_settings_add_new_group); addAddressPreference.setDialogTitle(R.string.room_settings_add_new_group); addAddressPreference.setKey(FLAIR_PREFERENCE_KEY_BASE + "__add"); addAddressPreference.setIcon(CommonActivityUtils.tintDrawable(getActivity(), ContextCompat.getDrawable(getActivity(), R.drawable.ic_add_black), R.attr.settings_icon_tint_color)); addAddressPreference.setOnPreferenceChangeListener( new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { final String groupId = ((String) newValue).trim(); // ignore empty alias if (!TextUtils.isEmpty(groupId)) { if (!MXSession.isGroupId(groupId)) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.room_settings_invalid_group_format_dialog_title); builder.setMessage(getString(R.string.room_settings_invalid_group_format_dialog_body, groupId)); builder.setPositiveButton(R.string.ok, null); AlertDialog dialog = builder.create(); dialog.show(); } else if (!groups.contains(groupId)) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { displayLoadingView(); mRoom.addRelatedGroup(groupId, mFlairUpdatesCallback); } }); } } return false; }}); mFlairSettingsCategory.addPreference(addAddressPreference);}} //================================================================================ // Aliases management //================================================================================ private final ApiCallback mAliasUpdatesCallback=new ApiCallback<Void>(){@Override public void onSuccess(Void info){getActivity().runOnUiThread(new Runnable(){@Override public void run(){hideLoadingView(false);refreshAddresses();}});} /** * Error management. * @param errorMessage the error message */ private void onError(final String errorMessage){getActivity().runOnUiThread(new Runnable(){@Override public void run(){Toast.makeText(getActivity(),errorMessage,Toast.LENGTH_SHORT).show();hideLoadingView(false);refreshAddresses();}});} @Override public void onNetworkError(Exception e){onError(e.getLocalizedMessage());} @Override public void onMatrixError(MatrixError e){onError(e.getLocalizedMessage());} @Override public void onUnexpectedError(Exception e){onError(e.getLocalizedMessage());}}; /** * Manage the long click on an address. * * @param roomAlias the room alias. * @param anchorView the popup menu anchor view. */ @SuppressLint("NewApi") private void onAddressLongClick(final String roomAlias, final View anchorView) { Context context = getActivity(); final PopupMenu popup = (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) ? new PopupMenu(context, anchorView, Gravity.END) : new PopupMenu(context, anchorView); popup.getMenuInflater().inflate(R.menu.vector_room_settings_addresses, popup.getMenu()); // force to display the icons try { Field[] fields = popup.getClass().getDeclaredFields(); for (Field field : fields) { if ("mPopup".equals(field.getName())) { field.setAccessible(true); Object menuPopupHelper = field.get(popup); Class<?> classPopupHelper = Class.forName(menuPopupHelper.getClass().getName()); Method setForceIcons = classPopupHelper.getMethod("setForceShowIcon", boolean.class); setForceIcons.invoke(menuPopupHelper, true); break; } } } catch (Exception e) { Log.e(LOG_TAG, "onMessageClick : force to display the icons failed " + e.getLocalizedMessage()); } Menu menu = popup.getMenu(); CommonActivityUtils.tintMenuIcons(menu, ThemeUtils.getColor(context, R.attr.icon_tint_on_light_action_bar_color)); String canonicalAlias = mRoom.getLiveState().alias; boolean canUpdateAliases = canUpdateAliases(); menu.findItem(R.id.ic_action_vector_delete_alias).setVisible(canUpdateAliases); menu.findItem(R.id.ic_action_vector_set_as_main_address).setVisible(canUpdateAliases && !TextUtils.equals(roomAlias, canonicalAlias)); menu.findItem(R.id.ic_action_vector_unset_main_address).setVisible(canUpdateAliases && TextUtils.equals(roomAlias, canonicalAlias)); // display the menu popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(final MenuItem item) { if (item.getItemId() == R.id.ic_action_vector_unset_main_address) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(R.string.room_settings_addresses_disable_main_address_prompt_msg); builder.setTitle(R.string.room_settings_addresses_disable_main_address_prompt_title); builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { displayLoadingView(); mRoom.updateCanonicalAlias(null, mAliasUpdatesCallback); } }); builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // nothing } }); AlertDialog dialog = builder.create(); dialog.show(); } else if (item.getItemId() == R.id.ic_action_vector_set_as_main_address) { displayLoadingView(); mRoom.updateCanonicalAlias(roomAlias, mAliasUpdatesCallback); } else if (item.getItemId() == R.id.ic_action_vector_delete_alias) { displayLoadingView(); mRoom.removeAlias(roomAlias, new ApiCallback<Void>() { @Override public void onSuccess(Void info) { // when there is only one alias, it becomes the main alias. if (mRoom.getAliases().size() == 1) { mRoom.updateCanonicalAlias(mRoom.getAliases().get(0), mAliasUpdatesCallback); } else { mAliasUpdatesCallback.onSuccess(info); } } @Override public void onNetworkError(Exception e) { mAliasUpdatesCallback.onNetworkError(e); } @Override public void onMatrixError(MatrixError e) { mAliasUpdatesCallback.onMatrixError(e); } @Override public void onUnexpectedError(Exception e) { mAliasUpdatesCallback.onUnexpectedError(e); } }); }else if(item.getItemId()==R.id.ic_action_vector_room_url) { VectorUtils.copyToClipboard(getActivity(), VectorUtils.getPermalink(roomAlias, null)); }else { VectorUtils.copyToClipboard(getActivity(), roomAlias); } return true; }}); popup.show();} /** * Tells if the current user can updates the room aliases. * * @return true if the user is allowed. */ private boolean canUpdateAliases() { boolean canUpdateAliases = false; PowerLevels powerLevels = mRoom.getLiveState().getPowerLevels(); if (null != powerLevels) { int powerLevel = powerLevels.getUserPowerLevel(mSession.getMyUserId()); canUpdateAliases = powerLevel >= powerLevels .minimumPowerLevelForSendingEventAsStateEvent(Event.EVENT_TYPE_STATE_ROOM_ALIASES); } return canUpdateAliases; } /** * Refresh the addresses section */ private void refreshAddresses() { final String localSuffix = ":" + mSession.getHomeServerConfig().getHomeserverUri().getHost(); final String canonicalAlias = mRoom.getLiveState().alias; final ArrayList<String> aliases = new ArrayList<>(mRoom.getAliases()); // remove the displayed preferences mAddressesSettingsCategory.removeAll(); if (0 == aliases.size()) { AddressPreference preference = new AddressPreference(getActivity()); preference.setTitle(getString(R.string.room_settings_addresses_no_local_addresses)); preference.setKey(NO_LOCAL_ADDRESS_PREFERENCE_KEY); mAddressesSettingsCategory.addPreference(preference); } else { ArrayList<String> localAliases = new ArrayList<>(); ArrayList<String> remoteAliases = new ArrayList<>(); for (String alias : aliases) { if (alias.endsWith(localSuffix)) { localAliases.add(alias); } else { remoteAliases.add(alias); } } // the local aliases are displayed first in the list aliases.clear(); aliases.addAll(localAliases); aliases.addAll(remoteAliases); int index = 0; for (String alias : aliases) { AddressPreference preference = new AddressPreference(getActivity()); preference.setTitle(alias); preference.setKey(ADDRESSES_PREFERENCE_KEY_BASE + index); preference.setMainIconVisible(TextUtils.equals(alias, canonicalAlias)); final String fAlias = alias; final AddressPreference fAddressPreference = preference; preference.setOnPreferenceLongClickListener(new VectorCustomActionEditTextPreference.OnPreferenceLongClickListener() { @Override public boolean onPreferenceLongClick(Preference preference) { onAddressLongClick(fAlias, fAddressPreference.getMainIconView()); return true; } }); mAddressesSettingsCategory.addPreference(preference); index++; } } if (canUpdateAliases()) { // display the "add addresses" entry EditTextPreference addAddressPreference = new EditTextPreference(getActivity()); addAddressPreference.setTitle(R.string.room_settings_addresses_add_new_address); addAddressPreference.setDialogTitle(R.string.room_settings_addresses_add_new_address); addAddressPreference.setKey(ADD_ADDRESSES_PREFERENCE_KEY); addAddressPreference.setIcon(CommonActivityUtils.tintDrawable(getActivity(), ContextCompat.getDrawable(getActivity(), R.drawable.ic_add_black), R.attr.settings_icon_tint_color)); addAddressPreference.setOnPreferenceChangeListener( new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { final String newAddress = ((String) newValue).trim(); // ignore empty alias if (!TextUtils.isEmpty(newAddress)) { if (!MXSession.isRoomAlias(newAddress)) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.room_settings_addresses_invalid_format_dialog_title); builder.setMessage(getString(R.string.room_settings_addresses_invalid_format_dialog_body, newAddress)); builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); AlertDialog dialog = builder.create(); dialog.show(); } else if (aliases.indexOf(newAddress) < 0) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { displayLoadingView(); mRoom.addAlias(newAddress, new ApiCallback<Void>() { @Override public void onSuccess(Void info) { // when there is only one alias, it becomes the main alias. if (mRoom.getAliases().size() == 1) { mRoom.updateCanonicalAlias(mRoom.getAliases().get(0), mAliasUpdatesCallback); } else { mAliasUpdatesCallback.onSuccess(info); } } @Override public void onNetworkError(Exception e) { mAliasUpdatesCallback.onNetworkError(e); } @Override public void onMatrixError(MatrixError e) { mAliasUpdatesCallback.onMatrixError(e); } @Override public void onUnexpectedError(Exception e) { mAliasUpdatesCallback.onUnexpectedError(e); } }); } });}}return false;}}); mAddressesSettingsCategory.addPreference(addAddressPreference);}} /** * Refresh the addresses section */ private void refreshEndToEnd() { // encrypt to unverified devices final CheckBoxPreference sendToUnverifiedDevicesPref = (CheckBoxPreference) findPreference(getString(R.string.room_settings_never_send_to_unverified_devices_title)); // reported by GA if (null == sendToUnverifiedDevicesPref) { Log.e(LOG_TAG, "## refreshEndToEnd() : sendToUnverifiedDevicesPref is null"); return; } // test if the crypto is if (null == mSession.getCrypto()) { mAdvandceSettingsCategory.removePreference(sendToUnverifiedDevicesPref); } else if (null != sendToUnverifiedDevicesPref) { if (mRoom.isEncrypted()) { sendToUnverifiedDevicesPref.setChecked(false); mSession.getCrypto().getGlobalBlacklistUnverifiedDevices( new SimpleApiCallback<Boolean>() { @Override public void onSuccess(final Boolean sendToVerifiedDevicesInAnyRoom) { mSession.getCrypto().isRoomBlacklistUnverifiedDevices(mRoom.getRoomId(), new SimpleApiCallback<Boolean>() { @Override public void onSuccess(final Boolean sendToVerifiedDevicesInRoom) { sendToUnverifiedDevicesPref.setChecked(sendToVerifiedDevicesInRoom || sendToVerifiedDevicesInAnyRoom); sendToUnverifiedDevicesPref.setEnabled(!sendToVerifiedDevicesInAnyRoom); } }); } } ); } else if (null != sendToUnverifiedDevicesPref) { mAdvandceSettingsCategory.removePreference(sendToUnverifiedDevicesPref); } sendToUnverifiedDevicesPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { mSession.getCrypto().isRoomBlacklistUnverifiedDevices(mRoom.getRoomId(), new SimpleApiCallback<Boolean>() { @Override public void onSuccess(Boolean status) { if (sendToUnverifiedDevicesPref.isChecked() != status) { SimpleApiCallback<Void> callback = new SimpleApiCallback<Void>() { @Override public void onSuccess(Void info) { } }; if (sendToUnverifiedDevicesPref.isChecked()) { mSession.getCrypto().setRoomBlacklistUnverifiedDevices(mRoom.getRoomId(), callback); } else { mSession.getCrypto().setRoomUnblacklistUnverifiedDevices(mRoom.getRoomId(), callback); } } } }); return true; }});} final String key = PREF_KEY_ENCRYPTION + mRoom.getRoomId(); // remove the displayed preferences Preference e2ePref = mAdvandceSettingsCategory.findPreference(key); if(null!=e2ePref){mAdvandceSettingsCategory.removePreference(e2ePref);} // remove the preference because it might switch from a SwitchPreference to VectorCustomActionEditTextPreference final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences.Editor editor = preferences.edit();editor.remove(key);editor.commit(); if(mRoom.isEncrypted()){ VectorCustomActionEditTextPreference isEncryptedPreference = new VectorCustomActionEditTextPreference( getActivity());isEncryptedPreference.setTitle(R.string.room_settings_addresses_e2e_enabled);isEncryptedPreference.setKey(key);isEncryptedPreference.setIcon(getResources().getDrawable(R.drawable.e2e_verified)); mAdvandceSettingsCategory.addPreference(isEncryptedPreference); }else{ PowerLevels powerLevels = mRoom.getLiveState().getPowerLevels(); int myPowerLevel = -1; int minimumPowerLevel = 0; if(null!=powerLevels) { myPowerLevel = powerLevels.getUserPowerLevel(mSession.getMyUserId()); minimumPowerLevel = powerLevels .minimumPowerLevelForSendingEventAsStateEvent(Event.EVENT_TYPE_MESSAGE_ENCRYPTION); } // Test if the user has enough power levels to enable the crypto is in this room if(myPowerLevel<minimumPowerLevel) { VectorCustomActionEditTextPreference isEncryptedPreference = new VectorCustomActionEditTextPreference( getActivity()); isEncryptedPreference.setTitle(R.string.room_settings_addresses_e2e_disabled); isEncryptedPreference.setKey(key); isEncryptedPreference.setIcon(CommonActivityUtils.tintDrawable(getActivity(), getResources().getDrawable(R.drawable.e2e_unencrypted), R.attr.settings_icon_tint_color)); mAdvandceSettingsCategory.addPreference(isEncryptedPreference); }else if(mSession.isCryptoEnabled()) { final VectorSwitchPreference encryptSwitchPreference = new VectorSwitchPreference(getActivity()); encryptSwitchPreference.setTitle(R.string.room_settings_addresses_e2e_encryption_warning); encryptSwitchPreference.setKey(key); encryptSwitchPreference.setIcon(CommonActivityUtils.tintDrawable(getActivity(), getResources().getDrawable(R.drawable.e2e_unencrypted), R.attr.settings_icon_tint_color)); encryptSwitchPreference.setChecked(false); mAdvandceSettingsCategory.addPreference(encryptSwitchPreference); encryptSwitchPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValueAsVoid) { boolean newValue = (boolean) newValueAsVoid; if (newValue != mRoom.isEncrypted()) { new AlertDialog.Builder(getActivity()) .setTitle(R.string.room_settings_addresses_e2e_prompt_title) .setMessage(R.string.room_settings_addresses_e2e_prompt_message) .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); displayLoadingView(); mRoom.enableEncryptionWithAlgorithm( MXCryptoAlgorithms.MXCRYPTO_ALGORITHM_MEGOLM, new ApiCallback<Void>() { private void onDone() { hideLoadingView(false); refreshEndToEnd(); } @Override public void onSuccess(Void info) { onDone(); } @Override public void onNetworkError(Exception e) { onDone(); } @Override public void onMatrixError(MatrixError e) { onDone(); } @Override public void onUnexpectedError(Exception e) { onDone(); } }); } }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); encryptSwitchPreference.setChecked(false); } }).create().show(); } return true; } }); } }}}