/* * Copyright 2014 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 org.matrix.androidsdk.data; import android.os.Handler; import android.os.Looper; import org.matrix.androidsdk.rest.model.RequestPhoneNumberValidationResponse; import org.matrix.androidsdk.util.Log; import org.matrix.androidsdk.rest.callback.ApiCallback; import org.matrix.androidsdk.rest.callback.SimpleApiCallback; import org.matrix.androidsdk.rest.model.MatrixError; import org.matrix.androidsdk.rest.model.ThirdPartyIdentifier; import org.matrix.androidsdk.rest.model.ThreePid; import org.matrix.androidsdk.rest.model.User; import java.util.ArrayList; import java.util.List; /** * Class representing the logged-in user. */ public class MyUser extends User { private static final String LOG_TAG = "MyUser"; // refresh status private boolean mIsAvatarRefreshed = false; private boolean mIsDisplayNameRefreshed = false; private boolean mAre3PIdsLoaded = false; // the account info is refreshed in one row // so, if there is a pending refresh the listeners are added to this list. private transient ArrayList<ApiCallback<Void>> mRefreshListeners; private transient Handler mUiHandler; // linked emails to the account private transient List<ThirdPartyIdentifier> mEmailIdentifiers = new ArrayList<>(); // linked phone number to the account private transient List<ThirdPartyIdentifier> mPhoneNumberIdentifiers = new ArrayList<>(); public MyUser(User user) { clone(user); mUiHandler = new Handler(Looper.getMainLooper()); } /** * Update the user's display name. * @param displayName the new name * @param callback the async callback */ public void updateDisplayName(final String displayName, ApiCallback<Void> callback) { mDataHandler.getProfileRestClient().updateDisplayname(displayName, new SimpleApiCallback<Void>(callback) { @Override public void onSuccess(Void info) { // Update the object member before calling the given callback MyUser.this.displayname = displayName; MyUser.this.mDataHandler.getStore().setDisplayName(displayName); super.onSuccess(info); } }); } /** * Update the user's avatar URL. * @param avatarUrl the new avatar URL * @param callback the async callback */ public void updateAvatarUrl(final String avatarUrl, ApiCallback<Void> callback) { mDataHandler.getProfileRestClient().updateAvatarUrl(avatarUrl, new SimpleApiCallback<Void>(callback) { @Override public void onSuccess(Void info) { // Update the object member before calling the given callback MyUser.this.setAvatarUrl(avatarUrl); MyUser.this.mDataHandler.getStore().setAvatarURL(avatarUrl); super.onSuccess(info); } }); } /** * Update the user's presence information. * @param presence the presence * @param statusMsg the status message * @param callback the async callback */ public void updatePresence(final String presence, final String statusMsg, ApiCallback<Void> callback) { mDataHandler.getPresenceRestClient().setPresence(presence, statusMsg, new SimpleApiCallback<Void>(callback) { @Override public void onSuccess(Void info) { // Update the object member before calling the given callback MyUser.this.presence = presence; MyUser.this.statusMsg = statusMsg; super.onSuccess(info); } }); } /** * Request a validation token for an email address 3Pid * @param pid the pid to retrieve a token * @param callback the callback when the operation is done */ public void requestEmailValidationToken(ThreePid pid, ApiCallback<Void> callback) { if (null != pid) { pid.requestEmailValidationToken(mDataHandler.getThirdPidRestClient(), null, callback); } } /** * Request a validation token for a phone number 3Pid * @param pid the pid to retrieve a token * @param callback the callback when the operation is done */ public void requestPhoneNumberValidationToken(ThreePid pid, ApiCallback<RequestPhoneNumberValidationResponse> callback) { if (null != pid) { pid.requestPhoneNumberValidationToken(mDataHandler.getThirdPidRestClient(), null, callback); } } /** * Add a new pid to the account. * * @param pid the pid to add. * @param bind true to add it. * @param callback the async callback */ public void add3Pid(final ThreePid pid, final boolean bind, final ApiCallback<Void> callback) { if (null != pid) { mDataHandler.getProfileRestClient().add3PID(pid, bind, new ApiCallback<Void>() { @Override public void onSuccess(Void info) { // refresh the third party identifiers lists refreshThirdPartyIdentifiers(callback); } @Override public void onNetworkError(Exception e) { if (null != callback) { callback.onNetworkError(e); } } @Override public void onMatrixError(MatrixError e) { if (null != callback) { callback.onMatrixError(e); } } @Override public void onUnexpectedError(Exception e) { if (null != callback) { callback.onUnexpectedError(e); } } }); } } /** * Delete a 3pid from an account * * @param pid the pid to delete * @param callback the async callback */ public void delete3Pid(final ThirdPartyIdentifier pid, final ApiCallback<Void> callback){ if (null != pid) { mDataHandler.getProfileRestClient().delete3PID(pid, new ApiCallback<Void>() { @Override public void onSuccess(Void info) { // refresh the third party identifiers lists refreshThirdPartyIdentifiers(callback); } @Override public void onNetworkError(Exception e) { if (null != callback) { callback.onNetworkError(e); } } @Override public void onMatrixError(MatrixError e) { if (null != callback) { callback.onMatrixError(e); } } @Override public void onUnexpectedError(Exception e) { if (null != callback) { callback.onUnexpectedError(e); } } }); } } /** * Build the lists of identifiers */ private void buildIdentifiersLists() { List<ThirdPartyIdentifier> identifiers = mDataHandler.getStore().thirdPartyIdentifiers(); mEmailIdentifiers = new ArrayList<>(); mPhoneNumberIdentifiers = new ArrayList<>(); for (ThirdPartyIdentifier identifier : identifiers) { switch (identifier.medium) { case ThreePid.MEDIUM_EMAIL: mEmailIdentifiers.add(identifier); break; case ThreePid.MEDIUM_MSISDN: mPhoneNumberIdentifiers.add(identifier); break; } } } /** * @return the list of linked emails */ public List<ThirdPartyIdentifier> getlinkedEmails() { if (mEmailIdentifiers == null) { buildIdentifiersLists(); } return mEmailIdentifiers; } /** * @return the list of linked emails */ public List<ThirdPartyIdentifier> getlinkedPhoneNumbers() { if (mPhoneNumberIdentifiers == null) { buildIdentifiersLists(); } return mPhoneNumberIdentifiers; } //================================================================================ // Refresh //================================================================================ /** * Refresh the user data if it is required * @param callback callback when the job is done. */ public void refreshUserInfos(final ApiCallback<Void> callback) { refreshUserInfos(false, callback); } /** * Refresh the user data if it is required * @param callback callback when the job is done. */ public void refreshThirdPartyIdentifiers(final ApiCallback<Void> callback) { mAre3PIdsLoaded = false; refreshUserInfos(false, callback); } /** * Refresh the user data if it is required * @param skipPendingTest true to do not check if the refreshes started (private use) * @param callback callback when the job is done. */ public void refreshUserInfos(boolean skipPendingTest, final ApiCallback<Void> callback) { if (!skipPendingTest) { boolean isPending; synchronized (this) { // mRefreshListeners == null => no refresh in progress // mRefreshListeners != null -> a refresh is in progress isPending = (null != mRefreshListeners); if (null == mRefreshListeners) { mRefreshListeners = new ArrayList<>(); } if (null != callback) { mRefreshListeners.add(callback); } } if (isPending) { // please wait return; } } if (!mIsDisplayNameRefreshed) { refreshUserDisplayname(); return; } if (!mIsAvatarRefreshed) { refreshUserAvatarUrl(); return; } if (!mAre3PIdsLoaded) { refreshThirdPartyIdentifiers(); return; } synchronized (this) { if (null != mRefreshListeners) { for (ApiCallback<Void> listener : mRefreshListeners) { try { listener.onSuccess(null); } catch (Exception e) { Log.e(LOG_TAG, "## refreshUserInfos() : listener.onSuccess failed " + e.getMessage()); } } } // no more pending refreshes mRefreshListeners = null; } } /** * Refresh the avatar url */ private void refreshUserAvatarUrl() { mDataHandler.getProfileRestClient().avatarUrl(user_id, new SimpleApiCallback<String>() { @Override public void onSuccess(String anAvatarUrl) { if (mDataHandler.isAlive()) { // local value setAvatarUrl(anAvatarUrl); // metadata file mDataHandler.getStore().setAvatarURL(anAvatarUrl); // user mDataHandler.getStore().storeUser(MyUser.this); mIsAvatarRefreshed = true; // jump to the next items refreshUserInfos(true, null); } } private void onError() { if (mDataHandler.isAlive()) { mUiHandler.postDelayed(new Runnable() { @Override public void run() { refreshUserAvatarUrl(); } }, 1 * 1000); } } @Override public void onNetworkError(Exception e) { onError(); } @Override public void onMatrixError(final MatrixError e) { // cannot retrieve this value, jump to the next items mIsAvatarRefreshed = true; refreshUserInfos(true, null); } @Override public void onUnexpectedError(final Exception e) { // cannot retrieve this value, jump to the next items mIsAvatarRefreshed = true; refreshUserInfos(true, null); } }); } /** * Refresh the displayname. */ private void refreshUserDisplayname() { mDataHandler.getProfileRestClient().displayname(user_id, new SimpleApiCallback<String>() { @Override public void onSuccess(String aDisplayname) { if (mDataHandler.isAlive()) { // local value displayname = aDisplayname; // store metadata mDataHandler.getStore().setDisplayName(aDisplayname); mIsDisplayNameRefreshed = true; // jump to the next items refreshUserInfos(true, null); } } private void onError() { if (mDataHandler.isAlive()) { mUiHandler.postDelayed(new Runnable() { @Override public void run() { refreshUserDisplayname(); } }, 1 * 1000); } } @Override public void onNetworkError(Exception e) { onError(); } @Override public void onMatrixError(final MatrixError e) { // cannot retrieve this value, jump to the next items mIsDisplayNameRefreshed = true; refreshUserInfos(true, null); } @Override public void onUnexpectedError(final Exception e) { // cannot retrieve this value, jump to the next items mIsDisplayNameRefreshed = true; refreshUserInfos(true, null); } }); } /** * Refresh the Third party identifiers i.e. the linked email to this account */ public void refreshThirdPartyIdentifiers() { mDataHandler.getProfileRestClient().threePIDs(new SimpleApiCallback<List<ThirdPartyIdentifier>>() { @Override public void onSuccess(List<ThirdPartyIdentifier> identifiers) { if (mDataHandler.isAlive()) { // store mDataHandler.getStore().setThirdPartyIdentifiers(identifiers); buildIdentifiersLists(); mAre3PIdsLoaded = true; // jump to the next items refreshUserInfos(true, null); } } private void onError() { if (mDataHandler.isAlive()) { mUiHandler.postDelayed(new Runnable() { @Override public void run() { refreshThirdPartyIdentifiers(); } }, 1 * 1000); } } @Override public void onNetworkError(Exception e) { onError(); } @Override public void onMatrixError(final MatrixError e) { // cannot retrieve this value, jump to the next items mAre3PIdsLoaded = true; refreshUserInfos(true, null); } @Override public void onUnexpectedError(final Exception e) { // cannot retrieve this value, jump to the next items mAre3PIdsLoaded = true; refreshUserInfos(true, null); } }); } }