Commit 5da259a7 authored by Valere's avatar Valere

Merge branch 'release/0.9.9'

parents b9879851 673d8523
Changes in Riot 0.9.9 (2019-11-25)
===================================================
MatrixSdk 🚀:
- Upgrade to version 0.9.32
- Changelog: https://github.com/matrix-org/matrix-android-sdk/releases/tag/v0.9.32
Features ✨:
- Privacy / Room Widget permissions (#3378)
- Privacy / Widget Permission for jitsi widgets (#3391)
Improvements 🙌:
- Jitsi / Use mx display name in Jitsi conf
Other changes:
- Add User-Interactive Auth to /account/3pid/add (#3333)
Bugfix 🐛:
- Crash / potential NPE after logout (#3367)
- Fix infinite restart loop after token expiration (#3249)
Changes in Riot 0.9.8 (2019-10-09)
===================================================
......
......@@ -10,7 +10,7 @@ Riot-Android [![Buildkite](https://badge.buildkite.com/5ae4f24dd485562a5b59a9f84
Important Announcement
======================
The core team is now working mainly on [RiotX](https://github.com/vector-im/riotX-android). New contributions (PR, issues) are still welcome, but be aware that this codebase will be replaced in the future by the RiotX implementation.
The core team is now working mainly on [RiotX](https://github.com/vector-im/riotX-android). New contributions about security concerns (PR, issues) are still welcome. Other subjects may rarely be addressed, as we do not have time to spend on maintenance on new features. Please contribute to RiotX now!
Contributing
============
......
......@@ -23,8 +23,8 @@ buildscript {
// global properties used in sub modules
ext {
versionCodeProp = 90800
versionNameProp = "0.9.8"
versionCodeProp = 90900
versionNameProp = "0.9.9"
versionBuild = System.getenv("BUILD_NUMBER") as Integer ?: 0
buildNumberProp = "${versionBuild}"
}
......
......@@ -245,7 +245,7 @@ dependencies {
/************* Matrix SDK management **************/
// update settings.gradle
// use the matrix SDK as external dependency
implementation 'com.github.matrix-org:matrix-android-sdk:v0.9.30'
implementation 'com.github.matrix-org:matrix-android-sdk:v0.9.32'
// use the matrix SDK as a sub project
// you have to uncomment some lines in settings.gradle
//implementation project(':matrix-sdk')
......
......@@ -117,6 +117,8 @@ public class Matrix {
@Nullable
private KeyRequestHandler mKeyRequestHandler;
private Map<String, WidgetManagerProvider> mWidgetManagerProviders = new HashMap<>();
// i.e the event has been read from another client
private static final MXEventListener mLiveEventListener = new MXEventListener() {
boolean mClearCacheRequired = false;
......@@ -133,10 +135,12 @@ public class Matrix {
public void onLiveEvent(Event event, RoomState roomState) {
mRefreshUnreadCounter |= Event.EVENT_TYPE_MESSAGE.equals(event.getType()) || Event.EVENT_TYPE_RECEIPT.equals(event.getType());
// TODO update to manage multisessions
WidgetsManager wm = WidgetManagerProvider.INSTANCE.getWidgetManager(VectorApp.getInstance().getApplicationContext());
if (wm != null) {
wm.onLiveEvent(instance.getDefaultSession(), event);
WidgetManagerProvider wp = instance.mWidgetManagerProviders.get(instance.getDefaultSession().getMyUserId());
if (wp != null) {
WidgetsManager wm = wp.getWidgetManager(VectorApp.getInstance().getApplicationContext());
if (wm != null) {
wm.onLiveEvent(instance.getDefaultSession(), event);
}
}
}
......@@ -306,6 +310,23 @@ public class Matrix {
return sessions;
}
@Nullable
public WidgetManagerProvider getWidgetManagerProvider(MXSession session) {
if (session == null) {
return null;
}
return mWidgetManagerProviders.get(session.getMyUserId());
}
@Nullable
public static WidgetsManager getWidgetManager(Context activity) {
if (Matrix.getInstance(activity) == null) return null;
MXSession session = Matrix.getInstance(activity).getDefaultSession();
if (session == null) return null;
WidgetManagerProvider widgetManagerProvider = Matrix.getInstance(activity).getWidgetManagerProvider(session);
if (widgetManagerProvider == null) return null;
return widgetManagerProvider.getWidgetManager(activity);
}
/**
* Retrieve the default session if one exists.
* <p>
......@@ -648,7 +669,9 @@ public class Matrix {
* @return The session.
*/
public MXSession createSession(HomeServerConnectionConfig hsConfig) {
return createSession(mAppContext, hsConfig);
MXSession session = createSession(mAppContext, hsConfig);
mWidgetManagerProviders.put(session.getMyUserId(), new WidgetManagerProvider(session));
return session;
}
/**
......
......@@ -28,7 +28,6 @@ import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
......@@ -197,11 +196,6 @@ public class VectorApp extends MultiDexApplication {
Log.d(LOG_TAG, "onCreate");
super.onCreate();
PreferencesManager.setIntegrationManagerUrls(this,
getString(R.string.integrations_ui_url),
getString(R.string.integrations_rest_url),
getString(R.string.integrations_jitsi_widget_url));
mLifeCycleListener = new VectorLifeCycleObserver();
ProcessLifecycleOwner.get().getLifecycle().addObserver(mLifeCycleListener);
......
......@@ -31,11 +31,11 @@ import im.vector.Matrix
import im.vector.R
import im.vector.activity.util.INTEGRATION_MANAGER_ACTIVITY_REQUEST_CODE
import im.vector.activity.util.TERMS_REQUEST_CODE
import im.vector.fragments.roomwidgets.WebviewPermissionUtils
import im.vector.types.JsonDict
import im.vector.types.WidgetEventData
import im.vector.util.AssetReader
import im.vector.util.toJsonMap
import im.vector.widgets.WidgetManagerProvider
import im.vector.widgets.WidgetsManager
import org.jetbrains.anko.toast
import org.matrix.androidsdk.MXSession
......@@ -88,7 +88,8 @@ abstract class AbstractWidgetActivity : VectorAppCompatActivity() {
@CallSuper
override fun initUiAndData() {
mSession = Matrix.getInstance(this).getSession(intent.getStringExtra(EXTRA_MATRIX_ID))
val matrix = Matrix.getInstance(this)
mSession = matrix.getSession(intent.getStringExtra(EXTRA_MATRIX_ID))
if (null == mSession || !mSession!!.isAlive) {
Log.e(LOG_TAG, "## onCreate() : invalid session")
......@@ -100,7 +101,7 @@ abstract class AbstractWidgetActivity : VectorAppCompatActivity() {
mRoom = mSession!!.dataHandler.getRoom(intent.getStringExtra(EXTRA_ROOM_ID))
widgetManager = WidgetManagerProvider.getWidgetManager(this) ?: run {
widgetManager = matrix.getWidgetManagerProvider(mSession)?.getWidgetManager(this) ?: run {
finish()
return
}
......@@ -149,7 +150,7 @@ abstract class AbstractWidgetActivity : VectorAppCompatActivity() {
}
private fun presentTermsForServices(token: String) {
val wm = WidgetManagerProvider.getWidgetManager(this)
val wm = Matrix.getInstance(this).getWidgetManagerProvider(mSession)?.getWidgetManager(this)//WidgetManagerProvider.getWidgetManagerProvider(this)
if (wm == null) { // should not happen
finish()
return
......@@ -194,7 +195,7 @@ abstract class AbstractWidgetActivity : VectorAppCompatActivity() {
// Permission requests
it.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest) {
runOnUiThread { request.grant(request.resources) }
WebviewPermissionUtils.promptForPermissions(R.string.room_widget_resource_permission_title, request, this@AbstractWidgetActivity)
}
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
......
......@@ -316,39 +316,34 @@ public class CommonActivityUtils {
private static boolean isRecoveringFromInvalidatedToken = false;
public static void recoverInvalidatedToken() {
Log.e(LOG_TAG, "## recoverInvalidatedToken: Start Recover ");
if (isRecoveringFromInvalidatedToken) {
//ignore, we are doing it
Log.e(LOG_TAG, "## recoverInvalidatedToken: ignore, we are doing it");
return;
}
isRecoveringFromInvalidatedToken = true;
Context context = VectorApp.getCurrentActivity() != null ? VectorApp.getCurrentActivity() : VectorApp.getInstance();
try {
// Clear the credentials
Matrix.getInstance(context).getLoginStorage().clear() ;
VectorApp.getInstance().getNotificationDrawerManager().clearAllEvents();
EventStreamServiceX.Companion.onLogout(context);
// stopEventStream(context);
EventStreamServiceX.Companion.onApplicationStopped(context);
BadgeProxy.INSTANCE.updateBadgeCount(context, 0);
MXSession session = Matrix.getInstance(context).getDefaultSession();
// Publish to the server that we're now offline
MyPresenceManager.getInstance(context, session).advertiseOffline();
MyPresenceManager.remove(session);
// clear the preferences
PreferencesManager.clearPreferences(context);
// reset the FCM
Matrix.getInstance(context).getPushManager().resetFCMRegistration();
// clear the preferences
Matrix.getInstance(context).getPushManager().clearPreferences();
// Clear the credentials
Matrix.getInstance(context).getLoginStorage().clear();
// clear the tmp store list
Matrix.getInstance(context).clearTmpStoresList();
......@@ -358,20 +353,11 @@ public class CommonActivityUtils {
MXMediaCache.clearThumbnailsCache(context);
Matrix.getInstance(context).clearSessions(context, true, new SimpleApiCallback<Void>() {
@Override
public void onSuccess(Void info) {
}
});
session.clear(context);
} catch (Exception e) {
Log.e(LOG_TAG, "## recoverInvalidatedToken: Error while cleaning: ", e);
} finally {
// go to login page
CommonActivityUtils.restartApp(context, true);
isRecoveringFromInvalidatedToken = false;
CommonActivityUtils.restartApp(context, true);
}
}
......@@ -436,15 +422,12 @@ public class CommonActivityUtils {
if (goToLoginPage) {
Activity activeActivity = VectorApp.getCurrentActivity();
final Context activeContext = (null == activeActivity) ? VectorApp.getInstance().getApplicationContext() : activeActivity;
// go to login page
Intent intent = new Intent(activeActivity, LoginActivity.class);
Intent intent = new Intent(activeContext, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
if (null != activeActivity) {
activeActivity.startActivity(intent);
} else {
context.startActivity(intent);
}
activeContext.startActivity(intent);
}
}
});
......
/*
* Copyright 2019 New Vector 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.activity
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import im.vector.R
import im.vector.extensions.showPassword
object DialogUtils {
fun promptPassword(context: Context, errorText: String? = null, defaultPwd: String? = null,
done: (String) -> Unit,
cancel: (() -> Unit)? = null) {
val view: ViewGroup = LayoutInflater.from(context).inflate(R.layout.dialog_confirm_password, null) as ViewGroup
val showPassword: ImageView = view.findViewById(R.id.confirm_password_show_passwords)
val passwordTil: TextInputLayout = view.findViewById(R.id.confirm_password_til)
val passwordText: TextInputEditText = view.findViewById(R.id.password_label)
passwordText.setText(defaultPwd)
var passwordShown = false
showPassword.setOnClickListener {
passwordShown = !passwordShown
passwordText.showPassword(passwordShown)
showPassword.setImageResource(if (passwordShown) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
}
passwordTil.error = errorText
AlertDialog.Builder(context)
.setView(view)
.setPositiveButton(R.string._continue) { tv, _ ->
done(passwordText.text.toString())
}
.apply {
if (cancel != null) {
setNegativeButton(R.string.cancel) { _, _ ->
cancel()
}
}
}
.show()
}
}
\ No newline at end of file
/*
* Copyright 2019 New Vector 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.activity
/**
* A fragment should implement this interface if it wants to intercept backPressed events.
* Any activity extending VectorAppCompatActivity will propagate back pressed event to child
* fragment that implements it.
*/
interface HandleBackParticipant {
/**
* Returns true, if the on back pressed event has been handled by this Fragment.
* Otherwise return false
*/
fun onBackPressed(): Boolean
}
\ No newline at end of file
......@@ -259,7 +259,7 @@ class IntegrationManagerActivity : AbstractWidgetActivity() {
Log.d(LOG_TAG, "Received request to get widget in room " + mRoom!!.roomId)
val widgets = widgetManager.getActiveWidgets(mSession, mRoom)
val widgets = WidgetsManager.getActiveWidgets(mSession, mRoom)
val responseData = ArrayList<JsonDict<Any>>()
for (widget in widgets) {
......
......@@ -27,15 +27,18 @@ import android.widget.FrameLayout;
import com.facebook.react.modules.core.PermissionListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jitsi.meet.sdk.JitsiMeetActivityDelegate;
import org.jitsi.meet.sdk.JitsiMeetActivityInterface;
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
import org.jitsi.meet.sdk.JitsiMeetUserInfo;
import org.jitsi.meet.sdk.JitsiMeetView;
import org.jitsi.meet.sdk.JitsiMeetViewListener;
import org.matrix.androidsdk.MXSession;
import org.matrix.androidsdk.core.Log;
import org.matrix.androidsdk.data.Room;
import java.net.URL;
import java.util.Map;
import butterknife.BindView;
......@@ -64,7 +67,7 @@ public class JitsiCallActivity extends VectorAppCompatActivity implements JitsiM
/**
* Base server URL
*/
private static final String JITSI_SERVER_URL = "https://jitsi.riot.im/";
public static final String JITSI_SERVER_URL = "https://jitsi.riot.im/";
// the jitsi view
private JitsiMeetView mJitsiView;
......@@ -109,10 +112,7 @@ public class JitsiCallActivity extends VectorAppCompatActivity implements JitsiM
@Override
@SuppressLint("NewApi")
public void initUiAndData() {
if (WidgetManagerProvider.INSTANCE.getWidgetManager(this) == null) {
finish();
return;
}
// Waiting View
setWaitingView(findViewById(R.id.jitsi_progress_layout));
......@@ -136,7 +136,6 @@ public class JitsiCallActivity extends VectorAppCompatActivity implements JitsiM
return;
}
mRoom = mSession.getDataHandler().getRoom(mWidget.getRoomId());
if (null == mRoom) {
Log.e(LOG_TAG, "## onCreate() : undefined room " + mWidget.getRoomId());
......@@ -154,8 +153,22 @@ public class JitsiCallActivity extends VectorAppCompatActivity implements JitsiM
*/
private void loadURL() {
try {
JitsiMeetUserInfo userInfo = new JitsiMeetUserInfo();
userInfo.setDisplayName(mSession.getMyUser().displayname);
try {
String avatarUrl = mSession.getMyUser().avatar_url;
if (avatarUrl != null) {
String downloadableUrl = mSession.getContentManager().getDownloadableUrl(avatarUrl, false);
if (downloadableUrl != null) {
userInfo.setAvatar(new URL(downloadableUrl));
}
}
} catch (Exception e) {
//nop
}
JitsiMeetConferenceOptions jitsiMeetConferenceOptions = new JitsiMeetConferenceOptions.Builder()
.setVideoMuted(!mIsVideoCall)
.setUserInfo(userInfo)
// Configure the title of the screen
// TODO config.putString("callDisplayName", mRoom.getRoomDisplayName(this));
.setRoom(mCallUrl)
......@@ -237,7 +250,7 @@ public class JitsiCallActivity extends VectorAppCompatActivity implements JitsiM
protected void onStop() {
super.onStop();
JitsiMeetActivityDelegate.onHostPause(this);
WidgetsManager wm = WidgetManagerProvider.INSTANCE.getWidgetManager(this);
WidgetsManager wm = getWidgetManager();
if (wm != null) {
wm.removeListener(mWidgetListener);
}
......@@ -253,12 +266,20 @@ public class JitsiCallActivity extends VectorAppCompatActivity implements JitsiM
super.onResume();
JitsiMeetActivityDelegate.onHostResume(this);
WidgetsManager wm = WidgetManagerProvider.INSTANCE.getWidgetManager(this);
WidgetsManager wm = getWidgetManager();
if (wm != null) {
wm.addListener(mWidgetListener);
}
}
@Nullable
private WidgetsManager getWidgetManager() {
if (mSession == null) return null;
WidgetManagerProvider widgetManagerProvider = Matrix.getInstance(this).getWidgetManagerProvider(mSession);
if (widgetManagerProvider == null) return null;
return widgetManagerProvider.getWidgetManager(this);
}
@Override
public void onBackPressed() {
JitsiMeetActivityDelegate.onBackPressed();
......
......@@ -267,7 +267,7 @@ public class PhoneNumberAdditionActivity extends VectorAppCompatActivity impleme
@Override
public void onSuccess(ThreePid pid) {
hideWaitingView();
Intent intent = PhoneNumberVerificationActivity.getIntent(PhoneNumberAdditionActivity.this,
Intent intent = PhoneNumberVerificationActivity.Companion.getIntent(PhoneNumberAdditionActivity.this,
mSession.getCredentials().userId, pid);
startActivityForResult(intent, REQUEST_VERIFICATION);
}
......
/*
* Copyright 2017 Vector Creations Ltd
* Copyright 2018 New Vector 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.activity;
import android.content.Context;
import android.content.Intent;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import org.matrix.androidsdk.MXSession;
import org.matrix.androidsdk.core.Log;
import org.matrix.androidsdk.core.callback.ApiCallback;
import org.matrix.androidsdk.core.model.MatrixError;
import org.matrix.androidsdk.rest.model.SuccessResult;
import org.matrix.androidsdk.rest.model.pid.ThreePid;
import im.vector.Matrix;
import im.vector.R;
public class PhoneNumberVerificationActivity extends VectorAppCompatActivity implements TextView.OnEditorActionListener, TextWatcher {
private static final String LOG_TAG = PhoneNumberVerificationActivity.class.getSimpleName();
private static final String EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID";
private static final String EXTRA_PID = "EXTRA_PID";
private TextInputEditText mPhoneNumberCode;
private TextInputLayout mPhoneNumberCodeLayout;
private MXSession mSession;
private ThreePid mThreePid;
// True when a phone number token is submitted
// Used to prevent user to submit several times in a row
private boolean mIsSubmittingToken;
/*
* *********************************************************************************************
* Static methods
* *********************************************************************************************
*/
public static Intent getIntent(final Context context, final String sessionId, final ThreePid pid) {
final Intent intent = new Intent(context, PhoneNumberVerificationActivity.class);
intent.putExtra(EXTRA_MATRIX_ID, sessionId);
intent.putExtra(EXTRA_PID, pid);
return intent;
}
/*
* *********************************************************************************************
* Activity lifecycle
* *********************************************************************************************
*/
@Override
public int getLayoutRes() {
return R.layout.activity_phone_number_verification;
}
@Override
public int getTitleRes() {
return R.string.settings_phone_number_verification;
}
@Override
public void initUiAndData() {
configureToolbar();
mPhoneNumberCode = findViewById(R.id.phone_number_code_value);
mPhoneNumberCodeLayout = findViewById(R.id.phone_number_code);
setWaitingView(findViewById(R.id.loading_view));
final Intent intent = getIntent();
mSession = Matrix.getInstance(this).getSession(intent.getStringExtra(EXTRA_MATRIX_ID));
if ((null == mSession) || !mSession.isAlive()) {
finish();
return;
}
mThreePid = intent.getParcelableExtra(EXTRA_PID);
mPhoneNumberCode.addTextChangedListener(this);
mPhoneNumberCode.setOnEditorActionListener(this);
}
@Override
protected void onResume() {
super.onResume();
mIsSubmittingToken = false;
}
@Override
public int getMenuRes() {
return R.menu.menu_phone_number_verification;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_verify_phone_number:
submitCode();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/*
* *********************************************************************************************
* Utils
* *********************************************************************************************
*/
/**
* Submit code (token) to attach phone number to account
*/
private void submitCode() {
if (!mIsSubmittingToken) {
mIsSubmittingToken = true;
if (TextUtils.isEmpty(mPhoneNumberCode.getText())) {
mPhoneNumberCodeLayout.setErrorEnabled(true);
mPhoneNumberCodeLayout.setError(getString(R.string.settings_phone_number_verification_error_empty_code));
} else {
showWaitingView();
mSession.getIdentityServerManager().submitValidationToken(mThreePid, mPhoneNumberCode.getText().toString(),
new ApiCallback<SuccessResult>() {
@Override
public void onSuccess(SuccessResult result) {
if (result.success) {
// the validation of mail ownership succeed, just resume the registration flow