Merge branch 'develop' into feature/dagger
@ -61,4 +61,6 @@ interface PushersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun livePushers(): LiveData<List<Pusher>>
|
fun livePushers(): LiveData<List<Pusher>>
|
||||||
|
|
||||||
|
fun pushers() : List<Pusher>
|
||||||
}
|
}
|
@ -191,5 +191,4 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -37,12 +37,15 @@ internal class BingRuleWatcher @Inject constructor(monarchy: Monarchy,
|
|||||||
override val query = Monarchy.Query<EventEntity> {
|
override val query = Monarchy.Query<EventEntity> {
|
||||||
|
|
||||||
EventEntity.types(it, listOf(
|
EventEntity.types(it, listOf(
|
||||||
EventType.REDACTION, EventType.MESSAGE, EventType.REDACTION, EventType.ENCRYPTED)
|
EventType.MESSAGE,
|
||||||
|
EventType.REDACTION,
|
||||||
|
EventType.ENCRYPTED)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) {
|
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) {
|
||||||
|
// TODO Use const for "global"
|
||||||
val rules = defaultPushRuleService.getPushRules("global")
|
val rules = defaultPushRuleService.getPushRules("global")
|
||||||
inserted.map { it.asDomain() }
|
inserted.map { it.asDomain() }
|
||||||
.filter { it.senderId != sessionParams.credentials.userId }
|
.filter { it.senderId != sessionParams.credentials.userId }
|
||||||
|
@ -112,4 +112,8 @@ internal class DefaultPusherService @Inject constructor(
|
|||||||
{ it.asDomain() }
|
{ it.asDomain() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun pushers(): List<Pusher> {
|
||||||
|
return monarchy.fetchAllCopiedSync { PusherEntity.where(it, sessionParam.credentials.userId) }.map { it.asDomain() }
|
||||||
|
}
|
||||||
}
|
}
|
@ -238,8 +238,11 @@ dependencies {
|
|||||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
|
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
|
||||||
|
|
||||||
// gplay flavor only
|
// gplay flavor only
|
||||||
gplayImplementation 'com.google.firebase:firebase-core:16.0.8'
|
gplayImplementation('com.google.firebase:firebase-messaging:19.0.1') {
|
||||||
gplayImplementation 'com.google.firebase:firebase-messaging:17.5.0'
|
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||||
|
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||||
|
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||||
|
}
|
||||||
|
|
||||||
// TESTS
|
// TESTS
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<application>
|
<application>
|
||||||
|
|
||||||
<receiver android:name=".receiver.OnApplicationUpgradeOrRebootReceiver">
|
<receiver android:name="im.vector.riotredesign.fdroid.receiver.OnApplicationUpgradeOrRebootReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
@ -16,10 +16,14 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".core.services.AlarmSyncBroadcastReceiver"
|
android:name="im.vector.riotredesign.fdroid.receiver.AlarmSyncBroadcastReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".fdroid.receiver.service.VectorSyncService"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.push.fcm.troubleshoot
|
package im.vector.riotredesign.fdroid.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.push.fcm.troubleshoot
|
package im.vector.riotredesign.fdroid.features.settings.troubleshoot
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.push.fcm.troubleshoot
|
package im.vector.riotredesign.fdroid.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code exclusively used by the FDroid build and not referenced on the main source code
|
||||||
|
*/
|
||||||
|
package im.vector.riotredesign.fdroid
|
@ -1,4 +1,20 @@
|
|||||||
package im.vector.riotredesign.core.services
|
/*
|
||||||
|
* 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.riotredesign.fdroid.receiver
|
||||||
|
|
||||||
import android.app.AlarmManager
|
import android.app.AlarmManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
@ -9,6 +25,7 @@ import android.os.Build
|
|||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import im.vector.matrix.android.internal.session.sync.job.SyncService
|
import im.vector.matrix.android.internal.session.sync.job.SyncService
|
||||||
|
import im.vector.riotredesign.fdroid.service.VectorSyncService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||||
@ -48,15 +65,14 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val REQUEST_CODE = 0
|
private const val REQUEST_CODE = 0
|
||||||
|
|
||||||
fun scheduleAlarm(context: Context, userId: String, delay: Long) {
|
fun scheduleAlarm(context: Context, userId: String, delay: Long) {
|
||||||
//Reschedule
|
//Reschedule
|
||||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
|
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
|
||||||
putExtra(SyncService.EXTRA_USER_ID, userId)
|
putExtra(SyncService.EXTRA_USER_ID, userId)
|
||||||
}
|
}
|
||||||
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
|
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
val firstMillis = System.currentTimeMillis() + delay
|
val firstMillis = System.currentTimeMillis() + delay
|
||||||
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@ -68,8 +84,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
fun cancelAlarm(context: Context) {
|
fun cancelAlarm(context: Context) {
|
||||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
|
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
|
||||||
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
|
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
alarmMgr.cancel(pIntent)
|
alarmMgr.cancel(pIntent)
|
||||||
}
|
}
|
@ -15,21 +15,21 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.riotredesign.receiver
|
package im.vector.riotredesign.fdroid.receiver
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import im.vector.riotredesign.core.di.HasVectorInjector
|
import im.vector.riotredesign.core.di.HasVectorInjector
|
||||||
import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
|
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
Timber.v("## onReceive() ${intent.action}")
|
Timber.v("## onReceive() ${intent.action}")
|
||||||
if (context is HasVectorInjector) {
|
val appContext = context.applicationContext
|
||||||
val activeSession = context.injector().activeSessionHolder().getSafeActiveSession()
|
if (appContext is HasVectorInjector) {
|
||||||
|
val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
|
||||||
if (activeSession != null) {
|
if (activeSession != null) {
|
||||||
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.myUserId, 10)
|
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.myUserId, 10)
|
||||||
}
|
}
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.core.services
|
package im.vector.riotredesign.fdroid.service
|
||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -57,7 +57,7 @@ class VectorSyncService : SyncService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the service is bounded and the service was previously started we can remove foreground notif
|
* If the service is bounded and the service was previously started we can remove foreground notification
|
||||||
*/
|
*/
|
||||||
override fun onBind(intent: Intent?): IBinder {
|
override fun onBind(intent: Intent?): IBinder {
|
||||||
Timber.v("VectorSyncService - onBind ")
|
Timber.v("VectorSyncService - onBind ")
|
@ -18,9 +18,15 @@ package im.vector.riotredesign.push.fcm
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotredesign.core.pushers.PushersManager
|
import im.vector.riotredesign.core.pushers.PushersManager
|
||||||
|
import im.vector.riotredesign.fdroid.receiver.AlarmSyncBroadcastReceiver
|
||||||
|
import im.vector.riotredesign.features.settings.PreferencesManager
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class has an alter ego in the gplay variant.
|
||||||
|
*/
|
||||||
object FcmHelper {
|
object FcmHelper {
|
||||||
|
|
||||||
fun isPushSupported(): Boolean = false
|
fun isPushSupported(): Boolean = false
|
||||||
@ -52,4 +58,17 @@ object FcmHelper {
|
|||||||
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager) {
|
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager) {
|
||||||
// No op
|
// No op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onEnterForeground(context: Context) {
|
||||||
|
AlarmSyncBroadcastReceiver.cancelAlarm(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onEnterBackground(context: Context, activeSessionHolder: ActiveSessionHolder) {
|
||||||
|
//We need to use alarm in this mode
|
||||||
|
if (PreferencesManager.areNotificationEnabledForDevice(context) && activeSessionHolder.hasActiveSession()) {
|
||||||
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
|
AlarmSyncBroadcastReceiver.scheduleAlarm(context, currentSession.myUserId, 4_000L)
|
||||||
|
Timber.i("Alarm scheduled to restart service")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@ package im.vector.riotredesign.push.fcm
|
|||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.riotredesign.fdroid.features.settings.troubleshoot.TestAutoStartBoot
|
||||||
|
import im.vector.riotredesign.fdroid.features.settings.troubleshoot.TestBackgroundRestrictions
|
||||||
import im.vector.riotredesign.features.settings.troubleshoot.*
|
import im.vector.riotredesign.features.settings.troubleshoot.*
|
||||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestAutoStartBoot
|
|
||||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestBackgroundRestrictions
|
|
||||||
|
|
||||||
class NotificationTroubleshootTestManagerFactory {
|
class NotificationTroubleshootTestManagerFactory {
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
android:name="firebase_analytics_collection_deactivated"
|
android:name="firebase_analytics_collection_deactivated"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
|
||||||
<service android:name=".push.fcm.VectorFirebaseMessagingService">
|
<service android:name=".gplay.push.fcm.VectorFirebaseMessagingService">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.firebase.iid.FirebaseInstanceId
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.core.utils.startAddGoogleAccountIntent
|
||||||
|
import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||||
|
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
||||||
|
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that app can successfully retrieve a token via firebase
|
||||||
|
*/
|
||||||
|
class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity,
|
||||||
|
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
|
||||||
|
|
||||||
|
override fun perform() {
|
||||||
|
status = TestStatus.RUNNING
|
||||||
|
try {
|
||||||
|
FirebaseInstanceId.getInstance().instanceId
|
||||||
|
.addOnCompleteListener(context) { task ->
|
||||||
|
if (!task.isSuccessful) {
|
||||||
|
val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage
|
||||||
|
//Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated)
|
||||||
|
if ("SERVICE_NOT_AVAILABLE".equals(errorMsg)) {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
|
||||||
|
} else if ("TOO_MANY_REGISTRATIONS".equals(errorMsg)) {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
|
||||||
|
} else if ("ACCOUNT_MISSING".equals(errorMsg)) {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
|
||||||
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
|
||||||
|
override fun doFix() {
|
||||||
|
startAddGoogleAccountIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
|
||||||
|
}
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
} else {
|
||||||
|
task.result?.token?.let {
|
||||||
|
val tok = it.substring(0, Math.min(8, it.length)) + "********************"
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, tok)
|
||||||
|
Timber.e("Retrieved FCM token success [$it].")
|
||||||
|
FcmHelper.storeFcmToken(context, tok)
|
||||||
|
}
|
||||||
|
status = TestStatus.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, e.localizedMessage)
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,40 +13,41 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.push.fcm.troubleshoot
|
package im.vector.riotredesign.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.gms.common.ConnectionResult
|
import com.google.android.gms.common.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that the play services APK is available an up-to-date. If needed provide quick fix to install it.
|
* Check that the play services APK is available an up-to-date. If needed provide quick fix to install it.
|
||||||
*/
|
*/
|
||||||
class TestPlayServices(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
|
class TestPlayServices @Inject constructor(private val context: AppCompatActivity,
|
||||||
|
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(fragment.context)
|
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||||
if (resultCode == ConnectionResult.SUCCESS) {
|
if (resultCode == ConnectionResult.SUCCESS) {
|
||||||
quickFix = null
|
quickFix = null
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_play_services_success)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_play_services_success)
|
||||||
status = TestStatus.SUCCESS
|
status = TestStatus.SUCCESS
|
||||||
} else {
|
} else {
|
||||||
if (apiAvailability.isUserResolvableError(resultCode)) {
|
if (apiAvailability.isUserResolvableError(resultCode)) {
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_play_services_quickfix) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_play_services_quickfix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
fragment.activity?.let {
|
apiAvailability.getErrorDialog(context, resultCode, 9000 /*hey does the magic number*/).show()
|
||||||
apiAvailability.getErrorDialog(it, resultCode, 9000 /*hey does the magic number*/).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.e("Play Services apk error $resultCode -> ${apiAvailability.getErrorString(resultCode)}.")
|
Timber.e("Play Services apk error $resultCode -> ${apiAvailability.getErrorString(resultCode)}.")
|
||||||
}
|
}
|
||||||
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_play_services_failed, apiAvailability.getErrorString(resultCode))
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_play_services_failed, apiAvailability.getErrorString(resultCode))
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.work.WorkInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import im.vector.matrix.android.api.session.pushers.PusherState
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.riotredesign.core.pushers.PushersManager
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
||||||
|
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force registration of the token to HomeServer
|
||||||
|
*/
|
||||||
|
class TestTokenRegistration @Inject constructor(private val context: AppCompatActivity,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val pushersManager: PushersManager,
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder) : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
|
||||||
|
|
||||||
|
override fun perform() {
|
||||||
|
//Check if we have a registered pusher for this token
|
||||||
|
val fcmToken = FcmHelper.getFcmToken(context) ?: run {
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession() ?: run {
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val pusher = session.pushers().filter {
|
||||||
|
it.pushKey == fcmToken && it.state == PusherState.REGISTERED
|
||||||
|
}
|
||||||
|
if (pusher.isEmpty()) {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_token_registration_failed, null)
|
||||||
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) {
|
||||||
|
override fun doFix() {
|
||||||
|
val workId = pushersManager.registerPusherWithFcmKey(fcmToken)
|
||||||
|
WorkManager.getInstance().getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
||||||
|
if (workInfo != null) {
|
||||||
|
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||||
|
manager?.retry()
|
||||||
|
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
||||||
|
manager?.retry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
|
||||||
|
} else {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_token_registration_success)
|
||||||
|
status = TestStatus.SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code exclusively used by the GPlay build and not referenced on the main source code
|
||||||
|
*/
|
||||||
|
package im.vector.riotredesign.gplay
|
||||||
|
|
@ -17,7 +17,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.riotredesign.push.fcm
|
package im.vector.riotredesign.gplay.push.fcm
|
||||||
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@ -26,11 +26,12 @@ import androidx.lifecycle.Lifecycle
|
|||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
import com.google.firebase.messaging.RemoteMessage
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
import im.vector.matrix.android.api.Matrix
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.riotredesign.BuildConfig
|
import im.vector.riotredesign.BuildConfig
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.riotredesign.core.extensions.vectorComponent
|
||||||
import im.vector.riotredesign.core.preference.BingRule
|
import im.vector.riotredesign.core.preference.BingRule
|
||||||
import im.vector.riotredesign.core.pushers.PushersManager
|
import im.vector.riotredesign.core.pushers.PushersManager
|
||||||
import im.vector.riotredesign.features.badge.BadgeProxy
|
import im.vector.riotredesign.features.badge.BadgeProxy
|
||||||
@ -38,35 +39,31 @@ import im.vector.riotredesign.features.notifications.NotifiableEventResolver
|
|||||||
import im.vector.riotredesign.features.notifications.NotifiableMessageEvent
|
import im.vector.riotredesign.features.notifications.NotifiableMessageEvent
|
||||||
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
||||||
import im.vector.riotredesign.features.notifications.SimpleNotifiableEvent
|
import im.vector.riotredesign.features.notifications.SimpleNotifiableEvent
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
import im.vector.riotredesign.features.settings.PreferencesManager
|
import im.vector.riotredesign.features.settings.PreferencesManager
|
||||||
import org.koin.android.ext.android.inject
|
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||||
>>>>>>> develop
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class extending FirebaseMessagingService.
|
* Class extending FirebaseMessagingService.
|
||||||
*/
|
*/
|
||||||
class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
||||||
|
@Inject lateinit var notifiableEventResolver: NotifiableEventResolver
|
||||||
|
@Inject lateinit var pusherManager: PushersManager
|
||||||
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
|
|
||||||
private val notifiableEventResolver by lazy {
|
|
||||||
NotifiableEventResolver(this)
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
private val notificationDrawerManager by inject<NotificationDrawerManager>()
|
|
||||||
private val pusherManager by inject<PushersManager>()
|
|
||||||
>>>>>>> develop
|
|
||||||
|
|
||||||
private val notifiableEventResolver by inject<NotifiableEventResolver>()
|
|
||||||
// UI handler
|
// UI handler
|
||||||
private val mUIHandler by lazy {
|
private val mUIHandler by lazy {
|
||||||
Handler(Looper.getMainLooper())
|
Handler(Looper.getMainLooper())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
vectorComponent().inject(this)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when message is received.
|
* Called when message is received.
|
||||||
*
|
*
|
||||||
@ -103,7 +100,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
* you retrieve the token.
|
* you retrieve the token.
|
||||||
*/
|
*/
|
||||||
override fun onNewToken(refreshedToken: String?) {
|
override fun onNewToken(refreshedToken: String?) {
|
||||||
if (Matrix.getInstance().currentSession == null) return
|
|
||||||
Timber.i("onNewToken: FCM Token has been updated")
|
Timber.i("onNewToken: FCM Token has been updated")
|
||||||
FcmHelper.storeFcmToken(this, refreshedToken)
|
FcmHelper.storeFcmToken(this, refreshedToken)
|
||||||
if (refreshedToken == null) {
|
if (refreshedToken == null) {
|
||||||
@ -149,7 +145,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
val unreadCount = data.get("unread")?.let { Integer.parseInt(it) } ?: 0
|
val unreadCount = data.get("unread")?.let { Integer.parseInt(it) } ?: 0
|
||||||
BadgeProxy.updateBadgeCount(applicationContext, unreadCount)
|
BadgeProxy.updateBadgeCount(applicationContext, unreadCount)
|
||||||
|
|
||||||
val session = safeGetCurrentSession()
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
|
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
Timber.w("## Can't sync from push, no current session")
|
Timber.w("## Can't sync from push, no current session")
|
||||||
@ -167,21 +163,12 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun safeGetCurrentSession(): Session? {
|
|
||||||
try {
|
|
||||||
return Matrix.getInstance().currentSession
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Timber.e(e, "## Failed to get current session")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the event was not yet received
|
// check if the event was not yet received
|
||||||
// a previous catchup might have already retrieved the notified event
|
// a previous catchup might have already retrieved the notified event
|
||||||
private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean {
|
private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean {
|
||||||
if (null != eventId && null != roomId) {
|
if (null != eventId && null != roomId) {
|
||||||
try {
|
try {
|
||||||
val session = safeGetCurrentSession() ?: return false
|
val session = activeSessionHolder.getSafeActiveSession() ?: return false
|
||||||
val room = session.getRoom(roomId) ?: return false
|
val room = session.getRoom(roomId) ?: return false
|
||||||
return room.getTimeLineEvent(eventId) != null
|
return room.getTimeLineEvent(eventId) != null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
@ -25,6 +25,7 @@ import com.google.android.gms.common.ConnectionResult
|
|||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import com.google.firebase.iid.FirebaseInstanceId
|
import com.google.firebase.iid.FirebaseInstanceId
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotredesign.core.pushers.PushersManager
|
import im.vector.riotredesign.core.pushers.PushersManager
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -99,4 +100,12 @@ object FcmHelper {
|
|||||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity)
|
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity)
|
||||||
return resultCode == ConnectionResult.SUCCESS
|
return resultCode == ConnectionResult.SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onEnterForeground(context: Context) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onEnterBackground(context: Context, activeSessionHolder: ActiveSessionHolder) {
|
||||||
|
// TODO FCM fallback
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,30 +16,34 @@
|
|||||||
package im.vector.riotredesign.push.fcm
|
package im.vector.riotredesign.push.fcm
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||||
import im.vector.riotredesign.features.settings.troubleshoot.*
|
import im.vector.riotredesign.features.settings.troubleshoot.TestAccountSettings
|
||||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestFirebaseToken
|
import im.vector.riotredesign.features.settings.troubleshoot.TestBingRulesSettings
|
||||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestPlayServices
|
import im.vector.riotredesign.features.settings.troubleshoot.TestDeviceSettings
|
||||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestTokenRegistration
|
import im.vector.riotredesign.features.settings.troubleshoot.TestSystemSettings
|
||||||
|
import im.vector.riotredesign.gplay.features.settings.troubleshoot.TestFirebaseToken
|
||||||
|
import im.vector.riotredesign.gplay.features.settings.troubleshoot.TestPlayServices
|
||||||
|
import im.vector.riotredesign.gplay.features.settings.troubleshoot.TestTokenRegistration
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NotificationTroubleshootTestManagerFactory {
|
class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
|
||||||
|
private val testAccountSettings: TestAccountSettings,
|
||||||
|
private val testDeviceSettings: TestDeviceSettings,
|
||||||
|
private val testBingRulesSettings: TestBingRulesSettings,
|
||||||
|
private val testPlayServices: TestPlayServices,
|
||||||
|
private val testFirebaseToken: TestFirebaseToken,
|
||||||
|
private val testTokenRegistration: TestTokenRegistration) {
|
||||||
|
|
||||||
companion object {
|
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
|
||||||
fun createTestManager(fragment: Fragment, session: Session?): NotificationTroubleshootTestManager {
|
|
||||||
val mgr = NotificationTroubleshootTestManager(fragment)
|
val mgr = NotificationTroubleshootTestManager(fragment)
|
||||||
mgr.addTest(TestSystemSettings(fragment))
|
mgr.addTest(testSystemSettings)
|
||||||
if (session != null) {
|
mgr.addTest(testAccountSettings)
|
||||||
mgr.addTest(TestAccountSettings(fragment, session))
|
mgr.addTest(testDeviceSettings)
|
||||||
}
|
mgr.addTest(testBingRulesSettings)
|
||||||
mgr.addTest(TestDeviceSettings(fragment))
|
mgr.addTest(testPlayServices)
|
||||||
if (session != null) {
|
mgr.addTest(testFirebaseToken)
|
||||||
mgr.addTest(TestBingRulesSettings(fragment, session))
|
mgr.addTest(testTokenRegistration)
|
||||||
}
|
|
||||||
mgr.addTest(TestPlayServices(fragment))
|
|
||||||
mgr.addTest(TestFirebaseToken(fragment))
|
|
||||||
mgr.addTest(TestTokenRegistration(fragment))
|
|
||||||
return mgr
|
return mgr
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.riotredesign.push.fcm.troubleshoot
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.google.firebase.iid.FirebaseInstanceId
|
|
||||||
import im.vector.riotredesign.R
|
|
||||||
import im.vector.riotredesign.core.utils.startAddGoogleAccountIntent
|
|
||||||
import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
|
||||||
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test that app can successfully retrieve a token via firebase
|
|
||||||
*/
|
|
||||||
class TestFirebaseToken(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
|
|
||||||
|
|
||||||
override fun perform() {
|
|
||||||
status = TestStatus.RUNNING
|
|
||||||
val activity = fragment.activity
|
|
||||||
if (activity != null) {
|
|
||||||
try {
|
|
||||||
FirebaseInstanceId.getInstance().instanceId
|
|
||||||
.addOnCompleteListener(activity) { task ->
|
|
||||||
if (!task.isSuccessful) {
|
|
||||||
val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage
|
|
||||||
//Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated)
|
|
||||||
if ("SERVICE_NOT_AVAILABLE".equals(errorMsg)) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
|
|
||||||
} else if ("TOO_MANY_REGISTRATIONS".equals(errorMsg)) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
|
|
||||||
} else if ("ACCOUNT_MISSING".equals(errorMsg)) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
|
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
|
|
||||||
override fun doFix() {
|
|
||||||
startAddGoogleAccountIntent(fragment, NotificationTroubleshootTestManager.REQ_CODE_FIX)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
|
|
||||||
}
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
} else {
|
|
||||||
task.result?.token?.let {
|
|
||||||
val tok = it.substring(0, Math.min(8, it.length)) + "********************"
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_fcm_success, tok)
|
|
||||||
Timber.e("Retrieved FCM token success [$it].")
|
|
||||||
}
|
|
||||||
status = TestStatus.SUCCESS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_fcm_failed, e.localizedMessage)
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.riotredesign.push.fcm.troubleshoot
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.riotredesign.R
|
|
||||||
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force registration of the token to HomeServer
|
|
||||||
*/
|
|
||||||
class TestTokenRegistration(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
|
|
||||||
|
|
||||||
override fun perform() {
|
|
||||||
/*
|
|
||||||
TODO
|
|
||||||
Matrix.getInstance(VectorApp.getInstance().baseContext).pushManager.forceSessionsRegistration(object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(info: Void?) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_success)
|
|
||||||
status = TestStatus.SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNetworkError(e: Exception?) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_failed, e?.localizedMessage)
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMatrixError(e: MatrixError?) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_failed, e?.localizedMessage)
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnexpectedError(e: Exception?) {
|
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_token_registration_failed, e?.localizedMessage)
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
}
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
status = TestStatus.FAILED
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -68,15 +68,6 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".core.services.CallService"
|
android:name=".core.services.CallService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<!--<service-->
|
|
||||||
<!--android:name="im.vector.matrix.android.internal.session.sync.job.SyncService"-->
|
|
||||||
<!--android:exported="false" />-->
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".core.services.VectorSyncService"
|
|
||||||
android:exported="false">
|
|
||||||
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<!-- Receivers -->
|
<!-- Receivers -->
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ import im.vector.riotredesign.core.di.DaggerVectorComponent
|
|||||||
import im.vector.riotredesign.core.di.HasVectorInjector
|
import im.vector.riotredesign.core.di.HasVectorInjector
|
||||||
import im.vector.riotredesign.core.di.VectorComponent
|
import im.vector.riotredesign.core.di.VectorComponent
|
||||||
import im.vector.riotredesign.core.extensions.configureAndStart
|
import im.vector.riotredesign.core.extensions.configureAndStart
|
||||||
import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver
|
|
||||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||||
import im.vector.riotredesign.features.lifecycle.VectorActivityLifecycleCallbacks
|
import im.vector.riotredesign.features.lifecycle.VectorActivityLifecycleCallbacks
|
||||||
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
||||||
@ -50,7 +49,6 @@ import im.vector.riotredesign.features.notifications.NotificationUtils
|
|||||||
import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
|
import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
|
||||||
import im.vector.riotredesign.features.rageshake.VectorFileLogger
|
import im.vector.riotredesign.features.rageshake.VectorFileLogger
|
||||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||||
import im.vector.riotredesign.features.settings.PreferencesManager
|
|
||||||
import im.vector.riotredesign.features.version.getVersion
|
import im.vector.riotredesign.features.version.getVersion
|
||||||
import im.vector.riotredesign.push.fcm.FcmHelper
|
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -113,7 +111,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
fun entersForeground() {
|
fun entersForeground() {
|
||||||
AlarmSyncBroadcastReceiver.cancelAlarm(appContext)
|
FcmHelper.onEnterForeground(appContext)
|
||||||
activeSessionHolder.getSafeActiveSession()?.also {
|
activeSessionHolder.getSafeActiveSession()?.also {
|
||||||
it.stopAnyBackgroundSync()
|
it.stopAnyBackgroundSync()
|
||||||
}
|
}
|
||||||
@ -123,19 +121,8 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
fun entersBackground() {
|
fun entersBackground() {
|
||||||
Timber.i("App entered background") // call persistInfo
|
Timber.i("App entered background") // call persistInfo
|
||||||
notificationDrawerManager.persistInfo()
|
notificationDrawerManager.persistInfo()
|
||||||
if (FcmHelper.isPushSupported()) {
|
FcmHelper.onEnterBackground(appContext, activeSessionHolder)
|
||||||
//TODO FCM fallback
|
|
||||||
} else {
|
|
||||||
//TODO check if notifications are enabled for this device
|
|
||||||
//We need to use alarm in this mode
|
|
||||||
val activeSession = activeSessionHolder.getSafeActiveSession()
|
|
||||||
if (activeSession != null && PreferencesManager.areNotificationEnabledForDevice(applicationContext)) {
|
|
||||||
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext, activeSession.myUserId, 4_000L)
|
|
||||||
Timber.i("Alarm scheduled to restart service")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import dagger.BindsInstance
|
|||||||
import dagger.Component
|
import dagger.Component
|
||||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
|
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.riotredesign.core.preference.UserAvatarPreference
|
||||||
import im.vector.riotredesign.features.MainActivity
|
import im.vector.riotredesign.features.MainActivity
|
||||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyFragment
|
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyFragment
|
||||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSuccessFragment
|
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSuccessFragment
|
||||||
@ -54,6 +55,7 @@ import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerF
|
|||||||
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
|
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
|
||||||
import im.vector.riotredesign.features.settings.VectorSettingsActivity
|
import im.vector.riotredesign.features.settings.VectorSettingsActivity
|
||||||
import im.vector.riotredesign.features.settings.VectorSettingsNotificationPreferenceFragment
|
import im.vector.riotredesign.features.settings.VectorSettingsNotificationPreferenceFragment
|
||||||
|
import im.vector.riotredesign.features.settings.VectorSettingsNotificationsTroubleshootFragment
|
||||||
import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment
|
import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment
|
||||||
|
|
||||||
@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
|
@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
|
||||||
@ -122,8 +124,6 @@ interface ScreenComponent {
|
|||||||
|
|
||||||
fun inject(mainActivity: MainActivity)
|
fun inject(mainActivity: MainActivity)
|
||||||
|
|
||||||
fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)
|
|
||||||
|
|
||||||
fun inject(roomDirectoryActivity: RoomDirectoryActivity)
|
fun inject(roomDirectoryActivity: RoomDirectoryActivity)
|
||||||
|
|
||||||
fun inject(bugReportActivity: BugReportActivity)
|
fun inject(bugReportActivity: BugReportActivity)
|
||||||
@ -136,6 +136,12 @@ interface ScreenComponent {
|
|||||||
|
|
||||||
fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)
|
fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)
|
||||||
|
|
||||||
|
fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)
|
||||||
|
|
||||||
|
fun inject(userAvatarPreference: UserAvatarPreference)
|
||||||
|
|
||||||
|
fun inject(vectorSettingsNotificationsTroubleshootFragment: VectorSettingsNotificationsTroubleshootFragment)
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(vectorComponent: VectorComponent,
|
fun create(vectorComponent: VectorComponent,
|
||||||
|
@ -39,6 +39,7 @@ import im.vector.riotredesign.features.notifications.PushRuleTriggerListener
|
|||||||
import im.vector.riotredesign.features.rageshake.BugReporter
|
import im.vector.riotredesign.features.rageshake.BugReporter
|
||||||
import im.vector.riotredesign.features.rageshake.RageShake
|
import im.vector.riotredesign.features.rageshake.RageShake
|
||||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||||
|
import im.vector.riotredesign.gplay.push.fcm.VectorFirebaseMessagingService
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Component(modules = [VectorModule::class])
|
@Component(modules = [VectorModule::class])
|
||||||
@ -49,6 +50,8 @@ interface VectorComponent {
|
|||||||
|
|
||||||
fun inject(vectorApplication: VectorApplication)
|
fun inject(vectorApplication: VectorApplication)
|
||||||
|
|
||||||
|
fun inject(vectorFirebaseMessagingService: VectorFirebaseMessagingService)
|
||||||
|
|
||||||
fun matrix(): Matrix
|
fun matrix(): Matrix
|
||||||
|
|
||||||
fun currentSession(): Session
|
fun currentSession(): Session
|
||||||
@ -61,6 +64,8 @@ interface VectorComponent {
|
|||||||
|
|
||||||
fun vectorConfiguration(): VectorConfiguration
|
fun vectorConfiguration(): VectorConfiguration
|
||||||
|
|
||||||
|
fun avatarRenderer(): AvatarRenderer
|
||||||
|
|
||||||
fun activeSessionHolder(): ActiveSessionHolder
|
fun activeSessionHolder(): ActiveSessionHolder
|
||||||
|
|
||||||
fun emojiCompatFontProvider(): EmojiCompatFontProvider
|
fun emojiCompatFontProvider(): EmojiCompatFontProvider
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.core.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import im.vector.riotredesign.core.di.HasVectorInjector
|
||||||
|
import im.vector.riotredesign.core.di.VectorComponent
|
||||||
|
|
||||||
|
fun Context.vectorComponent(): VectorComponent {
|
||||||
|
val appContext = applicationContext
|
||||||
|
if (appContext is HasVectorInjector) {
|
||||||
|
return appContext.injector()
|
||||||
|
} else {
|
||||||
|
throw IllegalStateException("Your application context doesn't implement HasVectorInjector")
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,8 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.PreferenceViewHolder
|
import androidx.preference.PreferenceViewHolder
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.extensions.vectorComponent
|
||||||
|
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||||
|
|
||||||
open class UserAvatarPreference : Preference {
|
open class UserAvatarPreference : Preference {
|
||||||
|
|
||||||
@ -31,6 +33,8 @@ open class UserAvatarPreference : Preference {
|
|||||||
internal var mSession: Session? = null
|
internal var mSession: Session? = null
|
||||||
private var mLoadingProgressBar: ProgressBar? = null
|
private var mLoadingProgressBar: ProgressBar? = null
|
||||||
|
|
||||||
|
private var avatarRenderer: AvatarRenderer = context.vectorComponent().avatarRenderer()
|
||||||
|
|
||||||
constructor(context: Context) : super(context)
|
constructor(context: Context) : super(context)
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||||
@ -52,11 +56,14 @@ open class UserAvatarPreference : Preference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open fun refreshAvatar() {
|
open fun refreshAvatar() {
|
||||||
if (null != mAvatarView && null != mSession) {
|
val session = mSession ?: return
|
||||||
// TODO
|
val view = mAvatarView ?: return
|
||||||
// val myUser = session!!.myUser
|
session.getUser(session.sessionParams.credentials.userId)?.let {
|
||||||
// VectorUtils.loadUserAvatar(context, session, mAvatarView, myUser.avatarUrl, myUser.user_id, myUser.displayname)
|
avatarRenderer.render(it, view)
|
||||||
|
} ?: run {
|
||||||
|
avatarRenderer.render(null, session.sessionParams.credentials.userId, null, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSession(session: Session) {
|
fun setSession(session: Session) {
|
||||||
|
@ -1,26 +1,28 @@
|
|||||||
package im.vector.riotredesign.core.pushers
|
package im.vector.riotredesign.core.pushers
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotredesign.core.resources.AppNameProvider
|
import im.vector.riotredesign.core.resources.AppNameProvider
|
||||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val DEFAULT_PUSHER_FILE_TAG = "mobile"
|
private const val DEFAULT_PUSHER_FILE_TAG = "mobile"
|
||||||
|
|
||||||
class PushersManager @Inject constructor(
|
class PushersManager @Inject constructor(
|
||||||
private val currentSession: Session,
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
private val localeProvider: LocaleProvider,
|
private val localeProvider: LocaleProvider,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val appNameProvider: AppNameProvider
|
private val appNameProvider: AppNameProvider
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun registerPusherWithFcmKey(pushKey: String) {
|
fun registerPusherWithFcmKey(pushKey: String): UUID {
|
||||||
var profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + Math.abs(currentSession.sessionParams.credentials.userId.hashCode())
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
|
var profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + Math.abs(currentSession.myUserId.hashCode())
|
||||||
|
|
||||||
currentSession.addHttpPusher(
|
return currentSession.addHttpPusher(
|
||||||
pushKey,
|
pushKey,
|
||||||
stringProvider.getString(R.string.pusher_app_id),
|
stringProvider.getString(R.string.pusher_app_id),
|
||||||
profileTag,
|
profileTag,
|
||||||
@ -34,6 +36,7 @@ class PushersManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun unregisterPusher(pushKey: String, callback: MatrixCallback<Unit>) {
|
fun unregisterPusher(pushKey: String, callback: MatrixCallback<Unit>) {
|
||||||
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback)
|
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,12 +18,17 @@ package im.vector.riotredesign.core.utils
|
|||||||
|
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.*
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.features.notifications.supportNotificationChannels
|
import im.vector.riotredesign.features.notifications.supportNotificationChannels
|
||||||
@ -109,22 +114,22 @@ fun getDeviceLocale(context: Context): Locale {
|
|||||||
* Shows notification settings for the current app.
|
* Shows notification settings for the current app.
|
||||||
* In android O will directly opens the notification settings, in lower version it will show the App settings
|
* In android O will directly opens the notification settings, in lower version it will show the App settings
|
||||||
*/
|
*/
|
||||||
fun startNotificationSettingsIntent(fragment: Fragment, requestCode: Int) {
|
fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: Int) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, fragment.context?.packageName)
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||||
intent.putExtra("app_package", fragment.context?.packageName)
|
intent.putExtra("app_package", activity.packageName)
|
||||||
intent.putExtra("app_uid", fragment.context?.applicationInfo?.uid)
|
intent.putExtra("app_uid", activity.applicationInfo?.uid)
|
||||||
} else {
|
} else {
|
||||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||||
val uri = Uri.fromParts("package", fragment.activity?.packageName, null)
|
val uri = Uri.fromParts("package", activity.packageName, null)
|
||||||
intent.data = uri
|
intent.data = uri
|
||||||
}
|
}
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
activity.startActivityForResult(intent, requestCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,13 +145,13 @@ fun startNotificationChannelSettingsIntent(fragment: Fragment, channelID: String
|
|||||||
fragment.startActivity(intent)
|
fragment.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAddGoogleAccountIntent(fragment: Fragment, requestCode: Int) {
|
fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) {
|
||||||
try {
|
try {
|
||||||
val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
|
val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
|
||||||
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
|
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
|
||||||
fragment.startActivityForResult(intent, requestCode)
|
context.startActivityForResult(intent, requestCode)
|
||||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||||
fragment.activity?.toast(R.string.error_no_external_application_found)
|
context.toast(R.string.error_no_external_application_found)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +512,7 @@ class RoomDetailFragment :
|
|||||||
} else if (state.asyncInviter.complete) {
|
} else if (state.asyncInviter.complete) {
|
||||||
vectorBaseActivity.finish()
|
vectorBaseActivity.finish()
|
||||||
}
|
}
|
||||||
|
composerLayout.setRoomEncrypted(state.isEncrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderRoomSummary(state: RoomDetailViewState) {
|
private fun renderRoomSummary(state: RoomDetailViewState) {
|
||||||
|
@ -496,7 +496,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.rx().liveRoomSummary()
|
room.rx().liveRoomSummary()
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(asyncRoomSummary = async)
|
copy(
|
||||||
|
asyncRoomSummary = async,
|
||||||
|
isEncrypted = room.isEncrypted()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,8 @@ data class RoomDetailViewState(
|
|||||||
val asyncInviter: Async<User> = Uninitialized,
|
val asyncInviter: Async<User> = Uninitialized,
|
||||||
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
val sendMode: SendMode = SendMode.REGULAR,
|
val sendMode: SendMode = SendMode.REGULAR,
|
||||||
val selectedEvent: TimelineEvent? = null
|
val selectedEvent: TimelineEvent? = null,
|
||||||
|
val isEncrypted: Boolean = false
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
||||||
|
@ -9,7 +9,6 @@ import android.widget.ImageView
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintSet
|
import androidx.constraintlayout.widget.ConstraintSet
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.transition.AutoTransition
|
import androidx.transition.AutoTransition
|
||||||
import androidx.transition.Transition
|
import androidx.transition.Transition
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
@ -113,4 +112,13 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
|
|||||||
it.applyTo(this)
|
it.applyTo(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setRoomEncrypted(isEncrypted: Boolean) {
|
||||||
|
composerEditText.setHint(
|
||||||
|
if (isEncrypted) {
|
||||||
|
R.string.room_message_placeholder_encrypted
|
||||||
|
} else {
|
||||||
|
R.string.room_message_placeholder_not_encrypted
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
@ -89,10 +89,10 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
val event = session.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return state
|
val event = session.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return state
|
||||||
var body: CharSequence? = null
|
var body: CharSequence? = null
|
||||||
val originTs = event.root.originServerTs
|
val originTs = event.root.originServerTs
|
||||||
when (event.root.type) {
|
when (event.root.getClearType()) {
|
||||||
EventType.MESSAGE -> {
|
EventType.MESSAGE -> {
|
||||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||||
?: event.root.content.toModel()
|
?: event.root.getClearContent().toModel()
|
||||||
body = messageContent?.body
|
body = messageContent?.body
|
||||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||||
body = eventHtmlRenderer.render(messageContent.formattedBody
|
body = eventHtmlRenderer.render(messageContent.formattedBody
|
||||||
|
@ -64,12 +64,9 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
|
|||||||
// TODO This is not correct format for error, change it
|
// TODO This is not correct format for error, change it
|
||||||
|
|
||||||
val informationData = messageInformationDataFactory.create(event, nextEvent)
|
val informationData = messageInformationDataFactory.create(event, nextEvent)
|
||||||
return NoticeItem_()
|
|
||||||
.avatarRenderer(avatarRenderer)
|
|
||||||
.noticeText(spannableStr)
|
|
||||||
|
|
||||||
return MessageTextItem_()
|
return MessageTextItem_()
|
||||||
.message(spannableStr)
|
.message(spannableStr)
|
||||||
|
.avatarRenderer(avatarRenderer)
|
||||||
.informationData(informationData)
|
.informationData(informationData)
|
||||||
.highlighted(highlight)
|
.highlighted(highlight)
|
||||||
.avatarCallback(callback)
|
.avatarCallback(callback)
|
||||||
|
@ -25,7 +25,7 @@ import im.vector.matrix.android.api.session.Session
|
|||||||
import im.vector.matrix.android.api.session.room.Room
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotredesign.core.di.HasVectorInjector
|
import im.vector.riotredesign.core.extensions.vectorComponent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -42,10 +42,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
|||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
if (intent == null || context == null) return
|
if (intent == null || context == null) return
|
||||||
Timber.v("NotificationBroadcastReceiver received : $intent")
|
Timber.v("NotificationBroadcastReceiver received : $intent")
|
||||||
val appContext = context.applicationContext
|
context.vectorComponent().inject(this)
|
||||||
if (appContext is HasVectorInjector) {
|
|
||||||
appContext.injector().inject(this)
|
|
||||||
}
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
NotificationUtils.SMART_REPLY_ACTION ->
|
NotificationUtils.SMART_REPLY_ACTION ->
|
||||||
handleSmartReply(intent, context)
|
handleSmartReply(intent, context)
|
||||||
|
@ -376,7 +376,7 @@ object NotificationUtils {
|
|||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
// Build the pending intent for when the notification is clicked
|
// Build the pending intent for when the notification is clicked
|
||||||
val openRoomIntent = buildOpenRoomIntent(context, roomInfo.roomId)
|
val openRoomIntent = buildOpenRoomIntent(context, roomInfo.roomId)
|
||||||
val smallIcon = if (roomInfo.shouldBing) R.drawable.icon_notif_important else R.drawable.logo_transparent
|
val smallIcon = R.drawable.ic_status_bar
|
||||||
|
|
||||||
val channelID = if (roomInfo.shouldBing) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
val channelID = if (roomInfo.shouldBing) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
||||||
return NotificationCompat.Builder(context, channelID)
|
return NotificationCompat.Builder(context, channelID)
|
||||||
@ -479,7 +479,7 @@ object NotificationUtils {
|
|||||||
fun buildSimpleEventNotification(context: Context, simpleNotifiableEvent: NotifiableEvent, largeIcon: Bitmap?, matrixId: String): Notification? {
|
fun buildSimpleEventNotification(context: Context, simpleNotifiableEvent: NotifiableEvent, largeIcon: Bitmap?, matrixId: String): Notification? {
|
||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
// Build the pending intent for when the notification is clicked
|
// Build the pending intent for when the notification is clicked
|
||||||
val smallIcon = if (simpleNotifiableEvent.noisy) R.drawable.icon_notif_important else R.drawable.logo_transparent
|
val smallIcon = R.drawable.ic_status_bar
|
||||||
|
|
||||||
val channelID = if (simpleNotifiableEvent.noisy) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
val channelID = if (simpleNotifiableEvent.noisy) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
|
||||||
|
|
||||||
@ -611,7 +611,7 @@ object NotificationUtils {
|
|||||||
noisy: Boolean,
|
noisy: Boolean,
|
||||||
lastMessageTimestamp: Long): Notification? {
|
lastMessageTimestamp: Long): Notification? {
|
||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
val smallIcon = if (noisy) R.drawable.icon_notif_important else R.drawable.logo_transparent
|
val smallIcon = R.drawable.ic_status_bar
|
||||||
|
|
||||||
return NotificationCompat.Builder(context, if (noisy) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID)
|
return NotificationCompat.Builder(context, if (noisy) NOISY_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID)
|
||||||
// used in compat < N, after summary is built based on child notifications
|
// used in compat < N, after summary is built based on child notifications
|
||||||
|
@ -28,7 +28,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class PushRuleTriggerListener @Inject constructor(
|
class PushRuleTriggerListener @Inject constructor(
|
||||||
private val resolver: NotifiableEventResolver,
|
private val resolver: NotifiableEventResolver,
|
||||||
private val drawerManager: NotificationDrawerManager
|
private val notificationDrawerManager: NotificationDrawerManager
|
||||||
) : PushRuleService.PushRuleListener {
|
) : PushRuleService.PushRuleListener {
|
||||||
|
|
||||||
|
|
||||||
@ -42,14 +42,14 @@ class PushRuleTriggerListener @Inject constructor(
|
|||||||
}
|
}
|
||||||
val notificationAction = NotificationAction.extractFrom(actions)
|
val notificationAction = NotificationAction.extractFrom(actions)
|
||||||
if (notificationAction.shouldNotify) {
|
if (notificationAction.shouldNotify) {
|
||||||
val resolveEvent = resolver.resolveEvent(event, session!!)
|
val notifiableEvent = resolver.resolveEvent(event, session!!)
|
||||||
if (resolveEvent == null) {
|
if (notifiableEvent == null) {
|
||||||
Timber.v("## Failed to resolve event")
|
Timber.v("## Failed to resolve event")
|
||||||
//TODO
|
//TODO
|
||||||
} else {
|
} else {
|
||||||
resolveEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
||||||
Timber.v("New event to notify $resolveEvent tweaks:$notificationAction")
|
Timber.v("New event to notify $notifiableEvent tweaks:$notificationAction")
|
||||||
drawerManager.onNotifiableEventReceived(resolveEvent)
|
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.v("Matched push rule is set to not notify")
|
Timber.v("Matched push rule is set to not notify")
|
||||||
@ -57,7 +57,7 @@ class PushRuleTriggerListener @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun batchFinish() {
|
override fun batchFinish() {
|
||||||
drawerManager.refreshNotificationDrawer()
|
notificationDrawerManager.refreshNotificationDrawer()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startWithSession(session: Session) {
|
fun startWithSession(session: Session) {
|
||||||
@ -71,7 +71,7 @@ class PushRuleTriggerListener @Inject constructor(
|
|||||||
fun stop() {
|
fun stop() {
|
||||||
session?.removePushRuleListener(this)
|
session?.removePushRuleListener(this)
|
||||||
session = null
|
session = null
|
||||||
drawerManager.clearAllEvents()
|
notificationDrawerManager.clearAllEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -25,14 +25,15 @@ import android.net.Uri;
|
|||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import im.vector.riotredesign.R;
|
import im.vector.riotredesign.R;
|
||||||
import im.vector.riotredesign.features.homeserver.ServerUrlsRepository;
|
import im.vector.riotredesign.features.homeserver.ServerUrlsRepository;
|
||||||
import im.vector.riotredesign.features.themes.ThemeUtils;
|
import im.vector.riotredesign.features.themes.ThemeUtils;
|
||||||
@ -51,8 +52,6 @@ public class PreferencesManager {
|
|||||||
public static final String SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY = "SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY";
|
public static final String SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY = "SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY = "SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY";
|
public static final String SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY = "SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY";
|
||||||
|
|
||||||
//TODO delete
|
|
||||||
public static final String SETTINGS_NOTIFICATION_PRIVACY_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_PRIVACY_PREFERENCE_KEY";
|
|
||||||
public static final String SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY";
|
public static final String SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY";
|
public static final String SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY";
|
public static final String SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY";
|
||||||
@ -119,7 +118,7 @@ public class PreferencesManager {
|
|||||||
public static final String SETTINGS_NOTIFICATIONS_KEY = "SETTINGS_NOTIFICATIONS_KEY";
|
public static final String SETTINGS_NOTIFICATIONS_KEY = "SETTINGS_NOTIFICATIONS_KEY";
|
||||||
public static final String SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY = "SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY";
|
public static final String SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY = "SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY = "SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY";
|
public static final String SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY = "SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY = "SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY";
|
// public static final String SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY = "SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY";
|
public static final String SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_SYSTEM_NOISY_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_NOISY_NOTIFICATION_PREFERENCE_KEY";
|
public static final String SETTINGS_SYSTEM_NOISY_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_NOISY_NOTIFICATION_PREFERENCE_KEY";
|
||||||
public static final String SETTINGS_SYSTEM_SILENT_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_SILENT_NOTIFICATION_PREFERENCE_KEY";
|
public static final String SETTINGS_SYSTEM_SILENT_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_SILENT_NOTIFICATION_PREFERENCE_KEY";
|
||||||
@ -247,6 +246,13 @@ public class PreferencesManager {
|
|||||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY, true);
|
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setNotificationEnabledForDevice(Context context, Boolean enabled) {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY, enabled)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if we have already asked the user to disable battery optimisations on android >= M devices.
|
* Tells if we have already asked the user to disable battery optimisations on android >= M devices.
|
||||||
*
|
*
|
||||||
|
@ -53,20 +53,15 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
configureToolbar(settingsToolbar)
|
configureToolbar(settingsToolbar)
|
||||||
|
|
||||||
var vectorSettingsPreferencesFragment: Fragment? = null
|
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
vectorSettingsPreferencesFragment = VectorSettingsPreferencesFragmentV2.newInstance()
|
val vectorSettingsPreferencesFragment = VectorSettingsRootFragment.newInstance()
|
||||||
// display the fragment
|
// display the fragment
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.vector_settings_page, vectorSettingsPreferencesFragment, FRAGMENT_TAG)
|
.replace(R.id.vector_settings_page, vectorSettingsPreferencesFragment, FRAGMENT_TAG)
|
||||||
.commit()
|
.commit()
|
||||||
} else {
|
|
||||||
vectorSettingsPreferencesFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
supportFragmentManager.addOnBackStackChangedListener(this)
|
supportFragmentManager.addOnBackStackChangedListener(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
@ -83,9 +78,7 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||||||
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat?, pref: Preference?): Boolean {
|
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat?, pref: Preference?): Boolean {
|
||||||
var oFragment: Fragment? = null
|
var oFragment: Fragment? = null
|
||||||
|
|
||||||
if ("Legacy" == pref?.title) {
|
if (PreferencesManager.SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY == pref?.key) {
|
||||||
oFragment = VectorSettingsPreferencesFragment.newInstance(session.sessionParams.credentials.userId)
|
|
||||||
} else if (PreferencesManager.SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY == pref?.key) {
|
|
||||||
oFragment = VectorSettingsNotificationsTroubleshootFragment.newInstance(session.sessionParams.credentials.userId)
|
oFragment = VectorSettingsNotificationsTroubleshootFragment.newInstance(session.sessionParams.credentials.userId)
|
||||||
} else if (PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY == pref?.key) {
|
} else if (PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY == pref?.key) {
|
||||||
oFragment = VectorSettingsAdvancedNotificationPreferenceFragment.newInstance(session.sessionParams.credentials.userId)
|
oFragment = VectorSettingsAdvancedNotificationPreferenceFragment.newInstance(session.sessionParams.credentials.userId)
|
||||||
|
@ -36,11 +36,7 @@ import im.vector.riotredesign.features.notifications.NotificationUtils
|
|||||||
import im.vector.riotredesign.features.notifications.supportNotificationChannels
|
import im.vector.riotredesign.features.notifications.supportNotificationChannels
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFragment() {
|
class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
// members
|
|
||||||
@Inject lateinit var session: Session
|
|
||||||
private var mLoadingView: View? = null
|
|
||||||
|
|
||||||
// events listener
|
// events listener
|
||||||
/* TODO
|
/* TODO
|
||||||
@ -53,10 +49,9 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
|
|||||||
|
|
||||||
override var titleRes: Int = R.string.settings_notification_advanced
|
override var titleRes: Int = R.string.settings_notification_advanced
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override val preferenceXmlRes = R.xml.vector_settings_notification_advanced_preferences
|
||||||
// define the layout
|
|
||||||
addPreferencesFromResource(R.xml.vector_settings_notification_advanced_preferences)
|
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
val callNotificationsSystemOptions = findPreference(PreferencesManager.SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY)
|
val callNotificationsSystemOptions = findPreference(PreferencesManager.SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY)
|
||||||
if (supportNotificationChannels()) {
|
if (supportNotificationChannels()) {
|
||||||
callNotificationsSystemOptions.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
callNotificationsSystemOptions.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
@ -177,33 +172,6 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
// find the view from parent activity
|
|
||||||
mLoadingView = activity!!.findViewById(R.id.vector_settings_spinner_views)
|
|
||||||
|
|
||||||
/* TODO
|
|
||||||
if (session.isAlive) {
|
|
||||||
|
|
||||||
session.dataHandler.addListener(mEventsListener)
|
|
||||||
|
|
||||||
// refresh anything else
|
|
||||||
refreshPreferences()
|
|
||||||
refreshDisplay()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
|
|
||||||
/* TODO
|
|
||||||
if (session.isAlive) {
|
|
||||||
session.dataHandler.removeListener(mEventsListener)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the known information about the account
|
* Refresh the known information about the account
|
||||||
*/
|
*/
|
||||||
@ -246,30 +214,6 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================================================
|
|
||||||
// Display methods
|
|
||||||
//==============================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the loading view.
|
|
||||||
*/
|
|
||||||
private fun displayLoadingView() {
|
|
||||||
if (null != mLoadingView) {
|
|
||||||
mLoadingView!!.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the loading view.
|
|
||||||
*/
|
|
||||||
private fun hideLoadingView() {
|
|
||||||
if (null != mLoadingView) {
|
|
||||||
mLoadingView!!.visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* Companion
|
* Companion
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.DaggerScreenComponent
|
||||||
|
import im.vector.riotredesign.core.di.HasScreenInjector
|
||||||
|
import im.vector.riotredesign.core.di.ScreenComponent
|
||||||
|
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.riotredesign.core.utils.toast
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScreenInjector {
|
||||||
|
|
||||||
|
val vectorActivity: VectorBaseActivity by lazy {
|
||||||
|
activity as VectorBaseActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mLoadingView: View? = null
|
||||||
|
|
||||||
|
// members
|
||||||
|
protected lateinit var session: Session
|
||||||
|
private lateinit var screenComponent: ScreenComponent
|
||||||
|
|
||||||
|
abstract val preferenceXmlRes: Int
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
addPreferencesFromResource(preferenceXmlRes)
|
||||||
|
bindPref()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
screenComponent = DaggerScreenComponent.factory().create(vectorActivity.getVectorComponent(), vectorActivity)
|
||||||
|
super.onAttach(context)
|
||||||
|
session = screenComponent.session()
|
||||||
|
injectWith(injector())
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun injectWith(injector: ScreenComponent) = Unit
|
||||||
|
|
||||||
|
override fun injector(): ScreenComponent {
|
||||||
|
return screenComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
Timber.v("onResume Fragment ${this.javaClass.simpleName}")
|
||||||
|
vectorActivity.supportActionBar?.setTitle(titleRes)
|
||||||
|
// find the view from parent activity
|
||||||
|
mLoadingView = vectorActivity.findViewById(R.id.vector_settings_spinner_views)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun bindPref()
|
||||||
|
|
||||||
|
abstract var titleRes: Int
|
||||||
|
|
||||||
|
/* ==========================================================================================
|
||||||
|
* Protected
|
||||||
|
* ========================================================================================== */
|
||||||
|
|
||||||
|
protected fun notImplemented() {
|
||||||
|
// Snackbar cannot be display on PreferenceFragment
|
||||||
|
// Snackbar.make(view!!, R.string.not_implemented, Snackbar.LENGTH_SHORT)
|
||||||
|
activity?.toast(R.string.not_implemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the loading view.
|
||||||
|
*/
|
||||||
|
protected fun displayLoadingView() {
|
||||||
|
// search the loading view from the upper view
|
||||||
|
if (null == mLoadingView) {
|
||||||
|
var parent = view
|
||||||
|
|
||||||
|
while (parent != null && mLoadingView == null) {
|
||||||
|
mLoadingView = parent.findViewById(R.id.vector_settings_spinner_views)
|
||||||
|
parent = parent.parent as View
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mLoadingView?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the loading view.
|
||||||
|
*/
|
||||||
|
protected fun hideLoadingView() {
|
||||||
|
mLoadingView?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the loading view and refresh the preferences.
|
||||||
|
*
|
||||||
|
* @param refresh true to refresh the display
|
||||||
|
*/
|
||||||
|
protected fun hideLoadingView(refresh: Boolean) {
|
||||||
|
mLoadingView?.visibility = View.GONE
|
||||||
|
|
||||||
|
if (refresh) {
|
||||||
|
// TODO refreshDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request has been processed.
|
||||||
|
* Display a toast if there is a an error message
|
||||||
|
*
|
||||||
|
* @param errorMessage the error message
|
||||||
|
*/
|
||||||
|
protected fun onCommonDone(errorMessage: String?) {
|
||||||
|
if (!isAdded) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
if (!TextUtils.isEmpty(errorMessage) && errorMessage != null) {
|
||||||
|
activity?.toast(errorMessage!!)
|
||||||
|
}
|
||||||
|
hideLoadingView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.preference.ProgressBarPreference
|
||||||
|
|
||||||
|
class VectorSettingsFlairFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.settings_flair
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_flair
|
||||||
|
|
||||||
|
// current publicised group list
|
||||||
|
private var mPublicisedGroups: MutableSet<String>? = null
|
||||||
|
|
||||||
|
// Group Flairs
|
||||||
|
private val mGroupsFlairCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_GROUPS_FLAIR_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// Flair
|
||||||
|
refreshGroupFlairsList()
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// Group flairs management
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the refresh of the devices list.<br></br>
|
||||||
|
* The devices list is the list of the devices where the user as looged in.
|
||||||
|
* It can be any mobile device, as any browser.
|
||||||
|
*/
|
||||||
|
private fun refreshGroupFlairsList() {
|
||||||
|
// display a spinner while refreshing
|
||||||
|
if (0 == mGroupsFlairCategory.preferenceCount) {
|
||||||
|
activity?.let {
|
||||||
|
val preference = ProgressBarPreference(it)
|
||||||
|
mGroupsFlairCategory.addPreference(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
session.groupsManager.getUserPublicisedGroups(session.myUserId, true, object : MatrixCallback<Set<String>> {
|
||||||
|
override fun onSuccess(publicisedGroups: Set<String>) {
|
||||||
|
// clear everything
|
||||||
|
mGroupsFlairCategory.removeAll()
|
||||||
|
|
||||||
|
if (publicisedGroups.isEmpty()) {
|
||||||
|
val vectorGroupPreference = Preference(activity)
|
||||||
|
vectorGroupPreference.title = resources.getString(R.string.settings_without_flair)
|
||||||
|
mGroupsFlairCategory.addPreference(vectorGroupPreference)
|
||||||
|
} else {
|
||||||
|
buildGroupsList(publicisedGroups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the groups list.
|
||||||
|
*
|
||||||
|
* @param publicisedGroups the publicised groups list.
|
||||||
|
*/
|
||||||
|
private fun buildGroupsList(publicisedGroups: Set<String>) {
|
||||||
|
var isNewList = true
|
||||||
|
|
||||||
|
mPublicisedGroups?.let {
|
||||||
|
if (it.size == publicisedGroups.size) {
|
||||||
|
isNewList = !it.containsAll(publicisedGroups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewList) {
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
val joinedGroups = ArrayList(session.groupsManager.joinedGroups)
|
||||||
|
Collections.sort(joinedGroups, Group.mGroupsComparator)
|
||||||
|
|
||||||
|
mPublicisedGroups = publicisedGroups.toMutableSet()
|
||||||
|
|
||||||
|
for ((prefIndex, group) in joinedGroups.withIndex()) {
|
||||||
|
val vectorGroupPreference = VectorGroupPreference(activity!!)
|
||||||
|
vectorGroupPreference.key = DEVICES_PREFERENCE_KEY_BASE + prefIndex
|
||||||
|
|
||||||
|
vectorGroupPreference.setGroup(group, session)
|
||||||
|
vectorGroupPreference.title = group.displayName
|
||||||
|
vectorGroupPreference.summary = group.groupId
|
||||||
|
|
||||||
|
vectorGroupPreference.isChecked = publicisedGroups.contains(group.groupId)
|
||||||
|
mGroupsFlairCategory.addPreference(vectorGroupPreference)
|
||||||
|
|
||||||
|
vectorGroupPreference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
if (newValue is Boolean) {
|
||||||
|
/*
|
||||||
|
* if mPublicisedGroup is null somehow, then
|
||||||
|
* we cant check it contains groupId or not
|
||||||
|
* so set isFlaired to false
|
||||||
|
*/
|
||||||
|
val isFlaired = mPublicisedGroups?.contains(group.groupId) ?: false
|
||||||
|
|
||||||
|
if (newValue != isFlaired) {
|
||||||
|
displayLoadingView()
|
||||||
|
session.groupsManager.updateGroupPublicity(group.groupId, newValue, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
hideLoadingView()
|
||||||
|
if (newValue) {
|
||||||
|
mPublicisedGroups?.add(group.groupId)
|
||||||
|
} else {
|
||||||
|
mPublicisedGroups?.remove(group.groupId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onError() {
|
||||||
|
hideLoadingView()
|
||||||
|
// restore default value
|
||||||
|
vectorGroupPreference.isChecked = publicisedGroups.contains(group.groupId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
onError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onError()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,893 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.extensions.showPassword
|
||||||
|
import im.vector.riotredesign.core.platform.SimpleTextWatcher
|
||||||
|
import im.vector.riotredesign.core.preference.UserAvatarPreference
|
||||||
|
import im.vector.riotredesign.core.preference.VectorPreference
|
||||||
|
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
|
||||||
|
import im.vector.riotredesign.core.utils.allGranted
|
||||||
|
import im.vector.riotredesign.core.utils.copyToClipboard
|
||||||
|
import im.vector.riotredesign.core.utils.toast
|
||||||
|
import im.vector.riotredesign.features.MainActivity
|
||||||
|
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.settings_general_title
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_general
|
||||||
|
|
||||||
|
private var mDisplayedEmails = ArrayList<String>()
|
||||||
|
private var mDisplayedPhoneNumber = ArrayList<String>()
|
||||||
|
|
||||||
|
private val mUserSettingsCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_USER_SETTINGS_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
private val mUserAvatarPreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_PROFILE_PICTURE_PREFERENCE_KEY) as UserAvatarPreference
|
||||||
|
}
|
||||||
|
private val mDisplayNamePreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_DISPLAY_NAME_PREFERENCE_KEY) as EditTextPreference
|
||||||
|
}
|
||||||
|
private val mPasswordPreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CHANGE_PASSWORD_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local contacts
|
||||||
|
private val mContactSettingsCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CONTACT_PREFERENCE_KEYS) as PreferenceCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mContactPhonebookCountryPreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CONTACTS_PHONEBOOK_COUNTRY_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// Avatar
|
||||||
|
mUserAvatarPreference.let {
|
||||||
|
it.setSession(session)
|
||||||
|
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
onUpdateAvatarClick()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display name
|
||||||
|
mDisplayNamePreference.let {
|
||||||
|
it.summary = session.getUser(session.sessionParams.credentials.userId)?.displayName ?: ""
|
||||||
|
it.text = it.summary.toString()
|
||||||
|
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
onDisplayNameClick(newValue?.let { (it as String).trim() })
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password
|
||||||
|
mPasswordPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
notImplemented()
|
||||||
|
// onPasswordUpdateClick()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Email
|
||||||
|
(findPreference(ADD_EMAIL_PREFERENCE_KEY) as EditTextPreference).let {
|
||||||
|
// It does not work on XML, do it here
|
||||||
|
it.icon = activity?.let {
|
||||||
|
ThemeUtils.tintDrawable(it,
|
||||||
|
ContextCompat.getDrawable(it, R.drawable.ic_add_black)!!, R.attr.vctr_settings_icon_tint_color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, this is not supported in lib v7
|
||||||
|
// it.editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||||
|
|
||||||
|
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
notImplemented()
|
||||||
|
//addEmail((newValue as String).trim())
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add phone number
|
||||||
|
findPreference(ADD_PHONE_NUMBER_PREFERENCE_KEY).let {
|
||||||
|
// It does not work on XML, do it here
|
||||||
|
it.icon = activity?.let {
|
||||||
|
ThemeUtils.tintDrawable(it,
|
||||||
|
ContextCompat.getDrawable(it, R.drawable.ic_add_black)!!, R.attr.vctr_settings_icon_tint_color)
|
||||||
|
}
|
||||||
|
|
||||||
|
it.setOnPreferenceClickListener {
|
||||||
|
notImplemented()
|
||||||
|
// TODO val intent = PhoneNumberAdditionActivity.getIntent(activity, session.credentials.userId)
|
||||||
|
// startActivityForResult(intent, REQUEST_NEW_PHONE_NUMBER)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advanced settings
|
||||||
|
|
||||||
|
// user account
|
||||||
|
findPreference(PreferencesManager.SETTINGS_LOGGED_IN_PREFERENCE_KEY)
|
||||||
|
.summary = session.sessionParams.credentials.userId
|
||||||
|
|
||||||
|
// home server
|
||||||
|
findPreference(PreferencesManager.SETTINGS_HOME_SERVER_PREFERENCE_KEY)
|
||||||
|
.summary = session.sessionParams.homeServerConnectionConfig.homeServerUri.toString()
|
||||||
|
|
||||||
|
// identity server
|
||||||
|
findPreference(PreferencesManager.SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY)
|
||||||
|
.summary = session.sessionParams.homeServerConnectionConfig.identityServerUri.toString()
|
||||||
|
|
||||||
|
|
||||||
|
refreshEmailsList()
|
||||||
|
refreshPhoneNumbersList()
|
||||||
|
// Contacts
|
||||||
|
setContactsPreferences()
|
||||||
|
|
||||||
|
// clear cache
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CLEAR_CACHE_PREFERENCE_KEY).let {
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
MXSession.getApplicationSizeCaches(activity, object : SimpleApiCallback<Long>() {
|
||||||
|
override fun onSuccess(size: Long) {
|
||||||
|
if (null != activity) {
|
||||||
|
it.summary = android.text.format.Formatter.formatFileSize(activity, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
displayLoadingView()
|
||||||
|
MainActivity.restartApp(activity!!, clearCache = true, clearCredentials = false)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear medias cache
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY).let {
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
MXMediaCache.getCachesSize(activity, object : SimpleApiCallback<Long>() {
|
||||||
|
override fun onSuccess(size: Long) {
|
||||||
|
if (null != activity) {
|
||||||
|
it.summary = android.text.format.Formatter.formatFileSize(activity, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
val task = ClearMediaCacheAsyncTask(
|
||||||
|
backgroundTask = {
|
||||||
|
session.mediaCache.clear()
|
||||||
|
activity?.let { it -> Glide.get(it).clearDiskCache() }
|
||||||
|
},
|
||||||
|
onCompleteTask = {
|
||||||
|
hideLoadingView()
|
||||||
|
|
||||||
|
MXMediaCache.getCachesSize(activity, object : SimpleApiCallback<Long>() {
|
||||||
|
override fun onSuccess(size: Long) {
|
||||||
|
it.summary = Formatter.formatFileSize(activity, size)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "## session.getMediaCache().clear() failed " + e.message)
|
||||||
|
task.cancel(true)
|
||||||
|
hideLoadingView()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deactivate account section
|
||||||
|
|
||||||
|
// deactivate account
|
||||||
|
findPreference(PreferencesManager.SETTINGS_DEACTIVATE_ACCOUNT_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.let {
|
||||||
|
notImplemented()
|
||||||
|
// TODO startActivity(DeactivateAccountActivity.getIntent(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
|
if (allGranted(grantResults)) {
|
||||||
|
if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA) {
|
||||||
|
changeAvatar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
when (requestCode) {
|
||||||
|
REQUEST_NEW_PHONE_NUMBER -> refreshPhoneNumbersList()
|
||||||
|
REQUEST_PHONEBOOK_COUNTRY -> onPhonebookCountryUpdate(data)
|
||||||
|
/* TODO
|
||||||
|
VectorUtils.TAKE_IMAGE -> {
|
||||||
|
val thumbnailUri = VectorUtils.getThumbnailUriFromIntent(activity, data, session.mediaCache)
|
||||||
|
|
||||||
|
if (null != thumbnailUri) {
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
val resource = ResourceUtils.openResource(activity, thumbnailUri, null)
|
||||||
|
|
||||||
|
if (null != resource) {
|
||||||
|
session.mediaCache.uploadContent(resource.mContentStream, null, resource.mMimeType, null, object : MXMediaUploadListener() {
|
||||||
|
|
||||||
|
override fun onUploadError(uploadId: String?, serverResponseCode: Int, serverErrorMessage: String?) {
|
||||||
|
activity?.runOnUiThread { onCommonDone(serverResponseCode.toString() + " : " + serverErrorMessage) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUploadComplete(uploadId: String?, contentUri: String?) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
session.myUser.updateAvatarUrl(contentUri, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
onCommonDone(null)
|
||||||
|
refreshDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
if (MatrixError.M_CONSENT_NOT_GIVEN == e.errcode) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
hideLoadingView()
|
||||||
|
(activity as VectorAppCompatActivity).consentNotGivenHelper.displayDialog(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the avatar.
|
||||||
|
*/
|
||||||
|
private fun onUpdateAvatarClick() {
|
||||||
|
notImplemented()
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
|
||||||
|
changeAvatar()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeAvatar() {
|
||||||
|
/* TODO
|
||||||
|
val intent = Intent(activity, VectorMediaPickerActivity::class.java)
|
||||||
|
intent.putExtra(VectorMediaPickerActivity.EXTRA_AVATAR_MODE, true)
|
||||||
|
startActivityForResult(intent, VectorUtils.TAKE_IMAGE)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// contacts management
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
private fun setContactsPreferences() {
|
||||||
|
/* TODO
|
||||||
|
// Permission
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
// on Android >= 23, use the system one
|
||||||
|
mContactSettingsCategory.removePreference(findPreference(ContactsManager.CONTACTS_BOOK_ACCESS_KEY))
|
||||||
|
}
|
||||||
|
// Phonebook country
|
||||||
|
mContactPhonebookCountryPreference.summary = PhoneNumberUtils.getHumanCountryCode(PhoneNumberUtils.getCountryCode(activity))
|
||||||
|
|
||||||
|
mContactPhonebookCountryPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
val intent = CountryPickerActivity.getIntent(activity, true)
|
||||||
|
startActivityForResult(intent, REQUEST_PHONEBOOK_COUNTRY)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onPhonebookCountryUpdate(data: Intent?) {
|
||||||
|
/* TODO
|
||||||
|
if (data != null && data.hasExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_NAME)
|
||||||
|
&& data.hasExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_CODE)) {
|
||||||
|
val countryCode = data.getStringExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_CODE)
|
||||||
|
if (!TextUtils.equals(countryCode, PhoneNumberUtils.getCountryCode(activity))) {
|
||||||
|
PhoneNumberUtils.setCountryCode(activity, countryCode)
|
||||||
|
mContactPhonebookCountryPreference.summary = data.getStringExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// Phone number management
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh phone number list
|
||||||
|
*/
|
||||||
|
private fun refreshPhoneNumbersList() {
|
||||||
|
/* TODO
|
||||||
|
val currentPhoneNumber3PID = ArrayList(session.myUser.getlinkedPhoneNumbers())
|
||||||
|
|
||||||
|
val phoneNumberList = ArrayList<String>()
|
||||||
|
for (identifier in currentPhoneNumber3PID) {
|
||||||
|
phoneNumberList.add(identifier.address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check first if there is an update
|
||||||
|
var isNewList = true
|
||||||
|
if (phoneNumberList.size == mDisplayedPhoneNumber.size) {
|
||||||
|
isNewList = !mDisplayedPhoneNumber.containsAll(phoneNumberList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewList) {
|
||||||
|
// remove the displayed one
|
||||||
|
run {
|
||||||
|
var index = 0
|
||||||
|
while (true) {
|
||||||
|
val preference = mUserSettingsCategory.findPreference(PHONE_NUMBER_PREFERENCE_KEY_BASE + index)
|
||||||
|
|
||||||
|
if (null != preference) {
|
||||||
|
mUserSettingsCategory.removePreference(preference)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new phone number list
|
||||||
|
mDisplayedPhoneNumber = phoneNumberList
|
||||||
|
|
||||||
|
val addPhoneBtn = mUserSettingsCategory.findPreference(ADD_PHONE_NUMBER_PREFERENCE_KEY)
|
||||||
|
?: return
|
||||||
|
|
||||||
|
var order = addPhoneBtn.order
|
||||||
|
|
||||||
|
for ((index, phoneNumber3PID) in currentPhoneNumber3PID.withIndex()) {
|
||||||
|
val preference = VectorPreference(activity!!)
|
||||||
|
|
||||||
|
preference.title = getString(R.string.settings_phone_number)
|
||||||
|
var phoneNumberFormatted = phoneNumber3PID.address
|
||||||
|
try {
|
||||||
|
// Attempt to format phone number
|
||||||
|
val phoneNumber = PhoneNumberUtil.getInstance().parse("+$phoneNumberFormatted", null)
|
||||||
|
phoneNumberFormatted = PhoneNumberUtil.getInstance().format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)
|
||||||
|
} catch (e: NumberParseException) {
|
||||||
|
// Do nothing, we will display raw version
|
||||||
|
}
|
||||||
|
|
||||||
|
preference.summary = phoneNumberFormatted
|
||||||
|
preference.key = PHONE_NUMBER_PREFERENCE_KEY_BASE + index
|
||||||
|
preference.order = order
|
||||||
|
|
||||||
|
preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
displayDelete3PIDConfirmationDialog(phoneNumber3PID, preference.summary)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
preference.onPreferenceLongClickListener = object : VectorPreference.OnPreferenceLongClickListener {
|
||||||
|
override fun onPreferenceLongClick(preference: Preference): Boolean {
|
||||||
|
activity?.let { copyToClipboard(it, phoneNumber3PID.address) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order++
|
||||||
|
mUserSettingsCategory.addPreference(preference)
|
||||||
|
}
|
||||||
|
|
||||||
|
addPhoneBtn.order = order
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// Email management
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the emails list
|
||||||
|
*/
|
||||||
|
private fun refreshEmailsList() {
|
||||||
|
val currentEmail3PID = emptyList<String>() // TODO ArrayList(session.myUser.getlinkedEmails())
|
||||||
|
|
||||||
|
val newEmailsList = ArrayList<String>()
|
||||||
|
for (identifier in currentEmail3PID) {
|
||||||
|
// TODO newEmailsList.add(identifier.address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check first if there is an update
|
||||||
|
var isNewList = true
|
||||||
|
if (newEmailsList.size == mDisplayedEmails.size) {
|
||||||
|
isNewList = !mDisplayedEmails.containsAll(newEmailsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewList) {
|
||||||
|
// remove the displayed one
|
||||||
|
run {
|
||||||
|
var index = 0
|
||||||
|
while (true) {
|
||||||
|
val preference = mUserSettingsCategory.findPreference(EMAIL_PREFERENCE_KEY_BASE + index)
|
||||||
|
|
||||||
|
if (null != preference) {
|
||||||
|
mUserSettingsCategory.removePreference(preference)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new emails list
|
||||||
|
mDisplayedEmails = newEmailsList
|
||||||
|
|
||||||
|
val addEmailBtn = mUserSettingsCategory.findPreference(ADD_EMAIL_PREFERENCE_KEY)
|
||||||
|
?: return
|
||||||
|
|
||||||
|
var order = addEmailBtn.order
|
||||||
|
|
||||||
|
for ((index, email3PID) in currentEmail3PID.withIndex()) {
|
||||||
|
val preference = VectorPreference(activity!!)
|
||||||
|
|
||||||
|
preference.title = getString(R.string.settings_email_address)
|
||||||
|
preference.summary = "TODO" // email3PID.address
|
||||||
|
preference.key = EMAIL_PREFERENCE_KEY_BASE + index
|
||||||
|
preference.order = order
|
||||||
|
|
||||||
|
preference.onPreferenceClickListener = Preference.OnPreferenceClickListener { pref ->
|
||||||
|
displayDelete3PIDConfirmationDialog(/* TODO email3PID, */ pref.summary)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
preference.onPreferenceLongClickListener = object : VectorPreference.OnPreferenceLongClickListener {
|
||||||
|
override fun onPreferenceLongClick(preference: Preference): Boolean {
|
||||||
|
activity?.let { copyToClipboard(it, "TODO") } //email3PID.address) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mUserSettingsCategory.addPreference(preference)
|
||||||
|
|
||||||
|
order++
|
||||||
|
}
|
||||||
|
|
||||||
|
addEmailBtn.order = order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to add a new email to the account
|
||||||
|
*
|
||||||
|
* @param email the email to add.
|
||||||
|
*/
|
||||||
|
private fun addEmail(email: String) {
|
||||||
|
// check first if the email syntax is valid
|
||||||
|
// if email is null , then also its invalid email
|
||||||
|
if (TextUtils.isEmpty(email) || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||||
|
activity?.toast(R.string.auth_invalid_email)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check first if the email syntax is valid
|
||||||
|
if (mDisplayedEmails.indexOf(email) >= 0) {
|
||||||
|
activity?.toast(R.string.auth_email_already_defined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
val pid = ThreePid(email, ThreePid.MEDIUM_EMAIL)
|
||||||
|
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
session.myUser.requestEmailValidationToken(pid, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
activity?.runOnUiThread { showEmailValidationDialog(pid) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
if (TextUtils.equals(MatrixError.THREEPID_IN_USE, e.errcode)) {
|
||||||
|
onCommonDone(getString(R.string.account_email_already_used_error))
|
||||||
|
} else {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an email validation dialog to warn the user tho valid his email link.
|
||||||
|
*
|
||||||
|
* @param pid the used pid.
|
||||||
|
*/
|
||||||
|
/* TODO
|
||||||
|
private fun showEmailValidationDialog(pid: ThreePid) {
|
||||||
|
activity?.let {
|
||||||
|
AlertDialog.Builder(it)
|
||||||
|
.setTitle(R.string.account_email_validation_title)
|
||||||
|
.setMessage(R.string.account_email_validation_message)
|
||||||
|
.setPositiveButton(R.string._continue) { _, _ ->
|
||||||
|
session.myUser.add3Pid(pid, true, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
it.runOnUiThread {
|
||||||
|
hideLoadingView()
|
||||||
|
refreshEmailsList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
if (TextUtils.equals(e.errcode, MatrixError.THREEPID_AUTH_FAILED)) {
|
||||||
|
it.runOnUiThread {
|
||||||
|
hideLoadingView()
|
||||||
|
it.toast(R.string.account_email_validation_error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||||
|
hideLoadingView()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a dialog which asks confirmation for the deletion of a 3pid
|
||||||
|
*
|
||||||
|
* @param pid the 3pid to delete
|
||||||
|
* @param preferenceSummary the displayed 3pid
|
||||||
|
*/
|
||||||
|
private fun displayDelete3PIDConfirmationDialog(/* TODO pid: ThirdPartyIdentifier,*/ preferenceSummary: CharSequence) {
|
||||||
|
val mediumFriendlyName = "TODO" // ThreePid.getMediumFriendlyName(pid.medium, activity).toLowerCase(VectorLocale.applicationLocale)
|
||||||
|
val dialogMessage = getString(R.string.settings_delete_threepid_confirmation, mediumFriendlyName, preferenceSummary)
|
||||||
|
|
||||||
|
activity?.let {
|
||||||
|
AlertDialog.Builder(it)
|
||||||
|
.setTitle(R.string.dialog_title_confirmation)
|
||||||
|
.setMessage(dialogMessage)
|
||||||
|
.setPositiveButton(R.string.remove) { _, _ ->
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
session.myUser.delete3Pid(pid, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
when (pid.medium) {
|
||||||
|
ThreePid.MEDIUM_EMAIL -> refreshEmailsList()
|
||||||
|
ThreePid.MEDIUM_MSISDN -> refreshPhoneNumbersList()
|
||||||
|
}
|
||||||
|
onCommonDone(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the password.
|
||||||
|
*/
|
||||||
|
private fun onPasswordUpdateClick() {
|
||||||
|
activity?.let { activity ->
|
||||||
|
val view: ViewGroup = activity.layoutInflater.inflate(R.layout.dialog_change_password, null) as ViewGroup
|
||||||
|
|
||||||
|
val showPassword: ImageView = view.findViewById(R.id.change_password_show_passwords)
|
||||||
|
val oldPasswordTil: TextInputLayout = view.findViewById(R.id.change_password_old_pwd_til)
|
||||||
|
val oldPasswordText: TextInputEditText = view.findViewById(R.id.change_password_old_pwd_text)
|
||||||
|
val newPasswordText: TextInputEditText = view.findViewById(R.id.change_password_new_pwd_text)
|
||||||
|
val confirmNewPasswordTil: TextInputLayout = view.findViewById(R.id.change_password_confirm_new_pwd_til)
|
||||||
|
val confirmNewPasswordText: TextInputEditText = view.findViewById(R.id.change_password_confirm_new_pwd_text)
|
||||||
|
val changePasswordLoader: View = view.findViewById(R.id.change_password_loader)
|
||||||
|
|
||||||
|
var passwordShown = false
|
||||||
|
|
||||||
|
showPassword.setOnClickListener(object : View.OnClickListener {
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
passwordShown = !passwordShown
|
||||||
|
|
||||||
|
oldPasswordText.showPassword(passwordShown)
|
||||||
|
newPasswordText.showPassword(passwordShown)
|
||||||
|
confirmNewPasswordText.showPassword(passwordShown)
|
||||||
|
|
||||||
|
showPassword.setImageResource(if (passwordShown) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val dialog = AlertDialog.Builder(activity)
|
||||||
|
.setView(view)
|
||||||
|
.setPositiveButton(R.string.settings_change_password_submit, null)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setOnDismissListener {
|
||||||
|
val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(view.applicationWindowToken, 0)
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
|
||||||
|
dialog.setOnShowListener {
|
||||||
|
val updateButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
|
||||||
|
updateButton.isEnabled = false
|
||||||
|
|
||||||
|
fun updateUi() {
|
||||||
|
val oldPwd = oldPasswordText.text.toString().trim()
|
||||||
|
val newPwd = newPasswordText.text.toString().trim()
|
||||||
|
val newConfirmPwd = confirmNewPasswordText.text.toString().trim()
|
||||||
|
|
||||||
|
updateButton.isEnabled = oldPwd.isNotEmpty() && newPwd.isNotEmpty() && TextUtils.equals(newPwd, newConfirmPwd)
|
||||||
|
|
||||||
|
if (newPwd.isNotEmpty() && newConfirmPwd.isNotEmpty() && !TextUtils.equals(newPwd, newConfirmPwd)) {
|
||||||
|
confirmNewPasswordTil.error = getString(R.string.passwords_do_not_match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldPasswordText.addTextChangedListener(object : SimpleTextWatcher() {
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
oldPasswordTil.error = null
|
||||||
|
updateUi()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
newPasswordText.addTextChangedListener(object : SimpleTextWatcher() {
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
confirmNewPasswordTil.error = null
|
||||||
|
updateUi()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
confirmNewPasswordText.addTextChangedListener(object : SimpleTextWatcher() {
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
confirmNewPasswordTil.error = null
|
||||||
|
updateUi()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fun showPasswordLoadingView(toShow: Boolean) {
|
||||||
|
if (toShow) {
|
||||||
|
showPassword.isEnabled = false
|
||||||
|
oldPasswordText.isEnabled = false
|
||||||
|
newPasswordText.isEnabled = false
|
||||||
|
confirmNewPasswordText.isEnabled = false
|
||||||
|
changePasswordLoader.isVisible = true
|
||||||
|
updateButton.isEnabled = false
|
||||||
|
} else {
|
||||||
|
showPassword.isEnabled = true
|
||||||
|
oldPasswordText.isEnabled = true
|
||||||
|
newPasswordText.isEnabled = true
|
||||||
|
confirmNewPasswordText.isEnabled = true
|
||||||
|
changePasswordLoader.isVisible = false
|
||||||
|
updateButton.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButton.setOnClickListener {
|
||||||
|
if (passwordShown) {
|
||||||
|
// Hide passwords during processing
|
||||||
|
showPassword.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(view.applicationWindowToken, 0)
|
||||||
|
|
||||||
|
val oldPwd = oldPasswordText.text.toString().trim()
|
||||||
|
val newPwd = newPasswordText.text.toString().trim()
|
||||||
|
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
showPasswordLoadingView(true)
|
||||||
|
|
||||||
|
session.updatePassword(oldPwd, newPwd, object : MatrixCallback<Unit> {
|
||||||
|
private fun onDone(@StringRes textResId: Int) {
|
||||||
|
showPasswordLoadingView(false)
|
||||||
|
|
||||||
|
if (textResId == R.string.settings_fail_to_update_password_invalid_current_password) {
|
||||||
|
oldPasswordTil.error = getString(textResId)
|
||||||
|
} else {
|
||||||
|
dialog.dismiss()
|
||||||
|
activity.toast(textResId, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
onDone(R.string.settings_password_updated)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onDone(R.string.settings_fail_to_update_password)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
if (e.error == "Invalid password") {
|
||||||
|
onDone(R.string.settings_fail_to_update_password_invalid_current_password)
|
||||||
|
} else {
|
||||||
|
dialog.dismiss()
|
||||||
|
onDone(R.string.settings_fail_to_update_password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onDone(R.string.settings_fail_to_update_password)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the displayname.
|
||||||
|
*/
|
||||||
|
private fun onDisplayNameClick(value: String?) {
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
if (!TextUtils.equals(session.myUser.displayname, value)) {
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
session.myUser.updateDisplayName(value, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
// refresh the settings value
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(activity).edit {
|
||||||
|
putString(PreferencesManager.SETTINGS_DISPLAY_NAME_PREFERENCE_KEY, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommonDone(null)
|
||||||
|
|
||||||
|
refreshDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
if (MatrixError.M_CONSENT_NOT_GIVEN == e.errcode) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
hideLoadingView()
|
||||||
|
(activity as VectorAppCompatActivity).consentNotGivenHelper.displayDialog(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClearMediaCacheAsyncTask internal constructor(
|
||||||
|
backgroundTask: () -> Unit,
|
||||||
|
onCompleteTask: () -> Unit
|
||||||
|
) : AsyncTask<Unit, Unit, Unit>() {
|
||||||
|
|
||||||
|
private val backgroundTaskReference = WeakReference(backgroundTask)
|
||||||
|
private val onCompleteTaskReference = WeakReference(onCompleteTask)
|
||||||
|
override fun doInBackground(vararg params: Unit?) {
|
||||||
|
backgroundTaskReference.get()?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(result: Unit?) {
|
||||||
|
super.onPostExecute(result)
|
||||||
|
onCompleteTaskReference.get()?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ADD_EMAIL_PREFERENCE_KEY = "ADD_EMAIL_PREFERENCE_KEY"
|
||||||
|
private const val ADD_PHONE_NUMBER_PREFERENCE_KEY = "ADD_PHONE_NUMBER_PREFERENCE_KEY"
|
||||||
|
|
||||||
|
private const val EMAIL_PREFERENCE_KEY_BASE = "EMAIL_PREFERENCE_KEY_BASE"
|
||||||
|
private const val PHONE_NUMBER_PREFERENCE_KEY_BASE = "PHONE_NUMBER_PREFERENCE_KEY_BASE"
|
||||||
|
|
||||||
|
private const val REQUEST_NEW_PHONE_NUMBER = 456
|
||||||
|
private const val REQUEST_PHONEBOOK_COUNTRY = 789
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
||||||
|
import im.vector.matrix.android.api.Matrix
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.utils.copyToClipboard
|
||||||
|
import im.vector.riotredesign.core.utils.displayInWebView
|
||||||
|
import im.vector.riotredesign.features.version.getVersion
|
||||||
|
|
||||||
|
class VectorSettingsHelpAboutFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.preference_root_help_about
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_help_about
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// preference to start the App info screen, to facilitate App permissions access
|
||||||
|
findPreference(APP_INFO_LINK_PREFERENCE_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
|
||||||
|
activity?.let {
|
||||||
|
val intent = Intent().apply {
|
||||||
|
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
|
||||||
|
val uri = Uri.fromParts("package", requireContext().packageName, null)
|
||||||
|
|
||||||
|
data = uri
|
||||||
|
}
|
||||||
|
it.applicationContext.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// application version
|
||||||
|
(findPreference(PreferencesManager.SETTINGS_VERSION_PREFERENCE_KEY)).let {
|
||||||
|
it.summary = getVersion(longFormat = false, useBuildNumber = true)
|
||||||
|
|
||||||
|
it.setOnPreferenceClickListener { pref ->
|
||||||
|
copyToClipboard(requireContext(), pref.summary)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SDK version
|
||||||
|
(findPreference(PreferencesManager.SETTINGS_SDK_VERSION_PREFERENCE_KEY)).let {
|
||||||
|
it.summary = Matrix.getSdkVersion()
|
||||||
|
|
||||||
|
it.setOnPreferenceClickListener { pref ->
|
||||||
|
copyToClipboard(requireContext(), pref.summary)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// olm version
|
||||||
|
findPreference(PreferencesManager.SETTINGS_OLM_VERSION_PREFERENCE_KEY)
|
||||||
|
.summary = session.getCryptoVersion(requireContext(), false)
|
||||||
|
|
||||||
|
// copyright
|
||||||
|
findPreference(PreferencesManager.SETTINGS_COPYRIGHT_PREFERENCE_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.displayInWebView(VectorSettingsUrls.COPYRIGHT)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// terms & conditions
|
||||||
|
findPreference(PreferencesManager.SETTINGS_APP_TERM_CONDITIONS_PREFERENCE_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.displayInWebView(VectorSettingsUrls.TAC)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// privacy policy
|
||||||
|
findPreference(PreferencesManager.SETTINGS_PRIVACY_POLICY_PREFERENCE_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.displayInWebView(VectorSettingsUrls.PRIVACY_POLICY)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// third party notice
|
||||||
|
findPreference(PreferencesManager.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.displayInWebView(VectorSettingsUrls.THIRD_PARTY_LICENSES)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
findPreference(PreferencesManager.SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY)
|
||||||
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
// See https://developers.google.com/android/guides/opensource
|
||||||
|
startActivity(Intent(requireActivity(), OssLicensesMenuActivity::class.java))
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val APP_INFO_LINK_PREFERENCE_KEY = "APP_INFO_LINK_PREFERENCE_KEY"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import java.util.ArrayList
|
||||||
|
import kotlin.Comparator
|
||||||
|
import kotlin.String
|
||||||
|
import kotlin.getValue
|
||||||
|
import kotlin.lazy
|
||||||
|
import kotlin.let
|
||||||
|
|
||||||
|
class VectorSettingsIgnoredUsersFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.settings_ignored_users
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_ignored_users
|
||||||
|
|
||||||
|
// displayed the ignored users list
|
||||||
|
private val mIgnoredUserSettingsCategoryDivider by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
private val mIgnoredUserSettingsCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_IGNORED_USERS_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// Ignore users
|
||||||
|
refreshIgnoredUsersList()
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// ignored users list management
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the ignored users list
|
||||||
|
*/
|
||||||
|
private fun refreshIgnoredUsersList() {
|
||||||
|
val ignoredUsersList = mutableListOf<String>() // TODO session.dataHandler.ignoredUserIds
|
||||||
|
|
||||||
|
ignoredUsersList.sortWith(Comparator { u1, u2 ->
|
||||||
|
u1.toLowerCase(VectorLocale.applicationLocale).compareTo(u2.toLowerCase(VectorLocale.applicationLocale))
|
||||||
|
})
|
||||||
|
|
||||||
|
val preferenceScreen = preferenceScreen
|
||||||
|
|
||||||
|
preferenceScreen.removePreference(mIgnoredUserSettingsCategory)
|
||||||
|
preferenceScreen.removePreference(mIgnoredUserSettingsCategoryDivider)
|
||||||
|
mIgnoredUserSettingsCategory.removeAll()
|
||||||
|
|
||||||
|
if (ignoredUsersList.size > 0) {
|
||||||
|
preferenceScreen.addPreference(mIgnoredUserSettingsCategoryDivider)
|
||||||
|
preferenceScreen.addPreference(mIgnoredUserSettingsCategory)
|
||||||
|
|
||||||
|
for (userId in ignoredUsersList) {
|
||||||
|
val preference = Preference(activity)
|
||||||
|
|
||||||
|
preference.title = userId
|
||||||
|
preference.key = IGNORED_USER_KEY_BASE + userId
|
||||||
|
|
||||||
|
preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.let {
|
||||||
|
AlertDialog.Builder(it)
|
||||||
|
.setMessage(getString(R.string.settings_unignore_user, userId))
|
||||||
|
.setPositiveButton(R.string.yes) { _, _ ->
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
val idsList = ArrayList<String>()
|
||||||
|
idsList.add(userId)
|
||||||
|
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
session.unIgnoreUsers(idsList, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
onCommonDone(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.no, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
mIgnoredUserSettingsCategory.addPreference(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val IGNORED_USER_KEY_BASE = "IGNORED_USER_KEY_BASE"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import androidx.preference.SwitchPreference
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
|
||||||
|
class VectorSettingsLabsFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.room_settings_labs_pref_title
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_labs
|
||||||
|
|
||||||
|
private val mLabsCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_LABS_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// Lab
|
||||||
|
val useCryptoPref = findPreference(PreferencesManager.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_PREFERENCE_KEY) as SwitchPreference
|
||||||
|
val cryptoIsEnabledPref = findPreference(PreferencesManager.SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_IS_ACTIVE_PREFERENCE_KEY)
|
||||||
|
|
||||||
|
|
||||||
|
if (session.isCryptoEnabled()) {
|
||||||
|
mLabsCategory.removePreference(useCryptoPref)
|
||||||
|
|
||||||
|
cryptoIsEnabledPref.isEnabled = false
|
||||||
|
} else {
|
||||||
|
mLabsCategory.removePreference(cryptoIsEnabledPref)
|
||||||
|
|
||||||
|
useCryptoPref.isChecked = false
|
||||||
|
|
||||||
|
useCryptoPref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValueAsVoid ->
|
||||||
|
if (TextUtils.isEmpty(session.sessionParams.credentials.deviceId)) {
|
||||||
|
activity?.let { activity ->
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setMessage(R.string.room_settings_labs_end_to_end_warnings)
|
||||||
|
.setPositiveButton(R.string.logout) { _, _ ->
|
||||||
|
notImplemented()
|
||||||
|
// TODO CommonActivityUtils.logout(activity)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||||
|
useCryptoPref.isChecked = false
|
||||||
|
}
|
||||||
|
.setOnCancelListener {
|
||||||
|
useCryptoPref.isChecked = false
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val newValue = newValueAsVoid as Boolean
|
||||||
|
|
||||||
|
if (session.isCryptoEnabled() != newValue) {
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
session.enableCrypto(newValue, object : MatrixCallback<Unit> {
|
||||||
|
private fun refresh() {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
hideLoadingView()
|
||||||
|
useCryptoPref.isChecked = session.isCryptoEnabled
|
||||||
|
|
||||||
|
if (session.isCryptoEnabled) {
|
||||||
|
mLabsCategory.removePreference(useCryptoPref)
|
||||||
|
mLabsCategory.addPreference(cryptoIsEnabledPref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
useCryptoPref.isEnabled = false
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
useCryptoPref.isChecked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
useCryptoPref.isChecked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
useCryptoPref.isChecked = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveMode Management
|
||||||
|
findPreference(PreferencesManager.SETTINGS_DATA_SAVE_MODE_PREFERENCE_KEY)
|
||||||
|
.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
notImplemented()
|
||||||
|
/* TODO
|
||||||
|
val sessions = Matrix.getMXSessions(activity)
|
||||||
|
for (session in sessions) {
|
||||||
|
session.setUseDataSaveMode(newValue as Boolean)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,39 +16,46 @@
|
|||||||
|
|
||||||
package im.vector.riotredesign.features.settings
|
package im.vector.riotredesign.features.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.widget.Toast
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import im.vector.matrix.android.api.Matrix
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.pushrules.RuleIds
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotredesign.core.di.DaggerScreenComponent
|
import im.vector.riotredesign.core.di.ScreenComponent
|
||||||
import im.vector.riotredesign.core.platform.VectorPreferenceFragment
|
|
||||||
import im.vector.riotredesign.core.pushers.PushersManager
|
import im.vector.riotredesign.core.pushers.PushersManager
|
||||||
import im.vector.riotredesign.push.fcm.FcmHelper
|
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// Referenced in vector_settings_preferences_root.xml
|
// Referenced in vector_settings_preferences_root.xml
|
||||||
class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment() {
|
class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
|
||||||
override var titleRes: Int = R.string.settings_notifications
|
override var titleRes: Int = R.string.settings_notifications
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_notifications
|
||||||
|
|
||||||
@Inject lateinit var pushManager: PushersManager
|
@Inject lateinit var pushManager: PushersManager
|
||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun bindPref() {
|
||||||
addPreferencesFromResource(R.xml.vector_settings_notifications)
|
findPreference(PreferencesManager.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY).let { pref ->
|
||||||
|
val pushRuleService = session
|
||||||
|
val mRuleMaster = pushRuleService.getPushRules()
|
||||||
|
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
|
||||||
|
|
||||||
|
if (mRuleMaster == null) {
|
||||||
|
pref.isVisible = false
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
val areNotifEnabledAtAccountLevelt = !mRuleMaster.enabled
|
||||||
val screenComponent = DaggerScreenComponent.factory().create(vectorActivity.getVectorComponent(), vectorActivity)
|
(pref as SwitchPreference).isChecked = areNotifEnabledAtAccountLevelt
|
||||||
super.onAttach(context)
|
}
|
||||||
screenComponent.inject(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
|
injector.inject(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
@ -56,7 +63,24 @@ class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment()
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||||
if (preference?.key == PreferencesManager.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) {
|
|
||||||
|
return when (preference?.key) {
|
||||||
|
PreferencesManager.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY -> {
|
||||||
|
updateEnabledForDevice(preference)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
PreferencesManager.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY -> {
|
||||||
|
updateEnabledForAccount(preference)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
return super.onPreferenceTreeClick(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEnabledForDevice(preference: Preference?) {
|
||||||
val switchPref = preference as SwitchPreference
|
val switchPref = preference as SwitchPreference
|
||||||
if (switchPref.isChecked) {
|
if (switchPref.isChecked) {
|
||||||
FcmHelper.getFcmToken(requireContext())?.let {
|
FcmHelper.getFcmToken(requireContext())?.let {
|
||||||
@ -68,16 +92,41 @@ class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment()
|
|||||||
FcmHelper.getFcmToken(requireContext())?.let {
|
FcmHelper.getFcmToken(requireContext())?.let {
|
||||||
pushManager.unregisterPusher(it, object : MatrixCallback<Unit> {
|
pushManager.unregisterPusher(it, object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
|
session.refreshPushers()
|
||||||
super.onSuccess(data)
|
super.onSuccess(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
super.onFailure(failure)
|
session.refreshPushers()
|
||||||
|
Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onPreferenceTreeClick(preference)
|
|
||||||
|
|
||||||
|
private fun updateEnabledForAccount(preference: Preference?) {
|
||||||
|
val pushRuleService = session
|
||||||
|
val switchPref = preference as SwitchPreference
|
||||||
|
pushRuleService.getPushRules()
|
||||||
|
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
|
||||||
|
?.let {
|
||||||
|
//Trick, we must enable this room to disable notifications
|
||||||
|
pushRuleService.updatePushRuleEnableStatus("override", it, !switchPref.isChecked,
|
||||||
|
object : MatrixCallback<Unit> {
|
||||||
|
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
pushRuleService.fetchPushRules()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
//revert the check box
|
||||||
|
switchPref.isChecked = !switchPref.isChecked
|
||||||
|
Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,6 +30,7 @@ import androidx.transition.TransitionManager
|
|||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.ScreenComponent
|
||||||
import im.vector.riotredesign.core.extensions.withArgs
|
import im.vector.riotredesign.core.extensions.withArgs
|
||||||
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||||
@ -56,15 +57,15 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||||||
// members
|
// members
|
||||||
@Inject lateinit var session: Session
|
@Inject lateinit var session: Session
|
||||||
@Inject lateinit var bugReporter: BugReporter
|
@Inject lateinit var bugReporter: BugReporter
|
||||||
|
@Inject lateinit var testManagerFactory: NotificationTroubleshootTestManagerFactory
|
||||||
|
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_settings_notifications_troubleshoot
|
override fun getLayoutResId() = R.layout.fragment_settings_notifications_troubleshoot
|
||||||
|
|
||||||
private var interactionListener: VectorSettingsFragmentInteractionListener? = null
|
private var interactionListener: VectorSettingsFragmentInteractionListener? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
super.onCreate(savedInstanceState)
|
injector.inject(this)
|
||||||
|
|
||||||
val appContext = activity!!.applicationContext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@ -92,9 +93,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||||||
|
|
||||||
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status,
|
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status,
|
||||||
0, 0)
|
0, 0)
|
||||||
|
testManager = testManagerFactory.create(this)
|
||||||
testManager = NotificationTroubleshootTestManagerFactory.createTestManager(this, session)
|
|
||||||
|
|
||||||
testManager?.statusListener = { troubleshootTestManager ->
|
testManager?.statusListener = { troubleshootTestManager ->
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
TransitionManager.beginDelayedTransition(mBottomView)
|
TransitionManager.beginDelayedTransition(mBottomView)
|
||||||
|
2821
vector/src/main/java/im/vector/riotredesign/features/settings/VectorSettingsPreferencesFragment.kt
Executable file → Normal file
@ -16,22 +16,20 @@
|
|||||||
|
|
||||||
package im.vector.riotredesign.features.settings
|
package im.vector.riotredesign.features.settings
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.extensions.withArgs
|
import im.vector.riotredesign.core.extensions.withArgs
|
||||||
import im.vector.riotredesign.core.platform.VectorPreferenceFragment
|
|
||||||
|
|
||||||
class VectorSettingsPreferencesFragmentV2 : VectorPreferenceFragment() {
|
class VectorSettingsRootFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
override var titleRes: Int = R.string.title_activity_settings
|
override var titleRes: Int = R.string.title_activity_settings
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_root
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun bindPref() {
|
||||||
addPreferencesFromResource(R.xml.vector_settings_preferences_root)
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance() = VectorSettingsPreferencesFragmentV2()
|
fun newInstance() = VectorSettingsRootFragment()
|
||||||
.withArgs {
|
.withArgs {
|
||||||
//putString(ARG_MATRIX_ID, matrixId)
|
//putString(ARG_MATRIX_ID, matrixId)
|
||||||
}
|
}
|
@ -0,0 +1,868 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import androidx.preference.SwitchPreference
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.extensions.getFingerprintHumanReadable
|
||||||
|
import im.vector.matrix.android.api.extensions.sortByLastSeen
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.dialogs.ExportKeysDialog
|
||||||
|
import im.vector.riotredesign.core.intent.ExternalIntentData
|
||||||
|
import im.vector.riotredesign.core.intent.analyseIntent
|
||||||
|
import im.vector.riotredesign.core.intent.getFilenameFromUri
|
||||||
|
import im.vector.riotredesign.core.platform.SimpleTextWatcher
|
||||||
|
import im.vector.riotredesign.core.preference.ProgressBarPreference
|
||||||
|
import im.vector.riotredesign.core.preference.VectorPreference
|
||||||
|
import im.vector.riotredesign.core.utils.*
|
||||||
|
import im.vector.riotredesign.features.crypto.keys.KeysExporter
|
||||||
|
import im.vector.riotredesign.features.crypto.keys.KeysImporter
|
||||||
|
import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupManageActivity
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.settings_security_and_privacy
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_security_privacy
|
||||||
|
|
||||||
|
// used to avoid requesting to enter the password for each deletion
|
||||||
|
private var mAccountPassword: String = ""
|
||||||
|
|
||||||
|
// devices: device IDs and device names
|
||||||
|
private var mDevicesNameList: List<DeviceInfo> = ArrayList()
|
||||||
|
|
||||||
|
private var mMyDeviceInfo: DeviceInfo? = null
|
||||||
|
|
||||||
|
|
||||||
|
// cryptography
|
||||||
|
private val mCryptographyCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
private val mCryptographyCategoryDivider by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
// cryptography manage
|
||||||
|
private val mCryptographyManageCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
private val mCryptographyManageCategoryDivider by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CRYPTOGRAPHY_MANAGE_DIVIDER_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
// displayed pushers
|
||||||
|
private val mPushersSettingsDivider by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_NOTIFICATIONS_TARGET_DIVIDER_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
private val mPushersSettingsCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
private val mDevicesListSettingsCategory by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_DEVICES_LIST_PREFERENCE_KEY) as PreferenceCategory
|
||||||
|
}
|
||||||
|
private val mDevicesListSettingsCategoryDivider by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_DEVICES_DIVIDER_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
private val cryptoInfoDeviceNamePreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_ENCRYPTION_INFORMATION_DEVICE_NAME_PREFERENCE_KEY) as VectorPreference
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cryptoInfoDeviceIdPreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_ENCRYPTION_INFORMATION_DEVICE_ID_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val manageBackupPref by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_SECURE_MESSAGE_RECOVERY_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val exportPref by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_ENCRYPTION_EXPORT_E2E_ROOM_KEYS_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val importPref by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_ENCRYPTION_IMPORT_E2E_ROOM_KEYS_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cryptoInfoTextPreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_ENCRYPTION_INFORMATION_DEVICE_KEY_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
// encrypt to unverified devices
|
||||||
|
private val sendToUnverifiedDevicesPref by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_ENCRYPTION_NEVER_SENT_TO_PREFERENCE_KEY) as SwitchPreference
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// Push target
|
||||||
|
refreshPushersList()
|
||||||
|
|
||||||
|
// Device list
|
||||||
|
refreshDevicesList()
|
||||||
|
|
||||||
|
//Refresh Key Management section
|
||||||
|
refreshKeysManagementSection()
|
||||||
|
|
||||||
|
// Analytics
|
||||||
|
|
||||||
|
// Analytics tracking management
|
||||||
|
(findPreference(PreferencesManager.SETTINGS_USE_ANALYTICS_KEY) as SwitchPreference).let {
|
||||||
|
// On if the analytics tracking is activated
|
||||||
|
it.isChecked = PreferencesManager.useAnalytics(requireContext())
|
||||||
|
|
||||||
|
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
PreferencesManager.setUseAnalytics(requireContext(), newValue as Boolean)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rageshake Management
|
||||||
|
(findPreference(PreferencesManager.SETTINGS_USE_RAGE_SHAKE_KEY) as SwitchPreference).let {
|
||||||
|
it.isChecked = PreferencesManager.useRageshake(requireContext())
|
||||||
|
|
||||||
|
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
PreferencesManager.setUseRageshake(requireContext(), newValue as Boolean)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
|
if (allGranted(grantResults)) {
|
||||||
|
if (requestCode == PERMISSION_REQUEST_CODE_EXPORT_KEYS) {
|
||||||
|
exportKeys()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
when (requestCode) {
|
||||||
|
REQUEST_E2E_FILE_REQUEST_CODE -> importKeys(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun refreshKeysManagementSection() {
|
||||||
|
//If crypto is not enabled parent section will be removed
|
||||||
|
//TODO notice that this will not work when no network
|
||||||
|
manageBackupPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
context?.let {
|
||||||
|
startActivity(KeysBackupManageActivity.intent(it))
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
exportPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
exportKeys()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
importPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
importKeys()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the e2e keys export.
|
||||||
|
*/
|
||||||
|
private fun exportKeys() {
|
||||||
|
// We need WRITE_EXTERNAL permission
|
||||||
|
if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, PERMISSION_REQUEST_CODE_EXPORT_KEYS)) {
|
||||||
|
activity?.let { activity ->
|
||||||
|
ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener {
|
||||||
|
override fun onPassphrase(passphrase: String) {
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
KeysExporter(session)
|
||||||
|
.export(requireContext(),
|
||||||
|
passphrase,
|
||||||
|
object : MatrixCallback<String> {
|
||||||
|
override fun onSuccess(data: String) {
|
||||||
|
if (isAdded) {
|
||||||
|
hideLoadingView()
|
||||||
|
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setMessage(getString(R.string.encryption_export_saved_as, data))
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
onCommonDone(failure.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the e2e keys import.
|
||||||
|
*/
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private fun importKeys() {
|
||||||
|
activity?.let { openFileSelection(it, this, false, REQUEST_E2E_FILE_REQUEST_CODE) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the e2e keys import.
|
||||||
|
*
|
||||||
|
* @param intent the intent result
|
||||||
|
*/
|
||||||
|
private fun importKeys(intent: Intent?) {
|
||||||
|
// sanity check
|
||||||
|
if (null == intent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val sharedDataItems = analyseIntent(intent)
|
||||||
|
val thisActivity = activity
|
||||||
|
|
||||||
|
if (sharedDataItems.isNotEmpty() && thisActivity != null) {
|
||||||
|
val sharedDataItem = sharedDataItems[0]
|
||||||
|
|
||||||
|
val uri = when (sharedDataItem) {
|
||||||
|
is ExternalIntentData.IntentDataUri -> sharedDataItem.uri
|
||||||
|
is ExternalIntentData.IntentDataClipData -> sharedDataItem.clipDataItem.uri
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val mimetype = when (sharedDataItem) {
|
||||||
|
is ExternalIntentData.IntentDataClipData -> sharedDataItem.mimeType
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val appContext = thisActivity.applicationContext
|
||||||
|
|
||||||
|
val filename = getFilenameFromUri(appContext, uri)
|
||||||
|
|
||||||
|
val dialogLayout = thisActivity.layoutInflater.inflate(R.layout.dialog_import_e2e_keys, null)
|
||||||
|
|
||||||
|
val textView = dialogLayout.findViewById<TextView>(R.id.dialog_e2e_keys_passphrase_filename)
|
||||||
|
|
||||||
|
if (filename.isNullOrBlank()) {
|
||||||
|
textView.isVisible = false
|
||||||
|
} else {
|
||||||
|
textView.isVisible = true
|
||||||
|
textView.text = getString(R.string.import_e2e_keys_from_file, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(thisActivity)
|
||||||
|
.setTitle(R.string.encryption_import_room_keys)
|
||||||
|
.setView(dialogLayout)
|
||||||
|
|
||||||
|
val passPhraseEditText = dialogLayout.findViewById<TextInputEditText>(R.id.dialog_e2e_keys_passphrase_edit_text)
|
||||||
|
val importButton = dialogLayout.findViewById<Button>(R.id.dialog_e2e_keys_import_button)
|
||||||
|
|
||||||
|
passPhraseEditText.addTextChangedListener(object : SimpleTextWatcher() {
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
importButton.isEnabled = !TextUtils.isEmpty(passPhraseEditText.text)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val importDialog = builder.show()
|
||||||
|
|
||||||
|
importButton.setOnClickListener(View.OnClickListener {
|
||||||
|
val password = passPhraseEditText.text.toString()
|
||||||
|
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
KeysImporter(session)
|
||||||
|
.import(requireContext(),
|
||||||
|
uri,
|
||||||
|
mimetype,
|
||||||
|
password,
|
||||||
|
object : MatrixCallback<ImportRoomKeysResult> {
|
||||||
|
override fun onSuccess(data: ImportRoomKeysResult) {
|
||||||
|
if (!isAdded) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hideLoadingView()
|
||||||
|
|
||||||
|
AlertDialog.Builder(thisActivity)
|
||||||
|
.setMessage(getString(R.string.encryption_import_room_keys_success,
|
||||||
|
data.successfullyNumberOfImportedKeys,
|
||||||
|
data.totalNumberOfKeys))
|
||||||
|
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
appContext.toast(failure.localizedMessage)
|
||||||
|
hideLoadingView()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
importDialog.dismiss()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// Cryptography
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
private fun removeCryptographyPreference() {
|
||||||
|
preferenceScreen.let {
|
||||||
|
it.removePreference(mCryptographyCategory)
|
||||||
|
it.removePreference(mCryptographyCategoryDivider)
|
||||||
|
|
||||||
|
// Also remove keys management section
|
||||||
|
it.removePreference(mCryptographyManageCategory)
|
||||||
|
it.removePreference(mCryptographyManageCategoryDivider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the cryptography preference section.
|
||||||
|
*
|
||||||
|
* @param aMyDeviceInfo the device info
|
||||||
|
*/
|
||||||
|
private fun refreshCryptographyPreference(aMyDeviceInfo: DeviceInfo?) {
|
||||||
|
val userId = session.sessionParams.credentials.userId
|
||||||
|
val deviceId = session.sessionParams.credentials.deviceId
|
||||||
|
|
||||||
|
// device name
|
||||||
|
if (null != aMyDeviceInfo) {
|
||||||
|
cryptoInfoDeviceNamePreference.summary = aMyDeviceInfo.displayName
|
||||||
|
|
||||||
|
cryptoInfoDeviceNamePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
displayDeviceRenameDialog(aMyDeviceInfo)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptoInfoDeviceNamePreference.onPreferenceLongClickListener = object : VectorPreference.OnPreferenceLongClickListener {
|
||||||
|
override fun onPreferenceLongClick(preference: Preference): Boolean {
|
||||||
|
activity?.let { copyToClipboard(it, aMyDeviceInfo.displayName!!) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// crypto section: device ID
|
||||||
|
if (!TextUtils.isEmpty(deviceId)) {
|
||||||
|
cryptoInfoDeviceIdPreference.summary = deviceId
|
||||||
|
|
||||||
|
cryptoInfoDeviceIdPreference.setOnPreferenceClickListener {
|
||||||
|
activity?.let { copyToClipboard(it, deviceId!!) }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// crypto section: device key (fingerprint)
|
||||||
|
if (!TextUtils.isEmpty(deviceId) && !TextUtils.isEmpty(userId)) {
|
||||||
|
val deviceInfo = session.getDeviceInfo(userId, deviceId)
|
||||||
|
|
||||||
|
if (null != deviceInfo && !TextUtils.isEmpty(deviceInfo.fingerprint())) {
|
||||||
|
cryptoInfoTextPreference.summary = deviceInfo.getFingerprintHumanReadable()
|
||||||
|
|
||||||
|
cryptoInfoTextPreference.setOnPreferenceClickListener {
|
||||||
|
deviceInfo.fingerprint()?.let {
|
||||||
|
copyToClipboard(requireActivity(), it)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendToUnverifiedDevicesPref.isChecked = false
|
||||||
|
|
||||||
|
sendToUnverifiedDevicesPref.isChecked = session.getGlobalBlacklistUnverifiedDevices()
|
||||||
|
|
||||||
|
sendToUnverifiedDevicesPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
session.setGlobalBlacklistUnverifiedDevices(sendToUnverifiedDevicesPref.isChecked)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// devices list
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
private fun removeDevicesPreference() {
|
||||||
|
preferenceScreen.let {
|
||||||
|
it.removePreference(mDevicesListSettingsCategory)
|
||||||
|
it.removePreference(mDevicesListSettingsCategoryDivider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the refresh of the devices list.<br></br>
|
||||||
|
* The devices list is the list of the devices where the user as looged in.
|
||||||
|
* It can be any mobile device, as any browser.
|
||||||
|
*/
|
||||||
|
private fun refreshDevicesList() {
|
||||||
|
if (session.isCryptoEnabled() && !TextUtils.isEmpty(session.sessionParams.credentials.deviceId)) {
|
||||||
|
// display a spinner while loading the devices list
|
||||||
|
if (0 == mDevicesListSettingsCategory.preferenceCount) {
|
||||||
|
activity?.let {
|
||||||
|
val preference = ProgressBarPreference(it)
|
||||||
|
mDevicesListSettingsCategory.addPreference(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getDevicesList(object : MatrixCallback<DevicesListResponse> {
|
||||||
|
override fun onSuccess(data: DevicesListResponse) {
|
||||||
|
if (!isAdded) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.devices?.isEmpty() == true) {
|
||||||
|
removeDevicesPreference()
|
||||||
|
} else {
|
||||||
|
buildDevicesSettings(data.devices!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
if (!isAdded) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDevicesPreference()
|
||||||
|
onCommonDone(failure.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
removeDevicesPreference()
|
||||||
|
removeCryptographyPreference()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the devices portion of the settings.<br></br>
|
||||||
|
* Each row correspond to a device ID and its corresponding device name. Clicking on the row
|
||||||
|
* display a dialog containing: the device ID, the device name and the "last seen" information.
|
||||||
|
*
|
||||||
|
* @param aDeviceInfoList the list of the devices
|
||||||
|
*/
|
||||||
|
private fun buildDevicesSettings(aDeviceInfoList: List<DeviceInfo>) {
|
||||||
|
var preference: VectorPreference
|
||||||
|
var typeFaceHighlight: Int
|
||||||
|
var isNewList = true
|
||||||
|
val myDeviceId = session.sessionParams.credentials.deviceId
|
||||||
|
|
||||||
|
if (aDeviceInfoList.size == mDevicesNameList.size) {
|
||||||
|
isNewList = !mDevicesNameList.containsAll(aDeviceInfoList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewList) {
|
||||||
|
var prefIndex = 0
|
||||||
|
mDevicesNameList = aDeviceInfoList
|
||||||
|
|
||||||
|
// sort before display: most recent first
|
||||||
|
mDevicesNameList.sortByLastSeen()
|
||||||
|
|
||||||
|
// start from scratch: remove the displayed ones
|
||||||
|
mDevicesListSettingsCategory.removeAll()
|
||||||
|
|
||||||
|
for (deviceInfo in mDevicesNameList) {
|
||||||
|
// set bold to distinguish current device ID
|
||||||
|
if (null != myDeviceId && myDeviceId == deviceInfo.deviceId) {
|
||||||
|
mMyDeviceInfo = deviceInfo
|
||||||
|
typeFaceHighlight = Typeface.BOLD
|
||||||
|
} else {
|
||||||
|
typeFaceHighlight = Typeface.NORMAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the edit text preference
|
||||||
|
preference = VectorPreference(requireActivity()).apply {
|
||||||
|
mTypeface = typeFaceHighlight
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == deviceInfo.deviceId && null == deviceInfo.displayName) {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if (null != deviceInfo.deviceId) {
|
||||||
|
preference.title = deviceInfo.deviceId
|
||||||
|
}
|
||||||
|
|
||||||
|
// display name parameter can be null (new JSON API)
|
||||||
|
if (null != deviceInfo.displayName) {
|
||||||
|
preference.summary = deviceInfo.displayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preference.key = DEVICES_PREFERENCE_KEY_BASE + prefIndex
|
||||||
|
prefIndex++
|
||||||
|
|
||||||
|
// onClick handler: display device details dialog
|
||||||
|
preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
displayDeviceDetailsDialog(deviceInfo)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
mDevicesListSettingsCategory.addPreference(preference)
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshCryptographyPreference(mMyDeviceInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a dialog containing the device ID, the device name and the "last seen" information.<>
|
||||||
|
* This dialog allow to delete the corresponding device (see [.displayDeviceDeletionDialog])
|
||||||
|
*
|
||||||
|
* @param aDeviceInfo the device information
|
||||||
|
*/
|
||||||
|
private fun displayDeviceDetailsDialog(aDeviceInfo: DeviceInfo) {
|
||||||
|
|
||||||
|
activity?.let {
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(it)
|
||||||
|
val inflater = it.layoutInflater
|
||||||
|
val layout = inflater.inflate(R.layout.dialog_device_details, null)
|
||||||
|
var textView = layout.findViewById<TextView>(R.id.device_id)
|
||||||
|
|
||||||
|
textView.text = aDeviceInfo.deviceId
|
||||||
|
|
||||||
|
// device name
|
||||||
|
textView = layout.findViewById(R.id.device_name)
|
||||||
|
val displayName = if (TextUtils.isEmpty(aDeviceInfo.displayName)) LABEL_UNAVAILABLE_DATA else aDeviceInfo.displayName
|
||||||
|
textView.text = displayName
|
||||||
|
|
||||||
|
// last seen info
|
||||||
|
textView = layout.findViewById(R.id.device_last_seen)
|
||||||
|
|
||||||
|
val lastSeenIp = aDeviceInfo.lastSeenIp?.takeIf { ip -> ip.isNotBlank() } ?: "-"
|
||||||
|
|
||||||
|
val lastSeenTime = aDeviceInfo.lastSeenTs?.let { ts ->
|
||||||
|
val dateFormatTime = SimpleDateFormat("HH:mm:ss")
|
||||||
|
val date = Date(ts)
|
||||||
|
|
||||||
|
val time = dateFormatTime.format(date)
|
||||||
|
val dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault())
|
||||||
|
|
||||||
|
dateFormat.format(date) + ", " + time
|
||||||
|
} ?: "-"
|
||||||
|
|
||||||
|
val lastSeenInfo = getString(R.string.devices_details_last_seen_format, lastSeenIp, lastSeenTime)
|
||||||
|
textView.text = lastSeenInfo
|
||||||
|
|
||||||
|
// title & icon
|
||||||
|
builder.setTitle(R.string.devices_details_dialog_title)
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_info)
|
||||||
|
.setView(layout)
|
||||||
|
.setPositiveButton(R.string.rename) { _, _ -> displayDeviceRenameDialog(aDeviceInfo) }
|
||||||
|
|
||||||
|
// disable the deletion for our own device
|
||||||
|
if (!TextUtils.equals(session.getMyDevice()?.deviceId, aDeviceInfo.deviceId)) {
|
||||||
|
builder.setNegativeButton(R.string.delete) { _, _ -> deleteDevice(aDeviceInfo) }
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setNeutralButton(R.string.cancel, null)
|
||||||
|
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
|
||||||
|
if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
dialog.cancel()
|
||||||
|
return@OnKeyListener true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display an alert dialog to rename a device
|
||||||
|
*
|
||||||
|
* @param aDeviceInfoToRename device info
|
||||||
|
*/
|
||||||
|
private fun displayDeviceRenameDialog(aDeviceInfoToRename: DeviceInfo) {
|
||||||
|
activity?.let {
|
||||||
|
val inflater = it.layoutInflater
|
||||||
|
val layout = inflater.inflate(R.layout.dialog_base_edit_text, null)
|
||||||
|
|
||||||
|
val input = layout.findViewById<EditText>(R.id.edit_text)
|
||||||
|
input.setText(aDeviceInfoToRename.displayName)
|
||||||
|
|
||||||
|
AlertDialog.Builder(it)
|
||||||
|
.setTitle(R.string.devices_details_device_name)
|
||||||
|
.setView(layout)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
displayLoadingView()
|
||||||
|
|
||||||
|
val newName = input.text.toString()
|
||||||
|
|
||||||
|
session.setDeviceName(aDeviceInfoToRename.deviceId!!, newName, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
hideLoadingView()
|
||||||
|
|
||||||
|
// search which preference is updated
|
||||||
|
val count = mDevicesListSettingsCategory.preferenceCount
|
||||||
|
|
||||||
|
for (i in 0 until count) {
|
||||||
|
val pref = mDevicesListSettingsCategory.getPreference(i)
|
||||||
|
|
||||||
|
if (TextUtils.equals(aDeviceInfoToRename.deviceId, pref.title)) {
|
||||||
|
pref.summary = newName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect if the updated device is the current account one
|
||||||
|
if (TextUtils.equals(cryptoInfoDeviceIdPreference.summary, aDeviceInfoToRename.deviceId)) {
|
||||||
|
cryptoInfoDeviceNamePreference.summary = newName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also change the display name in aDeviceInfoToRename, in case of multiple renaming
|
||||||
|
aDeviceInfoToRename.displayName = newName
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
onCommonDone(failure.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to delete a device.
|
||||||
|
*
|
||||||
|
* @param deviceInfo the device to delete
|
||||||
|
*/
|
||||||
|
private fun deleteDevice(deviceInfo: DeviceInfo) {
|
||||||
|
val deviceId = deviceInfo.deviceId
|
||||||
|
if (deviceId == null) {
|
||||||
|
Timber.e("## displayDeviceDeletionDialog(): sanity check failure")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
displayLoadingView()
|
||||||
|
session.deleteDevice(deviceId, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
hideLoadingView()
|
||||||
|
// force settings update
|
||||||
|
refreshDevicesList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
var isPasswordRequestFound = false
|
||||||
|
|
||||||
|
if (failure is Failure.RegistrationFlowError) {
|
||||||
|
// We only support LoginFlowTypes.PASSWORD
|
||||||
|
// Check if we can provide the user password
|
||||||
|
failure.registrationFlowResponse.flows?.forEach { interactiveAuthenticationFlow ->
|
||||||
|
isPasswordRequestFound = isPasswordRequestFound || interactiveAuthenticationFlow.stages?.any { it == LoginFlowTypes.PASSWORD } == true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPasswordRequestFound) {
|
||||||
|
maybeShowDeleteDeviceWithPasswordDialog(deviceId, failure.registrationFlowResponse.session)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPasswordRequestFound) {
|
||||||
|
// LoginFlowTypes.PASSWORD not supported, and this is the only one RiotX supports so far...
|
||||||
|
onCommonDone(failure.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a dialog to ask for user password, or use a previously entered password.
|
||||||
|
*/
|
||||||
|
private fun maybeShowDeleteDeviceWithPasswordDialog(deviceId: String, authSession: String?) {
|
||||||
|
if (!TextUtils.isEmpty(mAccountPassword)) {
|
||||||
|
deleteDeviceWithPassword(deviceId, authSession, mAccountPassword)
|
||||||
|
} else {
|
||||||
|
activity?.let {
|
||||||
|
val inflater = it.layoutInflater
|
||||||
|
val layout = inflater.inflate(R.layout.dialog_device_delete, null)
|
||||||
|
val passwordEditText = layout.findViewById<EditText>(R.id.delete_password)
|
||||||
|
|
||||||
|
AlertDialog.Builder(it)
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.setTitle(R.string.devices_delete_dialog_title)
|
||||||
|
.setView(layout)
|
||||||
|
.setPositiveButton(R.string.devices_delete_submit_button_label, DialogInterface.OnClickListener { _, _ ->
|
||||||
|
if (TextUtils.isEmpty(passwordEditText.toString())) {
|
||||||
|
it.toast(R.string.error_empty_field_your_password)
|
||||||
|
return@OnClickListener
|
||||||
|
}
|
||||||
|
mAccountPassword = passwordEditText.text.toString()
|
||||||
|
deleteDeviceWithPassword(deviceId, authSession, mAccountPassword)
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||||
|
hideLoadingView()
|
||||||
|
}
|
||||||
|
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
|
||||||
|
if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
dialog.cancel()
|
||||||
|
hideLoadingView()
|
||||||
|
return@OnKeyListener true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteDeviceWithPassword(deviceId: String, authSession: String?, accountPassword: String) {
|
||||||
|
session.deleteDeviceWithUserPassword(deviceId, authSession, accountPassword, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
hideLoadingView()
|
||||||
|
// force settings update
|
||||||
|
refreshDevicesList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
// Password is maybe not good
|
||||||
|
onCommonDone(failure.localizedMessage)
|
||||||
|
mAccountPassword = ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================================================
|
||||||
|
// pushers list management
|
||||||
|
//==============================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the pushers list
|
||||||
|
*/
|
||||||
|
private fun refreshPushersList() {
|
||||||
|
activity?.let { activity ->
|
||||||
|
/* TODO
|
||||||
|
val pushManager = Matrix.getInstance(activity).pushManager
|
||||||
|
val pushersList = ArrayList(pushManager.mPushersList)
|
||||||
|
|
||||||
|
if (pushersList.isEmpty()) {
|
||||||
|
preferenceScreen.removePreference(mPushersSettingsCategory)
|
||||||
|
preferenceScreen.removePreference(mPushersSettingsDivider)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check first if there is an update
|
||||||
|
var isNewList = true
|
||||||
|
if (pushersList.size == mDisplayedPushers.size) {
|
||||||
|
isNewList = !mDisplayedPushers.containsAll(pushersList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewList) {
|
||||||
|
// remove the displayed one
|
||||||
|
mPushersSettingsCategory.removeAll()
|
||||||
|
|
||||||
|
// add new emails list
|
||||||
|
mDisplayedPushers = pushersList
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
|
||||||
|
for (pushRule in mDisplayedPushers) {
|
||||||
|
if (null != pushRule.lang) {
|
||||||
|
val isThisDeviceTarget = TextUtils.equals(pushManager.currentRegistrationToken, pushRule.pushkey)
|
||||||
|
|
||||||
|
val preference = VectorPreference(activity).apply {
|
||||||
|
mTypeface = if (isThisDeviceTarget) Typeface.BOLD else Typeface.NORMAL
|
||||||
|
}
|
||||||
|
preference.title = pushRule.deviceDisplayName
|
||||||
|
preference.summary = pushRule.appDisplayName
|
||||||
|
preference.key = PUSHER_PREFERENCE_KEY_BASE + index
|
||||||
|
index++
|
||||||
|
mPushersSettingsCategory.addPreference(preference)
|
||||||
|
|
||||||
|
// the user cannot remove the self device target
|
||||||
|
if (!isThisDeviceTarget) {
|
||||||
|
preference.onPreferenceLongClickListener = object : VectorPreference.OnPreferenceLongClickListener {
|
||||||
|
override fun onPreferenceLongClick(preference: Preference): Boolean {
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setTitle(R.string.dialog_title_confirmation)
|
||||||
|
.setMessage(R.string.settings_delete_notification_targets_confirmation)
|
||||||
|
.setPositiveButton(R.string.remove)
|
||||||
|
{ _, _ ->
|
||||||
|
displayLoadingView()
|
||||||
|
pushManager.unregister(session, pushRule, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(info: Void?) {
|
||||||
|
refreshPushersList()
|
||||||
|
onCommonDone(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMatrixError(e: MatrixError) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnexpectedError(e: Exception) {
|
||||||
|
onCommonDone(e.localizedMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val REQUEST_E2E_FILE_REQUEST_CODE = 123
|
||||||
|
|
||||||
|
private const val PUSHER_PREFERENCE_KEY_BASE = "PUSHER_PREFERENCE_KEY_BASE"
|
||||||
|
private const val DEVICES_PREFERENCE_KEY_BASE = "DEVICES_PREFERENCE_KEY_BASE"
|
||||||
|
|
||||||
|
// TODO i18n
|
||||||
|
private const val LABEL_UNAVAILABLE_DATA = "none"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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.riotredesign.features.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.RingtoneManager
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.SwitchPreference
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.utils.getCallRingtoneName
|
||||||
|
import im.vector.riotredesign.core.utils.getCallRingtoneUri
|
||||||
|
import im.vector.riotredesign.core.utils.setCallRingtoneUri
|
||||||
|
import im.vector.riotredesign.core.utils.setUseRiotDefaultRingtone
|
||||||
|
|
||||||
|
class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
|
override var titleRes = R.string.preference_voice_and_video
|
||||||
|
override val preferenceXmlRes = R.xml.vector_settings_voice_video
|
||||||
|
|
||||||
|
private val mUseRiotCallRingtonePreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CALL_RINGTONE_USE_RIOT_PREFERENCE_KEY) as SwitchPreference
|
||||||
|
}
|
||||||
|
private val mCallRingtonePreference by lazy {
|
||||||
|
findPreference(PreferencesManager.SETTINGS_CALL_RINGTONE_URI_PREFERENCE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindPref() {
|
||||||
|
// Incoming call sounds
|
||||||
|
mUseRiotCallRingtonePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
activity?.let { setUseRiotDefaultRingtone(it, mUseRiotCallRingtonePreference.isChecked) }
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallRingtonePreference.let {
|
||||||
|
activity?.let { activity -> it.summary = getCallRingtoneName(activity) }
|
||||||
|
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
displayRingtonePicker()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
when (requestCode) {
|
||||||
|
REQUEST_CALL_RINGTONE -> {
|
||||||
|
val callRingtoneUri: Uri? = data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||||
|
val thisActivity = activity
|
||||||
|
if (callRingtoneUri != null && thisActivity != null) {
|
||||||
|
setCallRingtoneUri(thisActivity, callRingtoneUri)
|
||||||
|
mCallRingtonePreference.summary = getCallRingtoneName(thisActivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun displayRingtonePicker() {
|
||||||
|
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
||||||
|
putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getString(R.string.settings_call_ringtone_dialog_title))
|
||||||
|
putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)
|
||||||
|
putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
|
||||||
|
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE)
|
||||||
|
activity?.let { putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, getCallRingtoneUri(it)) }
|
||||||
|
}
|
||||||
|
startActivityForResult(intent, REQUEST_CALL_RINGTONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val REQUEST_CALL_RINGTONE = 999
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,29 +15,32 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.settings.troubleshoot
|
package im.vector.riotredesign.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.pushrules.RuleIds
|
import im.vector.matrix.android.api.pushrules.RuleIds
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the main pushRule (RULE_ID_DISABLE_ALL) is correctly setup
|
* Check that the main pushRule (RULE_ID_DISABLE_ALL) is correctly setup
|
||||||
*/
|
*/
|
||||||
class TestAccountSettings(val fragment: Fragment, val session: Session)
|
class TestAccountSettings @Inject constructor(private val stringProvider: StringProvider,
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder)
|
||||||
: TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) {
|
: TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
val defaultRule = session.getPushRules()
|
val defaultRule = session.getPushRules()
|
||||||
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
|
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
|
||||||
|
|
||||||
if (defaultRule != null) {
|
if (defaultRule != null) {
|
||||||
if (!defaultRule.enabled) {
|
if (!defaultRule.enabled) {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_account_settings_success)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_account_settings_success)
|
||||||
quickFix = null
|
quickFix = null
|
||||||
status = TestStatus.SUCCESS
|
status = TestStatus.SUCCESS
|
||||||
} else {
|
} else {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_account_settings_failed)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_account_settings_failed)
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_account_settings_quickfix) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_account_settings_quickfix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
|
if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
|
||||||
|
@ -15,21 +15,23 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.settings.troubleshoot
|
package im.vector.riotredesign.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import im.vector.matrix.android.api.pushrules.Action
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.pushrules.RuleIds
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.features.settings.PreferencesManager
|
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotredesign.features.settings.VectorSettingsFragmentInteractionListener
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.notifications.NotificationAction
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TestBingRulesSettings(val fragment: Fragment, val session: Session) : TroubleshootTest(R.string.settings_troubleshoot_test_bing_settings_title) {
|
class TestBingRulesSettings @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_bing_settings_title) {
|
||||||
|
|
||||||
|
private val testedRules =
|
||||||
|
listOf(RuleIds.RULE_ID_CONTAIN_DISPLAY_NAME,
|
||||||
|
RuleIds.RULE_ID_CONTAIN_USER_NAME,
|
||||||
|
RuleIds.RULE_ID_ONE_TO_ONE_ROOM,
|
||||||
|
RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS)
|
||||||
|
|
||||||
private val testedRules = emptyArray<String>()
|
|
||||||
/* TODO
|
|
||||||
arrayOf(BingRule.RULE_ID_CONTAIN_DISPLAY_NAME,
|
|
||||||
BingRule.RULE_ID_CONTAIN_USER_NAME,
|
|
||||||
BingRule.RULE_ID_ONE_TO_ONE_ROOM,
|
|
||||||
BingRule.RULE_ID_ALL_OTHER_MESSAGES_ROOMS)
|
|
||||||
*/
|
|
||||||
|
|
||||||
val ruleSettingsName = arrayOf(R.string.settings_containing_my_display_name,
|
val ruleSettingsName = arrayOf(R.string.settings_containing_my_display_name,
|
||||||
R.string.settings_containing_my_user_name,
|
R.string.settings_containing_my_user_name,
|
||||||
@ -37,44 +39,47 @@ class TestBingRulesSettings(val fragment: Fragment, val session: Session) : Trou
|
|||||||
R.string.settings_messages_in_group_chat)
|
R.string.settings_messages_in_group_chat)
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
val pushRules = null // TODO session.dataHandler.pushRules()
|
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
|
val pushRules = session.getPushRules()
|
||||||
if (pushRules == null) {
|
if (pushRules == null) {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_failed_to_load_rules)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_failed_to_load_rules)
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
} else {
|
} else {
|
||||||
var oneOrMoreRuleIsOff = false
|
var oneOrMoreRuleIsOff = false
|
||||||
var oneOrMoreRuleAreSilent = false
|
var oneOrMoreRuleAreSilent = false
|
||||||
for ((index, ruleId) in testedRules.withIndex()) {
|
for ((index, ruleId) in testedRules.withIndex()) {
|
||||||
/* TODO
|
pushRules.find { it.ruleId == ruleId }?.let { rule ->
|
||||||
pushRules.findDefaultRule(ruleId)?.let { rule ->
|
val actions = Action.mapFrom(rule) ?: return@let
|
||||||
if (!rule.isEnabled || rule.shouldNotNotify()) {
|
val notifAction = NotificationAction.extractFrom(actions)
|
||||||
|
if (!rule.enabled || !notifAction.shouldNotify) {
|
||||||
//off
|
//off
|
||||||
oneOrMoreRuleIsOff = true
|
oneOrMoreRuleIsOff = true
|
||||||
} else if (rule.notificationSound == null) {
|
} else if (notifAction.soundName == null) {
|
||||||
//silent
|
//silent
|
||||||
oneOrMoreRuleAreSilent = true
|
oneOrMoreRuleAreSilent = true
|
||||||
} else {
|
} else {
|
||||||
//noisy
|
//noisy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oneOrMoreRuleIsOff) {
|
if (oneOrMoreRuleIsOff) {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_failed)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_failed)
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_bing_settings_quickfix) {
|
//TODO
|
||||||
override fun doFix() {
|
// quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_bing_settings_quickfix) {
|
||||||
val activity = fragment.activity
|
// override fun doFix() {
|
||||||
if (activity is VectorSettingsFragmentInteractionListener) {
|
// val activity = fragment.activity
|
||||||
activity.requestHighlightPreferenceKeyOnResume(PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY)
|
// if (activity is VectorSettingsFragmentInteractionListener) {
|
||||||
}
|
// activity.requestHighlightPreferenceKeyOnResume(PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY)
|
||||||
activity?.supportFragmentManager?.popBackStack()
|
// }
|
||||||
}
|
// activity?.supportFragmentManager?.popBackStack()
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
} else {
|
} else {
|
||||||
if (oneOrMoreRuleAreSilent) {
|
if (oneOrMoreRuleAreSilent) {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_success_with_warn)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_success_with_warn)
|
||||||
} else {
|
} else {
|
||||||
description = null
|
description = null
|
||||||
}
|
}
|
||||||
|
@ -15,33 +15,33 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.settings.troubleshoot
|
package im.vector.riotredesign.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.features.settings.PreferencesManager
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if notifications are enable in the system settings for this app.
|
* Checks if notifications are enable in the system settings for this app.
|
||||||
*/
|
*/
|
||||||
class TestDeviceSettings(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
|
class TestDeviceSettings @Inject constructor(private val context: AppCompatActivity,
|
||||||
|
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
/* TODO
|
|
||||||
val pushManager = Matrix.getInstance(fragment.activity).pushManager
|
if (PreferencesManager.areNotificationEnabledForDevice(context)) {
|
||||||
if (pushManager.areDeviceNotificationsAllowed()) {
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success)
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_device_settings_success)
|
|
||||||
quickFix = null
|
quickFix = null
|
||||||
status = TestStatus.SUCCESS
|
status = TestStatus.SUCCESS
|
||||||
} else {
|
} else {
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
pushManager.setDeviceNotificationsAllowed(true)
|
PreferencesManager.setNotificationEnabledForDevice(context, true)
|
||||||
manager?.retry()
|
manager?.retry()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_device_settings_failed)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed)
|
||||||
status = TestStatus.FAILED
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
status = TestStatus.FAILED
|
status = TestStatus.FAILED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
@ -15,27 +15,30 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotredesign.features.settings.troubleshoot
|
package im.vector.riotredesign.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
import im.vector.riotredesign.core.utils.startNotificationSettingsIntent
|
import im.vector.riotredesign.core.utils.startNotificationSettingsIntent
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if notifications are enable in the system settings for this app.
|
* Checks if notifications are enable in the system settings for this app.
|
||||||
*/
|
*/
|
||||||
class TestSystemSettings(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
|
class TestSystemSettings @Inject constructor(private val context: AppCompatActivity,
|
||||||
|
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
|
||||||
|
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if (NotificationManagerCompat.from(fragment.context!!).areNotificationsEnabled()) {
|
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_system_settings_success)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success)
|
||||||
quickFix = null
|
quickFix = null
|
||||||
status = TestStatus.SUCCESS
|
status = TestStatus.SUCCESS
|
||||||
} else {
|
} else {
|
||||||
description = fragment.getString(R.string.settings_troubleshoot_test_system_settings_failed)
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed)
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
|
quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
|
if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
|
||||||
startNotificationSettingsIntent(fragment, NotificationTroubleshootTestManager.REQ_CODE_FIX)
|
startNotificationSettingsIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:pathData="m103.426,146.731c10.234,10.234 27.539,9.521 38.653,-1.592s11.826,-28.419 1.592,-38.653c-10.234,-10.234 -72.627,-72.746 -72.627,-72.746l-7.951,9.069s1.497,6.213 -0.319,8.284c-1.816,2.071 -9.701,3.147 -9.701,3.147l-16.509,18.833s-1.861,4.935 -1.092,5.704z"
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:fillAlpha=".7"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m47.713,42.317v7.766l7.886,-0.008c0.1,0 0.192,-0.003 0.282,-0.009 2.073,-0.137 3.694,-1.837 3.694,-3.87 0,-2.139 -1.78,-3.879 -3.969,-3.879zM39.915,80.675c-4.307,0 -7.799,-3.413 -7.799,-7.623v-14.528c-0.028,-0.263 -0.043,-0.531 -0.043,-0.802 -0,-0.276 0.014,-0.549 0.043,-0.817v-22.21c0,-4.21 3.492,-7.624 7.799,-7.624h15.692c10.789,0 19.567,8.58 19.567,19.126 0,10.027 -8.012,18.408 -18.24,19.081 -0.435,0.029 -0.883,0.044 -1.327,0.044l-7.893,0.008v7.723c0,4.21 -3.491,7.623 -7.799,7.623z"
|
||||||
|
android:fillColor="#a2ddef"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m47.713,42.317v7.766l7.886,-0.008c0.1,0 0.192,-0.003 0.282,-0.009 2.073,-0.137 3.694,-1.837 3.694,-3.87 0,-2.139 -1.78,-3.879 -3.969,-3.879zM39.915,80.675c-4.307,0 -7.799,-3.413 -7.799,-7.623v-38.358c0,-4.21 3.492,-7.624 7.799,-7.624h15.692c10.789,0 19.567,8.58 19.567,19.126 0,10.027 -8.012,18.408 -18.24,19.081 -0.435,0.029 -0.883,0.044 -1.327,0.044l-7.893,0.008v7.723c0,4.21 -3.491,7.623 -7.799,7.623z"
|
||||||
|
android:strokeWidth="1.372009"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#368bd6"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m39.915,73.052v-38.358h15.692c6.499,0 11.767,5.15 11.767,11.502 0,6.089 -4.84,11.073 -10.964,11.476 -0.266,0.018 -0.533,0.027 -0.803,0.027h-15.692"
|
||||||
|
android:strokeWidth="1.372009"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#368bd6"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m46.341,30.322c1.196,1.668 1.655,3.691 1.293,5.695 -0.362,2.005 -1.502,3.752 -3.21,4.92 -3.525,2.411 -8.402,1.572 -10.87,-1.871 -1.196,-1.668 -1.655,-3.691 -1.293,-5.696 0.362,-2.005 1.502,-3.752 3.21,-4.92 3.525,-2.411 8.401,-1.572 10.869,1.871zM67.439,80.671c-2.458,0 -4.876,-1.132 -6.394,-3.249l-11.022,-15.375c-2.472,-3.448 -1.616,-8.202 1.911,-10.617 3.527,-2.417 8.39,-1.58 10.862,1.868l11.022,15.375c2.472,3.448 1.616,8.202 -1.911,10.617 -1.362,0.933 -2.923,1.381 -4.469,1.381z"
|
||||||
|
android:fillColor="#368bd6"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
BIN
vector/src/main/res/drawable-hdpi/ic_status_bar.png
Normal file
After Width: | Height: | Size: 908 B |
Before Width: | Height: | Size: 1022 B |
BIN
vector/src/main/res/drawable-mdpi/ic_status_bar.png
Normal file
After Width: | Height: | Size: 610 B |
Before Width: | Height: | Size: 596 B |
Before Width: | Height: | Size: 41 KiB |
@ -1,34 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108">
|
|
||||||
<path
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="78.5885"
|
|
||||||
android:endY="90.9159"
|
|
||||||
android:startX="48.7653"
|
|
||||||
android:startY="61.0927"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1" />
|
|
||||||
</vector>
|
|
BIN
vector/src/main/res/drawable-xhdpi/ic_status_bar.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
BIN
vector/src/main/res/drawable-xxhdpi/ic_status_bar.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.1 KiB |
BIN
vector/src/main/res/drawable-xxxhdpi/ic_status_bar.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.0 KiB |
@ -1,170 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportHeight="108"
|
android:viewportWidth="108"
|
||||||
android:viewportWidth="108">
|
android:viewportHeight="108">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#26A69A"
|
android:pathData="m0,0h108v108h-108z"
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
android:fillColor="#27303a"
|
||||||
<path
|
android:fillType="evenOdd"/>
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M9,0L9,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,0L19,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,0L29,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,0L39,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,0L49,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,0L59,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,0L69,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,0L79,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M89,0L89,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M99,0L99,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,9L108,9"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,19L108,19"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,29L108,29"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,39L108,39"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,49L108,49"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,59L108,59"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,69L108,69"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,79L108,79"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,89L108,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,99L108,99"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,29L89,29"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,39L89,39"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,49L89,49"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,59L89,59"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,69L89,69"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,79L89,79"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,19L29,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,19L39,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,19L49,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,19L59,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,19L69,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,19L79,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="14dp"
|
|
||||||
android:height="14dp"
|
|
||||||
android:viewportWidth="14"
|
|
||||||
android:viewportHeight="14">
|
|
||||||
<path
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:pathData="M7,7m-1.636,0a1.636,1.636 0,1 1,3.272 0a1.636,1.636 0,1 1,-3.272 0"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:strokeColor="#454545"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
<path
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:pathData="M11.036,8.636a0.9,0.9 0,0 0,0.18 0.993l0.033,0.033a1.09,1.09 0,1 1,-1.544 1.543l-0.032,-0.032a0.9,0.9 0,0 0,-0.993 -0.18,0.9 0.9,0 0,0 -0.545,0.823v0.093a1.09,1.09 0,1 1,-2.182 0v-0.049a0.9,0.9 0,0 0,-0.59 -0.824,0.9 0.9,0 0,0 -0.992,0.18l-0.033,0.033a1.09,1.09 0,1 1,-1.543 -1.544l0.032,-0.032a0.9,0.9 0,0 0,0.18 -0.993,0.9 0.9,0 0,0 -0.823,-0.545L2.09,8.135a1.09,1.09 0,1 1,0 -2.182h0.049a0.9,0.9 0,0 0,0.824 -0.59,0.9 0.9,0 0,0 -0.18,-0.992l-0.033,-0.033a1.09,1.09 0,1 1,1.544 -1.543l0.032,0.032a0.9,0.9 0,0 0,0.993 0.18h0.044a0.9,0.9 0,0 0,0.545 -0.823L5.908,2.09a1.09,1.09 0,0 1,2.182 0v0.049a0.9,0.9 0,0 0,0.545 0.824,0.9 0.9,0 0,0 0.993,-0.18l0.033,-0.033a1.09,1.09 0,1 1,1.543 1.544l-0.032,0.032a0.9,0.9 0,0 0,-0.18 0.993v0.044a0.9,0.9 0,0 0,0.823 0.545h0.093a1.09,1.09 0,1 1,0 2.182h-0.049a0.9,0.9 0,0 0,-0.824 0.545z"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:strokeColor="#454545"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
</vector>
|
|
@ -1,14 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="11dp"
|
|
||||||
android:height="14dp"
|
|
||||||
android:viewportWidth="11"
|
|
||||||
android:viewportHeight="14">
|
|
||||||
<path
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:pathData="M0.588,8.8s0.588,-0.6 2.353,-0.6c1.765,0 2.941,1.2 4.706,1.2C9.412,9.4 10,8.8 10,8.8V1.6s-0.588,0.6 -2.353,0.6C5.882,2.2 4.706,1 2.941,1c-1.765,0 -2.353,0.6 -2.353,0.6v7.2zM0.588,13V8.8"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:strokeColor="#454545"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
</vector>
|
|
14
vector/src/main/res/drawable/ic_settings_root_call.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="22dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="22"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M21,15.92v3a2,2 0,0 1,-2.18 2,19.79 19.79,0 0,1 -8.63,-3.07 19.5,19.5 0,0 1,-6 -6,19.79 19.79,0 0,1 -3.07,-8.67A2,2 0,0 1,3.11 1h3a2,2 0,0 1,2 1.72c0.127,0.96 0.361,1.903 0.7,2.81a2,2 0,0 1,-0.45 2.11L7.09,8.91a16,16 0,0 0,6 6l1.27,-1.27a2,2 0,0 1,2.11 -0.45c0.907,0.339 1.85,0.573 2.81,0.7A2,2 0,0 1,21 15.92z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
22
vector/src/main/res/drawable/ic_settings_root_flair.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M8,8m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M4.21,13.89L3,23l5,-3 5,3 -1.21,-9.12"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
22
vector/src/main/res/drawable/ic_settings_root_general.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.4,15a1.65,1.65 0,0 0,0.33 1.82l0.06,0.06a2,2 0,1 1,-2.83 2.83l-0.06,-0.06a1.65,1.65 0,0 0,-1.82 -0.33,1.65 1.65,0 0,0 -1,1.51L14.08,21a2,2 0,1 1,-4 0v-0.09A1.65,1.65 0,0 0,9 19.4a1.65,1.65 0,0 0,-1.82 0.33l-0.06,0.06a2,2 0,1 1,-2.83 -2.83l0.06,-0.06a1.65,1.65 0,0 0,0.33 -1.82,1.65 1.65,0 0,0 -1.51,-1L3,14.08a2,2 0,1 1,0 -4h0.09A1.65,1.65 0,0 0,4.6 9a1.65,1.65 0,0 0,-0.33 -1.82l-0.06,-0.06a2,2 0,1 1,2.83 -2.83l0.06,0.06a1.65,1.65 0,0 0,1.82 0.33L9,4.68a1.65,1.65 0,0 0,1 -1.51L10,3a2,2 0,1 1,4 0v0.09a1.65,1.65 0,0 0,1 1.51,1.65 1.65,0 0,0 1.82,-0.33l0.06,-0.06a2,2 0,1 1,2.83 2.83l-0.06,0.06a1.65,1.65 0,0 0,-0.33 1.82L19.32,9c0.26,0.604 0.852,0.997 1.51,1L21,10a2,2 0,1 1,0 4h-0.09a1.65,1.65 0,0 0,-1.51 1z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
30
vector/src/main/res/drawable/ic_settings_root_help_about.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="22dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="22"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M11,11m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M8.09,8C8.5754,6.62 9.9854,5.7914 11.4272,6.0387C12.869,6.286 13.9222,7.5372 13.92,9C13.92,11 10.92,12 10.92,12"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.0002,15.9913L11,15.9913"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="17dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="17"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M16,19v-2a4,4 0,0 0,-4 -4H5a4,4 0,0 0,-4 4v2"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M8.5,5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M5.672,2.172l5.656,5.656"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
14
vector/src/main/res/drawable/ic_settings_root_labs.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="18"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M1,14s1,-1 4,-1 5,2 8,2 4,-1 4,-1V2s-1,1 -4,1 -5,-2 -8,-2 -4,1 -4,1v12zM1,21v-7"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
14
vector/src/main/res/drawable/ic_settings_root_legacy.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M21,6v13H3V6M1,1h22v5H1zM10,10h4"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M16,7A6,6 0,1 0,4 7c0,7 -3,9 -3,9h18s-3,-2 -3,-9M11.73,20a2,2 0,0 1,-3.46 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M4,19v-7M4,8V1M12,19v-9M12,6V1M20,19v-5M20,10V1M1,12h6M9,6h6M17,14h6"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -0,0 +1,22 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="22dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="22">
|
||||||
|
<path
|
||||||
|
android:pathData="M3,10L17,10A2,2 0,0 1,19 12L19,19A2,2 0,0 1,17 21L3,21A2,2 0,0 1,1 19L1,12A2,2 0,0 1,3 10z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M5,10L5,6a5,5 0,1 1,10 0v4"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#7E899C"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -27,7 +27,8 @@
|
|||||||
android:id="@+id/logoImageView"
|
android:id="@+id/logoImageView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:src="@drawable/logo_login" />
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:src="@drawable/riot_splash_0_blue" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
style="@style/VectorTextInputLayout"
|
style="@style/VectorTextInputLayout"
|
||||||
|
@ -116,12 +116,12 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:hint="@string/room_message_placeholder_not_encrypted"
|
|
||||||
android:nextFocusLeft="@id/composerEditText"
|
android:nextFocusLeft="@id/composerEditText"
|
||||||
android:nextFocusUp="@id/composerEditText"
|
android:nextFocusUp="@id/composerEditText"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:textColor="?vctr_message_text_color"
|
android:textColor="?vctr_message_text_color"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
|
tools:hint="@string/room_message_placeholder_not_encrypted"
|
||||||
tools:ignore="MissingConstraints" />
|
tools:ignore="MissingConstraints" />
|
||||||
|
|
||||||
</merge>
|
</merge>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background" />
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background" />
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 7.0 KiB |