Merge branch 'develop' into feature/dagger
|
@ -61,4 +61,6 @@ interface PushersService {
|
|||
}
|
||||
|
||||
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> {
|
||||
|
||||
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>) {
|
||||
// TODO Use const for "global"
|
||||
val rules = defaultPushRuleService.getPushRules("global")
|
||||
inserted.map { it.asDomain() }
|
||||
.filter { it.senderId != sessionParams.credentials.userId }
|
||||
|
|
|
@ -112,4 +112,8 @@ internal class DefaultPusherService @Inject constructor(
|
|||
{ 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'
|
||||
|
||||
// gplay flavor only
|
||||
gplayImplementation 'com.google.firebase:firebase-core:16.0.8'
|
||||
gplayImplementation 'com.google.firebase:firebase-messaging:17.5.0'
|
||||
gplayImplementation('com.google.firebase:firebase-messaging:19.0.1') {
|
||||
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
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<application>
|
||||
|
||||
<receiver android:name=".receiver.OnApplicationUpgradeOrRebootReceiver">
|
||||
<receiver android:name="im.vector.riotredesign.fdroid.receiver.OnApplicationUpgradeOrRebootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
@ -16,10 +16,14 @@
|
|||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".core.services.AlarmSyncBroadcastReceiver"
|
||||
android:name="im.vector.riotredesign.fdroid.receiver.AlarmSyncBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".fdroid.receiver.service.VectorSyncService"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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 im.vector.riotredesign.R
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.net.ConnectivityManager
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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 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.PendingIntent
|
||||
|
@ -9,6 +25,7 @@ import android.os.Build
|
|||
import android.os.PowerManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncService
|
||||
import im.vector.riotredesign.fdroid.service.VectorSyncService
|
||||
import timber.log.Timber
|
||||
|
||||
class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||
|
@ -48,15 +65,14 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_CODE = 0
|
||||
private const val REQUEST_CODE = 0
|
||||
|
||||
fun scheduleAlarm(context: Context, userId: String, delay: Long) {
|
||||
//Reschedule
|
||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
|
||||
putExtra(SyncService.EXTRA_USER_ID, userId)
|
||||
}
|
||||
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val firstMillis = System.currentTimeMillis() + delay
|
||||
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
@ -68,8 +84,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
|||
|
||||
fun cancelAlarm(context: Context) {
|
||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
|
||||
val pIntent = PendingIntent.getBroadcast(context, AlarmSyncBroadcastReceiver.REQUEST_CODE,
|
||||
intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
alarmMgr.cancel(pIntent)
|
||||
}
|
|
@ -15,21 +15,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.receiver
|
||||
package im.vector.riotredesign.fdroid.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import im.vector.riotredesign.core.di.HasVectorInjector
|
||||
import im.vector.riotredesign.core.services.AlarmSyncBroadcastReceiver
|
||||
import timber.log.Timber
|
||||
|
||||
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Timber.v("## onReceive() ${intent.action}")
|
||||
if (context is HasVectorInjector) {
|
||||
val activeSession = context.injector().activeSessionHolder().getSafeActiveSession()
|
||||
val appContext = context.applicationContext
|
||||
if (appContext is HasVectorInjector) {
|
||||
val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
|
||||
if (activeSession != null) {
|
||||
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.myUserId, 10)
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotredesign.core.services
|
||||
package im.vector.riotredesign.fdroid.service
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
|
@ -28,12 +28,12 @@ import timber.log.Timber
|
|||
class VectorSyncService : SyncService() {
|
||||
|
||||
override fun onCreate() {
|
||||
Timber.v("VectorSyncService - onCreate ")
|
||||
Timber.v("VectorSyncService - onCreate")
|
||||
super.onCreate()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Timber.v("VectorSyncService - onDestroy ")
|
||||
Timber.v("VectorSyncService - onDestroy")
|
||||
removeForegroundNotif()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
@ -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 {
|
||||
Timber.v("VectorSyncService - onBind ")
|
|
@ -18,9 +18,15 @@ package im.vector.riotredesign.push.fcm
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
|
||||
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||
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 {
|
||||
|
||||
fun isPushSupported(): Boolean = false
|
||||
|
@ -52,4 +58,17 @@ object FcmHelper {
|
|||
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager) {
|
||||
// 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 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.push.fcm.troubleshoot.TestAutoStartBoot
|
||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestBackgroundRestrictions
|
||||
|
||||
class NotificationTroubleshootTestManagerFactory {
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
android:name="firebase_analytics_collection_deactivated"
|
||||
android:value="true" />
|
||||
|
||||
<service android:name=".push.fcm.VectorFirebaseMessagingService">
|
||||
<service android:name=".gplay.push.fcm.VectorFirebaseMessagingService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</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
|
||||
* 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.GoogleApiAvailability
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.settings.troubleshoot.TroubleshootTest
|
||||
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.
|
||||
*/
|
||||
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() {
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(fragment.context)
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||
if (resultCode == ConnectionResult.SUCCESS) {
|
||||
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
|
||||
} else {
|
||||
if (apiAvailability.isUserResolvableError(resultCode)) {
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_play_services_quickfix) {
|
||||
override fun doFix() {
|
||||
fragment.activity?.let {
|
||||
apiAvailability.getErrorDialog(it, resultCode, 9000 /*hey does the magic number*/).show()
|
||||
}
|
||||
apiAvailability.getErrorDialog(context, resultCode, 9000 /*hey does the magic number*/).show()
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.push.fcm
|
||||
package im.vector.riotredesign.gplay.push.fcm
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
|
@ -26,11 +26,12 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
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.events.model.Event
|
||||
import im.vector.riotredesign.BuildConfig
|
||||
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.pushers.PushersManager
|
||||
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.NotificationDrawerManager
|
||||
import im.vector.riotredesign.features.notifications.SimpleNotifiableEvent
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
import im.vector.riotredesign.features.settings.PreferencesManager
|
||||
import org.koin.android.ext.android.inject
|
||||
>>>>>>> develop
|
||||
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Class extending FirebaseMessagingService.
|
||||
*/
|
||||
class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||
|
||||
<<<<<<< HEAD
|
||||
@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
|
||||
private val mUIHandler by lazy {
|
||||
Handler(Looper.getMainLooper())
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
vectorComponent().inject(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when message is received.
|
||||
*
|
||||
|
@ -103,7 +100,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
* you retrieve the token.
|
||||
*/
|
||||
override fun onNewToken(refreshedToken: String?) {
|
||||
if (Matrix.getInstance().currentSession == null) return
|
||||
Timber.i("onNewToken: FCM Token has been updated")
|
||||
FcmHelper.storeFcmToken(this, refreshedToken)
|
||||
if (refreshedToken == null) {
|
||||
|
@ -149,7 +145,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
val unreadCount = data.get("unread")?.let { Integer.parseInt(it) } ?: 0
|
||||
BadgeProxy.updateBadgeCount(applicationContext, unreadCount)
|
||||
|
||||
val session = safeGetCurrentSession()
|
||||
val session = activeSessionHolder.getSafeActiveSession()
|
||||
|
||||
if (session == null) {
|
||||
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
|
||||
// a previous catchup might have already retrieved the notified event
|
||||
private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean {
|
||||
if (null != eventId && null != roomId) {
|
||||
try {
|
||||
val session = safeGetCurrentSession() ?: return false
|
||||
val session = activeSessionHolder.getSafeActiveSession() ?: return false
|
||||
val room = session.getRoom(roomId) ?: return false
|
||||
return room.getTimeLineEvent(eventId) != null
|
||||
} catch (e: Exception) {
|
||||
|
@ -241,7 +228,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
if (notifiableEvent is NotifiableMessageEvent) {
|
||||
if (TextUtils.isEmpty(notifiableEvent.senderName)) {
|
||||
notifiableEvent.senderName = data["sender_display_name"]
|
||||
?: data["sender"] ?: ""
|
||||
?: data["sender"] ?: ""
|
||||
}
|
||||
if (TextUtils.isEmpty(notifiableEvent.roomName)) {
|
||||
notifiableEvent.roomName = findRoomNameBestEffort(data, session) ?: ""
|
||||
|
@ -287,11 +274,11 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
|
||||
try {
|
||||
return Event(eventId = data["event_id"],
|
||||
senderId = data["sender"],
|
||||
roomId = data["room_id"],
|
||||
type = data.getValue("type"),
|
||||
senderId = data["sender"],
|
||||
roomId = data["room_id"],
|
||||
type = data.getValue("type"),
|
||||
// TODO content = data.getValue("content"),
|
||||
originServerTs = System.currentTimeMillis())
|
||||
originServerTs = System.currentTimeMillis())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "buildEvent fails ")
|
||||
}
|
|
@ -25,6 +25,7 @@ import com.google.android.gms.common.ConnectionResult
|
|||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.firebase.iid.FirebaseInstanceId
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||
import im.vector.riotredesign.core.pushers.PushersManager
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -99,4 +100,12 @@ object FcmHelper {
|
|||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity)
|
||||
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
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.features.settings.troubleshoot.*
|
||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestFirebaseToken
|
||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestPlayServices
|
||||
import im.vector.riotredesign.push.fcm.troubleshoot.TestTokenRegistration
|
||||
import im.vector.riotredesign.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||
import im.vector.riotredesign.features.settings.troubleshoot.TestAccountSettings
|
||||
import im.vector.riotredesign.features.settings.troubleshoot.TestBingRulesSettings
|
||||
import im.vector.riotredesign.features.settings.troubleshoot.TestDeviceSettings
|
||||
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 createTestManager(fragment: Fragment, session: Session?): NotificationTroubleshootTestManager {
|
||||
val mgr = NotificationTroubleshootTestManager(fragment)
|
||||
mgr.addTest(TestSystemSettings(fragment))
|
||||
if (session != null) {
|
||||
mgr.addTest(TestAccountSettings(fragment, session))
|
||||
}
|
||||
mgr.addTest(TestDeviceSettings(fragment))
|
||||
if (session != null) {
|
||||
mgr.addTest(TestBingRulesSettings(fragment, session))
|
||||
}
|
||||
mgr.addTest(TestPlayServices(fragment))
|
||||
mgr.addTest(TestFirebaseToken(fragment))
|
||||
mgr.addTest(TestTokenRegistration(fragment))
|
||||
return mgr
|
||||
}
|
||||
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
|
||||
val mgr = NotificationTroubleshootTestManager(fragment)
|
||||
mgr.addTest(testSystemSettings)
|
||||
mgr.addTest(testAccountSettings)
|
||||
mgr.addTest(testDeviceSettings)
|
||||
mgr.addTest(testBingRulesSettings)
|
||||
mgr.addTest(testPlayServices)
|
||||
mgr.addTest(testFirebaseToken)
|
||||
mgr.addTest(testTokenRegistration)
|
||||
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
|
||||
android:name=".core.services.CallService"
|
||||
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 -->
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ import im.vector.riotredesign.core.di.DaggerVectorComponent
|
|||
import im.vector.riotredesign.core.di.HasVectorInjector
|
||||
import im.vector.riotredesign.core.di.VectorComponent
|
||||
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.lifecycle.VectorActivityLifecycleCallbacks
|
||||
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.rageshake.VectorFileLogger
|
||||
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.push.fcm.FcmHelper
|
||||
import timber.log.Timber
|
||||
|
@ -113,7 +111,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
fun entersForeground() {
|
||||
AlarmSyncBroadcastReceiver.cancelAlarm(appContext)
|
||||
FcmHelper.onEnterForeground(appContext)
|
||||
activeSessionHolder.getSafeActiveSession()?.also {
|
||||
it.stopAnyBackgroundSync()
|
||||
}
|
||||
|
@ -123,19 +121,8 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||
fun entersBackground() {
|
||||
Timber.i("App entered background") // call persistInfo
|
||||
notificationDrawerManager.persistInfo()
|
||||
if (FcmHelper.isPushSupported()) {
|
||||
//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")
|
||||
}
|
||||
}
|
||||
FcmHelper.onEnterBackground(appContext, activeSessionHolder)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import dagger.BindsInstance
|
|||
import dagger.Component
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
|
||||
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.crypto.keysbackup.restore.KeysBackupRestoreFromKeyFragment
|
||||
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.settings.VectorSettingsActivity
|
||||
import im.vector.riotredesign.features.settings.VectorSettingsNotificationPreferenceFragment
|
||||
import im.vector.riotredesign.features.settings.VectorSettingsNotificationsTroubleshootFragment
|
||||
import im.vector.riotredesign.features.settings.VectorSettingsPreferencesFragment
|
||||
|
||||
@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
|
||||
|
@ -122,8 +124,6 @@ interface ScreenComponent {
|
|||
|
||||
fun inject(mainActivity: MainActivity)
|
||||
|
||||
fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)
|
||||
|
||||
fun inject(roomDirectoryActivity: RoomDirectoryActivity)
|
||||
|
||||
fun inject(bugReportActivity: BugReportActivity)
|
||||
|
@ -136,6 +136,12 @@ interface ScreenComponent {
|
|||
|
||||
fun inject(vectorSettingsNotificationPreferenceFragment: VectorSettingsNotificationPreferenceFragment)
|
||||
|
||||
fun inject(vectorSettingsPreferencesFragment: VectorSettingsPreferencesFragment)
|
||||
|
||||
fun inject(userAvatarPreference: UserAvatarPreference)
|
||||
|
||||
fun inject(vectorSettingsNotificationsTroubleshootFragment: VectorSettingsNotificationsTroubleshootFragment)
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
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.RageShake
|
||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotredesign.gplay.push.fcm.VectorFirebaseMessagingService
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Component(modules = [VectorModule::class])
|
||||
|
@ -49,6 +50,8 @@ interface VectorComponent {
|
|||
|
||||
fun inject(vectorApplication: VectorApplication)
|
||||
|
||||
fun inject(vectorFirebaseMessagingService: VectorFirebaseMessagingService)
|
||||
|
||||
fun matrix(): Matrix
|
||||
|
||||
fun currentSession(): Session
|
||||
|
@ -61,6 +64,8 @@ interface VectorComponent {
|
|||
|
||||
fun vectorConfiguration(): VectorConfiguration
|
||||
|
||||
fun avatarRenderer(): AvatarRenderer
|
||||
|
||||
fun activeSessionHolder(): ActiveSessionHolder
|
||||
|
||||
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 im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.vectorComponent
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
||||
open class UserAvatarPreference : Preference {
|
||||
|
||||
|
@ -31,6 +33,8 @@ open class UserAvatarPreference : Preference {
|
|||
internal var mSession: Session? = null
|
||||
private var mLoadingProgressBar: ProgressBar? = null
|
||||
|
||||
private var avatarRenderer: AvatarRenderer = context.vectorComponent().avatarRenderer()
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
@ -52,11 +56,14 @@ open class UserAvatarPreference : Preference {
|
|||
}
|
||||
|
||||
open fun refreshAvatar() {
|
||||
if (null != mAvatarView && null != mSession) {
|
||||
// TODO
|
||||
// val myUser = session!!.myUser
|
||||
// VectorUtils.loadUserAvatar(context, session, mAvatarView, myUser.avatarUrl, myUser.user_id, myUser.displayname)
|
||||
val session = mSession ?: return
|
||||
val view = mAvatarView ?: return
|
||||
session.getUser(session.sessionParams.credentials.userId)?.let {
|
||||
avatarRenderer.render(it, view)
|
||||
} ?: run {
|
||||
avatarRenderer.render(null, session.sessionParams.credentials.userId, null, view)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun setSession(session: Session) {
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
package im.vector.riotredesign.core.pushers
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
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.LocaleProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val DEFAULT_PUSHER_FILE_TAG = "mobile"
|
||||
|
||||
class PushersManager @Inject constructor(
|
||||
private val currentSession: Session,
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val localeProvider: LocaleProvider,
|
||||
private val stringProvider: StringProvider,
|
||||
private val appNameProvider: AppNameProvider
|
||||
) {
|
||||
|
||||
fun registerPusherWithFcmKey(pushKey: String) {
|
||||
var profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + Math.abs(currentSession.sessionParams.credentials.userId.hashCode())
|
||||
fun registerPusherWithFcmKey(pushKey: String): UUID {
|
||||
val currentSession = activeSessionHolder.getActiveSession()
|
||||
var profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + Math.abs(currentSession.myUserId.hashCode())
|
||||
|
||||
currentSession.addHttpPusher(
|
||||
return currentSession.addHttpPusher(
|
||||
pushKey,
|
||||
stringProvider.getString(R.string.pusher_app_id),
|
||||
profileTag,
|
||||
|
@ -34,6 +36,7 @@ class PushersManager @Inject constructor(
|
|||
}
|
||||
|
||||
fun unregisterPusher(pushKey: String, callback: MatrixCallback<Unit>) {
|
||||
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id),callback)
|
||||
val currentSession = activeSessionHolder.getActiveSession()
|
||||
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.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.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.notifications.supportNotificationChannels
|
||||
|
@ -44,7 +49,7 @@ import java.util.*
|
|||
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
|
||||
// no issue before Android M, battery optimisations did not exist
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
||||
|| (context.getSystemService(Context.POWER_SERVICE) as PowerManager?)?.isIgnoringBatteryOptimizations(context.packageName) == true
|
||||
|| (context.getSystemService(Context.POWER_SERVICE) as PowerManager?)?.isIgnoringBatteryOptimizations(context.packageName) == true
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,22 +114,22 @@ fun getDeviceLocale(context: Context): Locale {
|
|||
* 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
|
||||
*/
|
||||
fun startNotificationSettingsIntent(fragment: Fragment, requestCode: Int) {
|
||||
fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: Int) {
|
||||
val intent = Intent()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
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) {
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
intent.putExtra("app_package", fragment.context?.packageName)
|
||||
intent.putExtra("app_uid", fragment.context?.applicationInfo?.uid)
|
||||
intent.putExtra("app_package", activity.packageName)
|
||||
intent.putExtra("app_uid", activity.applicationInfo?.uid)
|
||||
} else {
|
||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
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
|
||||
}
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
activity.startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,13 +145,13 @@ fun startNotificationChannelSettingsIntent(fragment: Fragment, channelID: String
|
|||
fragment.startActivity(intent)
|
||||
}
|
||||
|
||||
fun startAddGoogleAccountIntent(fragment: Fragment, requestCode: Int) {
|
||||
fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
|
||||
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
context.startActivityForResult(intent, requestCode)
|
||||
} 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) {
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
composerLayout.setRoomEncrypted(state.isEncrypted)
|
||||
}
|
||||
|
||||
private fun renderRoomSummary(state: RoomDetailViewState) {
|
||||
|
|
|
@ -496,7 +496,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
private fun observeRoomSummary() {
|
||||
room.rx().liveRoomSummary()
|
||||
.execute { async ->
|
||||
copy(asyncRoomSummary = async)
|
||||
copy(
|
||||
asyncRoomSummary = async,
|
||||
isEncrypted = room.isEncrypted()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,8 @@ data class RoomDetailViewState(
|
|||
val asyncInviter: Async<User> = Uninitialized,
|
||||
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
||||
val sendMode: SendMode = SendMode.REGULAR,
|
||||
val selectedEvent: TimelineEvent? = null
|
||||
val selectedEvent: TimelineEvent? = null,
|
||||
val isEncrypted: Boolean = false
|
||||
) : MvRxState {
|
||||
|
||||
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.widget.ImageView
|
|||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionManager
|
||||
|
@ -113,4 +112,13 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
|
|||
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,14 +89,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
val event = session.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return state
|
||||
var body: CharSequence? = null
|
||||
val originTs = event.root.originServerTs
|
||||
when (event.root.type) {
|
||||
when (event.root.getClearType()) {
|
||||
EventType.MESSAGE -> {
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
?: event.root.getClearContent().toModel()
|
||||
body = messageContent?.body
|
||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||
body = eventHtmlRenderer.render(messageContent.formattedBody
|
||||
?: messageContent.body)
|
||||
?: messageContent.body)
|
||||
}
|
||||
}
|
||||
EventType.STATE_ROOM_NAME,
|
||||
|
|
|
@ -64,12 +64,9 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
|
|||
// TODO This is not correct format for error, change it
|
||||
|
||||
val informationData = messageInformationDataFactory.create(event, nextEvent)
|
||||
return NoticeItem_()
|
||||
.avatarRenderer(avatarRenderer)
|
||||
.noticeText(spannableStr)
|
||||
|
||||
return MessageTextItem_()
|
||||
.message(spannableStr)
|
||||
.avatarRenderer(avatarRenderer)
|
||||
.informationData(informationData)
|
||||
.highlighted(highlight)
|
||||
.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.riotredesign.R
|
||||
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 java.util.*
|
||||
import javax.inject.Inject
|
||||
|
@ -42,10 +42,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
|||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent == null || context == null) return
|
||||
Timber.v("NotificationBroadcastReceiver received : $intent")
|
||||
val appContext = context.applicationContext
|
||||
if (appContext is HasVectorInjector) {
|
||||
appContext.injector().inject(this)
|
||||
}
|
||||
context.vectorComponent().inject(this)
|
||||
when (intent.action) {
|
||||
NotificationUtils.SMART_REPLY_ACTION ->
|
||||
handleSmartReply(intent, context)
|
||||
|
|
|
@ -376,7 +376,7 @@ object NotificationUtils {
|
|||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
// Build the pending intent for when the notification is clicked
|
||||
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
|
||||
return NotificationCompat.Builder(context, channelID)
|
||||
|
@ -479,7 +479,7 @@ object NotificationUtils {
|
|||
fun buildSimpleEventNotification(context: Context, simpleNotifiableEvent: NotifiableEvent, largeIcon: Bitmap?, matrixId: String): Notification? {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
// 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
|
||||
|
||||
|
@ -611,7 +611,7 @@ object NotificationUtils {
|
|||
noisy: Boolean,
|
||||
lastMessageTimestamp: Long): Notification? {
|
||||
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)
|
||||
// used in compat < N, after summary is built based on child notifications
|
||||
|
|
|
@ -28,7 +28,7 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
class PushRuleTriggerListener @Inject constructor(
|
||||
private val resolver: NotifiableEventResolver,
|
||||
private val drawerManager: NotificationDrawerManager
|
||||
private val notificationDrawerManager: NotificationDrawerManager
|
||||
) : PushRuleService.PushRuleListener {
|
||||
|
||||
|
||||
|
@ -42,14 +42,14 @@ class PushRuleTriggerListener @Inject constructor(
|
|||
}
|
||||
val notificationAction = NotificationAction.extractFrom(actions)
|
||||
if (notificationAction.shouldNotify) {
|
||||
val resolveEvent = resolver.resolveEvent(event, session!!)
|
||||
if (resolveEvent == null) {
|
||||
val notifiableEvent = resolver.resolveEvent(event, session!!)
|
||||
if (notifiableEvent == null) {
|
||||
Timber.v("## Failed to resolve event")
|
||||
//TODO
|
||||
} else {
|
||||
resolveEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
||||
Timber.v("New event to notify $resolveEvent tweaks:$notificationAction")
|
||||
drawerManager.onNotifiableEventReceived(resolveEvent)
|
||||
notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
||||
Timber.v("New event to notify $notifiableEvent tweaks:$notificationAction")
|
||||
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
||||
}
|
||||
} else {
|
||||
Timber.v("Matched push rule is set to not notify")
|
||||
|
@ -57,7 +57,7 @@ class PushRuleTriggerListener @Inject constructor(
|
|||
}
|
||||
|
||||
override fun batchFinish() {
|
||||
drawerManager.refreshNotificationDrawer()
|
||||
notificationDrawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
|
||||
fun startWithSession(session: Session) {
|
||||
|
@ -71,7 +71,7 @@ class PushRuleTriggerListener @Inject constructor(
|
|||
fun stop() {
|
||||
session?.removePushRuleListener(this)
|
||||
session = null
|
||||
drawerManager.clearAllEvents()
|
||||
notificationDrawerManager.clearAllEvents()
|
||||
}
|
||||
|
||||
}
|
|
@ -25,14 +25,15 @@ import android.net.Uri;
|
|||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import im.vector.riotredesign.R;
|
||||
import im.vector.riotredesign.features.homeserver.ServerUrlsRepository;
|
||||
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_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_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_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_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_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_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";
|
||||
|
@ -247,6 +246,13 @@ public class PreferencesManager {
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -53,20 +53,15 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||
override fun initUiAndData() {
|
||||
configureToolbar(settingsToolbar)
|
||||
|
||||
var vectorSettingsPreferencesFragment: Fragment? = null
|
||||
if (isFirstCreation()) {
|
||||
vectorSettingsPreferencesFragment = VectorSettingsPreferencesFragmentV2.newInstance()
|
||||
val vectorSettingsPreferencesFragment = VectorSettingsRootFragment.newInstance()
|
||||
// display the fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.vector_settings_page, vectorSettingsPreferencesFragment, FRAGMENT_TAG)
|
||||
.commit()
|
||||
} else {
|
||||
vectorSettingsPreferencesFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)
|
||||
}
|
||||
|
||||
|
||||
supportFragmentManager.addOnBackStackChangedListener(this)
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -83,9 +78,7 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat?, pref: Preference?): Boolean {
|
||||
var oFragment: Fragment? = null
|
||||
|
||||
if ("Legacy" == pref?.title) {
|
||||
oFragment = VectorSettingsPreferencesFragment.newInstance(session.sessionParams.credentials.userId)
|
||||
} else if (PreferencesManager.SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY == pref?.key) {
|
||||
if (PreferencesManager.SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY == pref?.key) {
|
||||
oFragment = VectorSettingsNotificationsTroubleshootFragment.newInstance(session.sessionParams.credentials.userId)
|
||||
} else if (PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY == pref?.key) {
|
||||
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 javax.inject.Inject
|
||||
|
||||
class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFragment() {
|
||||
|
||||
// members
|
||||
@Inject lateinit var session: Session
|
||||
private var mLoadingView: View? = null
|
||||
class VectorSettingsAdvancedNotificationPreferenceFragment : VectorSettingsBaseFragment() {
|
||||
|
||||
// events listener
|
||||
/* TODO
|
||||
|
@ -53,10 +49,9 @@ class VectorSettingsAdvancedNotificationPreferenceFragment : VectorPreferenceFra
|
|||
|
||||
override var titleRes: Int = R.string.settings_notification_advanced
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
// define the layout
|
||||
addPreferencesFromResource(R.xml.vector_settings_notification_advanced_preferences)
|
||||
override val preferenceXmlRes = R.xml.vector_settings_notification_advanced_preferences
|
||||
|
||||
override fun bindPref() {
|
||||
val callNotificationsSystemOptions = findPreference(PreferencesManager.SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY)
|
||||
if (supportNotificationChannels()) {
|
||||
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
|
||||
*/
|
||||
|
@ -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
|
||||
* ========================================================================================== */
|
||||
|
|
|
@ -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,68 +16,117 @@
|
|||
|
||||
package im.vector.riotredesign.features.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreference
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.pushrules.RuleIds
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||
import im.vector.riotredesign.core.di.DaggerScreenComponent
|
||||
import im.vector.riotredesign.core.platform.VectorPreferenceFragment
|
||||
import im.vector.riotredesign.core.di.ScreenComponent
|
||||
import im.vector.riotredesign.core.pushers.PushersManager
|
||||
import im.vector.riotredesign.push.fcm.FcmHelper
|
||||
import javax.inject.Inject
|
||||
|
||||
// Referenced in vector_settings_preferences_root.xml
|
||||
class VectorSettingsNotificationPreferenceFragment : VectorPreferenceFragment() {
|
||||
|
||||
class VectorSettingsNotificationPreferenceFragment : VectorSettingsBaseFragment() {
|
||||
|
||||
override var titleRes: Int = R.string.settings_notifications
|
||||
override val preferenceXmlRes = R.xml.vector_settings_notifications
|
||||
|
||||
@Inject lateinit var pushManager: PushersManager
|
||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.vector_settings_notifications)
|
||||
override fun bindPref() {
|
||||
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
|
||||
}
|
||||
|
||||
val areNotifEnabledAtAccountLevelt = !mRuleMaster.enabled
|
||||
(pref as SwitchPreference).isChecked = areNotifEnabledAtAccountLevelt
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
val screenComponent = DaggerScreenComponent.factory().create(vectorActivity.getVectorComponent(), vectorActivity)
|
||||
super.onAttach(context)
|
||||
screenComponent.inject(this)
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
activeSessionHolder.getSafeActiveSession()?.refreshPushers()
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||
if (preference?.key == PreferencesManager.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) {
|
||||
val switchPref = preference as SwitchPreference
|
||||
if (switchPref.isChecked) {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
if (PreferencesManager.areNotificationEnabledForDevice(requireContext())) {
|
||||
pushManager.registerPusherWithFcmKey(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
pushManager.unregisterPusher(it, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
super.onSuccess(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
super.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
return super.onPreferenceTreeClick(preference)
|
||||
|
||||
}
|
||||
|
||||
private fun updateEnabledForDevice(preference: Preference?) {
|
||||
val switchPref = preference as SwitchPreference
|
||||
if (switchPref.isChecked) {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
if (PreferencesManager.areNotificationEnabledForDevice(requireContext())) {
|
||||
pushManager.registerPusherWithFcmKey(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
pushManager.unregisterPusher(it, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
session.refreshPushers()
|
||||
super.onSuccess(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
session.refreshPushers()
|
||||
Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.di.ScreenComponent
|
||||
import im.vector.riotredesign.core.extensions.withArgs
|
||||
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
|
@ -56,15 +57,15 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||
// members
|
||||
@Inject lateinit var session: Session
|
||||
@Inject lateinit var bugReporter: BugReporter
|
||||
@Inject lateinit var testManagerFactory: NotificationTroubleshootTestManagerFactory
|
||||
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_settings_notifications_troubleshoot
|
||||
|
||||
private var interactionListener: VectorSettingsFragmentInteractionListener? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val appContext = activity!!.applicationContext
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -74,7 +75,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||
mRecyclerView.layoutManager = layoutManager
|
||||
|
||||
val dividerItemDecoration = DividerItemDecoration(mRecyclerView.context,
|
||||
layoutManager.orientation)
|
||||
layoutManager.orientation)
|
||||
mRecyclerView.addItemDecoration(dividerItemDecoration)
|
||||
|
||||
|
||||
|
@ -91,10 +92,8 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||
private fun startUI() {
|
||||
|
||||
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status,
|
||||
0, 0)
|
||||
|
||||
testManager = NotificationTroubleshootTestManagerFactory.createTestManager(this, session)
|
||||
|
||||
0, 0)
|
||||
testManager = testManagerFactory.create(this)
|
||||
testManager?.statusListener = { troubleshootTestManager ->
|
||||
if (isAdded) {
|
||||
TransitionManager.beginDelayedTransition(mBottomView)
|
||||
|
@ -104,7 +103,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||
mSummaryButton.visibility = View.GONE
|
||||
mRunButton.visibility = View.VISIBLE
|
||||
}
|
||||
TroubleshootTest.TestStatus.RUNNING -> {
|
||||
TroubleshootTest.TestStatus.RUNNING -> {
|
||||
//Forces int type because it's breaking lint
|
||||
val size: Int = troubleshootTestManager.testList.size
|
||||
val currentTestIndex: Int = troubleshootTestManager.currentTestIndex
|
||||
|
@ -116,7 +115,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||
mSummaryButton.visibility = View.GONE
|
||||
mRunButton.visibility = View.GONE
|
||||
}
|
||||
TroubleshootTest.TestStatus.FAILED -> {
|
||||
TroubleshootTest.TestStatus.FAILED -> {
|
||||
//check if there are quick fixes
|
||||
var hasQuickFix = false
|
||||
testManager?.testList?.let {
|
||||
|
@ -135,7 +134,7 @@ class VectorSettingsNotificationsTroubleshootFragment : VectorBaseFragment() {
|
|||
mSummaryButton.visibility = View.VISIBLE
|
||||
mRunButton.visibility = View.VISIBLE
|
||||
}
|
||||
TroubleshootTest.TestStatus.SUCCESS -> {
|
||||
TroubleshootTest.TestStatus.SUCCESS -> {
|
||||
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_success_status)
|
||||
mSummaryButton.visibility = View.VISIBLE
|
||||
mRunButton.visibility = View.VISIBLE
|
||||
|
|
2823
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
|
||||
|
||||
import android.os.Bundle
|
||||
import im.vector.riotredesign.R
|
||||
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 val preferenceXmlRes = R.xml.vector_settings_root
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.vector_settings_preferences_root)
|
||||
override fun bindPref() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun newInstance() = VectorSettingsPreferencesFragmentV2()
|
||||
fun newInstance() = VectorSettingsRootFragment()
|
||||
.withArgs {
|
||||
//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,45 +15,48 @@
|
|||
*/
|
||||
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.pushrules.RuleIds
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
|
||||
override fun perform() {
|
||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
val defaultRule = session.getPushRules()
|
||||
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
|
||||
|
||||
if (defaultRule != null) {
|
||||
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
|
||||
status = TestStatus.SUCCESS
|
||||
} 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) {
|
||||
override fun doFix() {
|
||||
if (manager?.diagStatus == TestStatus.RUNNING) return //wait before all is finished
|
||||
|
||||
// TODO Use constant for kind
|
||||
session.updatePushRuleEnableStatus("override", defaultRule, !defaultRule.enabled,
|
||||
object : MatrixCallback<Unit> {
|
||||
object : MatrixCallback<Unit> {
|
||||
|
||||
override fun onSuccess(data: Unit) {
|
||||
manager?.retry()
|
||||
}
|
||||
override fun onSuccess(data: Unit) {
|
||||
manager?.retry()
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
manager?.retry()
|
||||
}
|
||||
})
|
||||
override fun onFailure(failure: Throwable) {
|
||||
manager?.retry()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
status = TestStatus.FAILED
|
||||
|
|
|
@ -15,66 +15,71 @@
|
|||
*/
|
||||
package im.vector.riotredesign.features.settings.troubleshoot
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.pushrules.Action
|
||||
import im.vector.matrix.android.api.pushrules.RuleIds
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.settings.PreferencesManager
|
||||
import im.vector.riotredesign.features.settings.VectorSettingsFragmentInteractionListener
|
||||
import im.vector.riotredesign.core.di.ActiveSessionHolder
|
||||
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,
|
||||
R.string.settings_containing_my_user_name,
|
||||
R.string.settings_messages_in_one_to_one,
|
||||
R.string.settings_messages_in_group_chat)
|
||||
R.string.settings_containing_my_user_name,
|
||||
R.string.settings_messages_in_one_to_one,
|
||||
R.string.settings_messages_in_group_chat)
|
||||
|
||||
override fun perform() {
|
||||
val pushRules = null // TODO session.dataHandler.pushRules()
|
||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
val pushRules = session.getPushRules()
|
||||
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
|
||||
} else {
|
||||
var oneOrMoreRuleIsOff = false
|
||||
var oneOrMoreRuleAreSilent = false
|
||||
for ((index, ruleId) in testedRules.withIndex()) {
|
||||
/* TODO
|
||||
pushRules.findDefaultRule(ruleId)?.let { rule ->
|
||||
if (!rule.isEnabled || rule.shouldNotNotify()) {
|
||||
pushRules.find { it.ruleId == ruleId }?.let { rule ->
|
||||
val actions = Action.mapFrom(rule) ?: return@let
|
||||
val notifAction = NotificationAction.extractFrom(actions)
|
||||
if (!rule.enabled || !notifAction.shouldNotify) {
|
||||
//off
|
||||
oneOrMoreRuleIsOff = true
|
||||
} else if (rule.notificationSound == null) {
|
||||
} else if (notifAction.soundName == null) {
|
||||
//silent
|
||||
oneOrMoreRuleAreSilent = true
|
||||
} else {
|
||||
//noisy
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
if (oneOrMoreRuleIsOff) {
|
||||
description = fragment.getString(R.string.settings_troubleshoot_test_bing_settings_failed)
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_bing_settings_quickfix) {
|
||||
override fun doFix() {
|
||||
val activity = fragment.activity
|
||||
if (activity is VectorSettingsFragmentInteractionListener) {
|
||||
activity.requestHighlightPreferenceKeyOnResume(PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY)
|
||||
}
|
||||
activity?.supportFragmentManager?.popBackStack()
|
||||
}
|
||||
}
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_bing_settings_failed)
|
||||
//TODO
|
||||
// quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_bing_settings_quickfix) {
|
||||
// override fun doFix() {
|
||||
// val activity = fragment.activity
|
||||
// if (activity is VectorSettingsFragmentInteractionListener) {
|
||||
// activity.requestHighlightPreferenceKeyOnResume(PreferencesManager.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY)
|
||||
// }
|
||||
// activity?.supportFragmentManager?.popBackStack()
|
||||
// }
|
||||
// }
|
||||
status = TestStatus.FAILED
|
||||
} else {
|
||||
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 {
|
||||
description = null
|
||||
}
|
||||
|
|
|
@ -15,33 +15,33 @@
|
|||
*/
|
||||
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.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.
|
||||
*/
|
||||
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() {
|
||||
/* TODO
|
||||
val pushManager = Matrix.getInstance(fragment.activity).pushManager
|
||||
if (pushManager.areDeviceNotificationsAllowed()) {
|
||||
description = fragment.getString(R.string.settings_troubleshoot_test_device_settings_success)
|
||||
|
||||
if (PreferencesManager.areNotificationEnabledForDevice(context)) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success)
|
||||
quickFix = null
|
||||
status = TestStatus.SUCCESS
|
||||
} else {
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
|
||||
override fun doFix() {
|
||||
pushManager.setDeviceNotificationsAllowed(true)
|
||||
PreferencesManager.setNotificationEnabledForDevice(context, true)
|
||||
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
|
||||
}
|
||||
}
|
|
@ -15,27 +15,30 @@
|
|||
*/
|
||||
package im.vector.riotredesign.features.settings.troubleshoot
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.core.utils.startNotificationSettingsIntent
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if (NotificationManagerCompat.from(fragment.context!!).areNotificationsEnabled()) {
|
||||
description = fragment.getString(R.string.settings_troubleshoot_test_system_settings_success)
|
||||
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success)
|
||||
quickFix = null
|
||||
status = TestStatus.SUCCESS
|
||||
} 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) {
|
||||
override fun doFix() {
|
||||
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: 1,022 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 KiB |
|
@ -1,170 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
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" />
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="m0,0h108v108h-108z"
|
||||
android:fillColor="#27303a"
|
||||
android:fillType="evenOdd"/>
|
||||
</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:layout_width="wrap_content"
|
||||
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
|
||||
style="@style/VectorTextInputLayout"
|
||||
|
|
|
@ -116,12 +116,12 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:hint="@string/room_message_placeholder_not_encrypted"
|
||||
android:nextFocusLeft="@id/composerEditText"
|
||||
android:nextFocusUp="@id/composerEditText"
|
||||
android:padding="8dp"
|
||||
android:textColor="?vctr_message_text_color"
|
||||
android:textSize="14sp"
|
||||
tools:hint="@string/room_message_placeholder_not_encrypted"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</merge>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</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 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 KiB |