Better scroll on new messages & Kotlin

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2019-09-09 21:59:40 +02:00
parent 4d9781fbe2
commit 362042e27e
16 changed files with 744 additions and 782 deletions

View File

@ -160,7 +160,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.biometric:biometric:1.0.0-alpha04'
implementation 'androidx.biometric:biometric:1.0.0-beta01'
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
implementation 'androidx.multidex:multidex:2.0.1'
@ -212,7 +212,7 @@ dependencies {
implementation 'me.zhanghai.android.effortlesspermissions:library:1.1.0'
implementation 'org.apache.commons:commons-lang3:3.9'
implementation 'com.github.wooplr:Spotlight:1.3'
implementation('com.github.mario:chatkit:a7c4f3c9ea', {
implementation('com.github.mario:chatkit:a183142049', {
exclude group: 'com.facebook.fresco'
})

View File

@ -1,155 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.activities;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.webkit.SslErrorHandler;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import autodagger.AutoInjector;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.events.CertificateEvent;
import com.nextcloud.talk.utils.SecurityUtils;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import com.nextcloud.talk.utils.ssl.MagicTrustManager;
import com.yarolegovich.lovelydialog.LovelyStandardDialog;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import javax.inject.Inject;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.List;
@AutoInjector(NextcloudTalkApplication.class)
public class BaseActivity extends AppCompatActivity {
private static final String TAG = "BaseActivity";
@Inject
EventBus eventBus;
@Inject
AppPreferences appPreferences;
@Inject
Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
super.onCreate(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
if (appPreferences.getIsScreenSecured()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
if (appPreferences.getIsScreenLocked()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
SecurityUtils.createKey(appPreferences.getScreenLockTimeout());
}
}
}
public void showCertificateDialog(X509Certificate cert, MagicTrustManager magicTrustManager,
@Nullable SslErrorHandler sslErrorHandler) {
DateFormat formatter = DateFormat.getDateInstance(DateFormat.LONG);
String validFrom = formatter.format(cert.getNotBefore());
String validUntil = formatter.format(cert.getNotAfter());
String issuedBy = cert.getIssuerDN().toString();
String issuedFor;
try {
if (cert.getSubjectAlternativeNames() != null) {
StringBuilder stringBuilder = new StringBuilder();
for (Object o : cert.getSubjectAlternativeNames()) {
List list = (List) o;
int type = (Integer) list.get(0);
if (type == 2) {
String name = (String) list.get(1);
stringBuilder.append("[").append(type).append("]").append(name).append(" ");
}
}
issuedFor = stringBuilder.toString();
} else {
issuedFor = cert.getSubjectDN().getName();
}
@SuppressLint("StringFormatMatches") String dialogText = String.format(getResources()
.getString(R.string.nc_certificate_dialog_text),
issuedBy, issuedFor, validFrom, validUntil);
new LovelyStandardDialog(this)
.setTopColorRes(R.color.nc_darkRed)
.setNegativeButtonColorRes(R.color.nc_darkRed)
.setPositiveButtonColorRes(R.color.colorPrimaryDark)
.setIcon(R.drawable.ic_security_white_24dp)
.setTitle(R.string.nc_certificate_dialog_title)
.setMessage(dialogText)
.setPositiveButton(R.string.nc_yes, v -> {
magicTrustManager.addCertInTrustStore(cert);
if (sslErrorHandler != null) {
sslErrorHandler.proceed();
}
})
.setNegativeButton(R.string.nc_no, view1 -> {
if (sslErrorHandler != null) {
sslErrorHandler.cancel();
}
})
.show();
} catch (CertificateParsingException e) {
Log.d(TAG, "Failed to parse the certificate");
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(CertificateEvent event) {
showCertificateDialog(event.getX509Certificate(), event.getMagicTrustManager(), event.getSslErrorHandler());
}
@Override
public void onStart() {
super.onStart();
eventBus.register(this);
}
@Override
public void onStop() {
super.onStop();
eventBus.unregister(this);
}
}

View File

@ -0,0 +1,148 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.activities
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import android.webkit.SslErrorHandler
import androidx.appcompat.app.AppCompatActivity
import autodagger.AutoInjector
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.events.CertificateEvent
import com.nextcloud.talk.utils.SecurityUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.nextcloud.talk.utils.ssl.MagicTrustManager
import com.yarolegovich.lovelydialog.LovelyStandardDialog
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.security.cert.CertificateParsingException
import java.security.cert.X509Certificate
import java.text.DateFormat
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
open class BaseActivity : AppCompatActivity() {
@Inject
lateinit var eventBus: EventBus
@Inject
lateinit var appPreferences: AppPreferences
@Inject
lateinit var context: Context
override fun onCreate(savedInstanceState: Bundle?) {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
super.onCreate(savedInstanceState)
}
public override fun onResume() {
super.onResume()
if (appPreferences.isScreenSecured) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
if (appPreferences.isScreenLocked) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
SecurityUtils.createKey(appPreferences.screenLockTimeout)
}
}
}
fun showCertificateDialog(cert: X509Certificate, magicTrustManager: MagicTrustManager,
sslErrorHandler: SslErrorHandler?) {
val formatter = DateFormat.getDateInstance(DateFormat.LONG)
val validFrom = formatter.format(cert.notBefore)
val validUntil = formatter.format(cert.notAfter)
val issuedBy = cert.issuerDN.toString()
val issuedFor: String
try {
if (cert.subjectAlternativeNames != null) {
val stringBuilder = StringBuilder()
for (o in cert.subjectAlternativeNames) {
val list = o as List<*>
val type = list[0] as Int
if (type == 2) {
val name = list[1] as String
stringBuilder.append("[").append(type).append("]").append(name).append(" ")
}
}
issuedFor = stringBuilder.toString()
} else {
issuedFor = cert.subjectDN.name
}
@SuppressLint("StringFormatMatches") val dialogText = String.format(resources
.getString(R.string.nc_certificate_dialog_text),
issuedBy, issuedFor, validFrom, validUntil)
LovelyStandardDialog(this)
.setTopColorRes(R.color.nc_darkRed)
.setNegativeButtonColorRes(R.color.nc_darkRed)
.setPositiveButtonColorRes(R.color.colorPrimaryDark)
.setIcon(R.drawable.ic_security_white_24dp)
.setTitle(R.string.nc_certificate_dialog_title)
.setMessage(dialogText)
.setPositiveButton(R.string.nc_yes) { v ->
magicTrustManager.addCertInTrustStore(cert)
sslErrorHandler?.proceed()
}
.setNegativeButton(R.string.nc_no) { view1 ->
sslErrorHandler?.cancel()
}
.show()
} catch (e: CertificateParsingException) {
Log.d(TAG, "Failed to parse the certificate")
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: CertificateEvent) {
showCertificateDialog(event.x509Certificate, event.magicTrustManager, event.sslErrorHandler)
}
public override fun onStart() {
super.onStart()
eventBus.register(this)
}
public override fun onStop() {
super.onStop()
eventBus.unregister(this)
}
companion object {
private val TAG = "BaseActivity"
}
}

View File

@ -1,100 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.activities;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.bluelinelabs.conductor.Conductor;
import com.bluelinelabs.conductor.Router;
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.CallController;
import com.nextcloud.talk.controllers.CallNotificationController;
import com.nextcloud.talk.events.ConfigurationChangeEvent;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import org.greenrobot.eventbus.EventBus;
import javax.inject.Inject;
@AutoInjector(NextcloudTalkApplication.class)
public class MagicCallActivity extends BaseActivity {
private static final String TAG = "MagicCallActivity";
@Inject
EventBus eventBus;
@BindView(R.id.controller_container)
ViewGroup container;
private Router router;
private static int getSystemUiVisibility() {
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
return flags;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility());
setContentView(R.layout.activity_magic_call);
ButterKnife.bind(this);
router = Conductor.attachRouter(this, container, savedInstanceState);
router.setPopsLastView(false);
if (!router.hasRootController()) {
if (getIntent().getBooleanExtra(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL(), false)) {
router.setRoot(RouterTransaction.with(new CallNotificationController(getIntent().getExtras()))
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {
router.setRoot(RouterTransaction.with(new CallController(getIntent().getExtras()))
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
}
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
eventBus.post(new ConfigurationChangeEvent());
}
}

View File

@ -0,0 +1,96 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.activities
import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Router
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.controllers.CallController
import com.nextcloud.talk.controllers.CallNotificationController
import com.nextcloud.talk.events.ConfigurationChangeEvent
import com.nextcloud.talk.utils.bundle.BundleKeys
@AutoInjector(NextcloudTalkApplication::class)
class MagicCallActivity : BaseActivity() {
@BindView(R.id.controller_container)
lateinit var container: ViewGroup
private var router: Router? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
window.decorView.systemUiVisibility = systemUiVisibility
setContentView(R.layout.activity_magic_call)
ButterKnife.bind(this)
router = Conductor.attachRouter(this, container, savedInstanceState)
router!!.setPopsLastView(false)
if (!router!!.hasRootController()) {
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
router!!.setRoot(RouterTransaction.with(CallNotificationController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
} else {
router!!.setRoot(RouterTransaction.with(CallController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
}
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
eventBus.post(ConfigurationChangeEvent())
}
companion object {
private val TAG = "MagicCallActivity"
private val systemUiVisibility: Int
get() {
var flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
flags = flags or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
return flags
}
}
}

View File

@ -1,175 +0,0 @@
/*
*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.activities;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.ViewGroup;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.Toolbar;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.bluelinelabs.conductor.Conductor;
import com.bluelinelabs.conductor.Router;
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
import com.google.android.material.appbar.MaterialToolbar;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.CallNotificationController;
import com.nextcloud.talk.controllers.ConversationsListController;
import com.nextcloud.talk.controllers.LockedController;
import com.nextcloud.talk.controllers.ServerSelectionController;
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider;
import com.nextcloud.talk.utils.ConductorRemapping;
import com.nextcloud.talk.utils.SecurityUtils;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;
import io.requery.Persistable;
import io.requery.android.sqlcipher.SqlCipherDatabaseSource;
import io.requery.reactivex.ReactiveEntityStore;
import javax.inject.Inject;
@AutoInjector(NextcloudTalkApplication.class)
public final class MainActivity extends BaseActivity implements ActionBarProvider {
private static final String TAG = "MainActivity";
@BindView(R.id.toolbar)
MaterialToolbar toolbar;
@BindView(R.id.controller_container)
ViewGroup container;
@Inject
UserUtils userUtils;
@Inject
ReactiveEntityStore<Persistable> dataStore;
@Inject
SqlCipherDatabaseSource sqlCipherDatabaseSource;
private Router router;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
router = Conductor.attachRouter(this, container, savedInstanceState);
boolean hasDb = true;
try {
sqlCipherDatabaseSource.getWritableDatabase();
} catch (Exception exception) {
hasDb = false;
}
if (getIntent().hasExtra(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL())) {
if (!router.hasRootController()) {
router.setRoot(RouterTransaction.with(new ConversationsListController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
}
onNewIntent(getIntent());
} else if (!router.hasRootController()) {
if (hasDb) {
if (userUtils.anyUserExists()) {
router.setRoot(RouterTransaction.with(new ConversationsListController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {
router.setRoot(RouterTransaction.with(new ServerSelectionController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
}
} else {
router.setRoot(RouterTransaction.with(new ServerSelectionController())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
}
}
}
@Override
public void onStart() {
super.onStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkIfWeAreSecure();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void checkIfWeAreSecure() {
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager != null && keyguardManager.isKeyguardSecure() && appPreferences.getIsScreenLocked()) {
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.getScreenLockTimeout())) {
if (router != null && router.getControllerWithTag(LockedController.TAG) == null) {
router.pushController(RouterTransaction.with(new LockedController())
.pushChangeHandler(new VerticalChangeHandler())
.popChangeHandler(new VerticalChangeHandler())
.tag(LockedController.TAG));
}
}
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.hasExtra(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL())) {
if (intent.getBooleanExtra(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL(), false)) {
router.pushController(RouterTransaction.with(new CallNotificationController(intent.getExtras()))
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {
ConductorRemapping.INSTANCE.remapChatController(router, intent.getLongExtra(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), -1),
intent.getStringExtra(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()), intent.getExtras(), false);
;
}
}
}
@Override
public void onBackPressed() {
if (router.getControllerWithTag(LockedController.TAG) != null) {
return;
}
if (!router.handleBack()) {
super.onBackPressed();
}
}
}

View File

@ -0,0 +1,171 @@
/*
*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.activities
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Router
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
import com.google.android.material.appbar.MaterialToolbar
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.controllers.CallNotificationController
import com.nextcloud.talk.controllers.ConversationsListController
import com.nextcloud.talk.controllers.LockedController
import com.nextcloud.talk.controllers.ServerSelectionController
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.SecurityUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.UserUtils
import io.requery.Persistable
import io.requery.android.sqlcipher.SqlCipherDatabaseSource
import io.requery.reactivex.ReactiveEntityStore
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class MainActivity : BaseActivity(), ActionBarProvider {
@BindView(R.id.toolbar)
lateinit var toolbar: MaterialToolbar
@BindView(R.id.controller_container)
lateinit var container: ViewGroup
@Inject
lateinit var userUtils: UserUtils
@Inject
lateinit var dataStore: ReactiveEntityStore<Persistable>
@Inject
lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource
private var router: Router? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
ButterKnife.bind(this)
setSupportActionBar(toolbar)
router = Conductor.attachRouter(this, container, savedInstanceState)
var hasDb = true
try {
sqlCipherDatabaseSource.writableDatabase
} catch (exception: Exception) {
hasDb = false
}
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (!router!!.hasRootController()) {
router!!.setRoot(RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
}
onNewIntent(intent)
} else if (!router!!.hasRootController()) {
if (hasDb) {
if (userUtils.anyUserExists()) {
router!!.setRoot(RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
} else {
router!!.setRoot(RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
}
} else {
router!!.setRoot(RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
}
}
}
override fun onStart() {
super.onStart()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkIfWeAreSecure()
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
fun checkIfWeAreSecure() {
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) {
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) {
if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) {
router!!.pushController(RouterTransaction.with(LockedController())
.pushChangeHandler(VerticalChangeHandler())
.popChangeHandler(VerticalChangeHandler())
.tag(LockedController.TAG))
}
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
router!!.pushController(RouterTransaction.with(CallNotificationController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
} else {
ConductorRemapping.remapChatController(router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false)
}
}
}
override fun onBackPressed() {
if (router!!.getControllerWithTag(LockedController.TAG) != null) {
return
}
if (!router!!.handleBack()) {
super.onBackPressed()
}
}
companion object {
private val TAG = "MainActivity"
}
}

View File

@ -1118,9 +1118,9 @@ public class CallController extends BaseController {
}
if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) {
NotificationUtils.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomId);
NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomId);
} else if (!TextUtils.isEmpty(roomToken)) {
NotificationUtils.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomToken);
NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomToken);
}
if (!hasExternalSignalingServer) {

View File

@ -701,9 +701,9 @@ public class ChatController extends BaseController implements MessagesListAdapte
private void cancelNotificationsForCurrentConversation() {
if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) {
NotificationUtils.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomId);
NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomId);
} else if (!TextUtils.isEmpty(roomToken)) {
NotificationUtils.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomToken);
NotificationUtils.INSTANCE.cancelExistingNotificationsForRoom(getApplicationContext(), conversationUser, roomToken);
}
}
@ -1160,7 +1160,6 @@ public class ChatController extends BaseController implements MessagesListAdapte
unreadChatMessage.setTimestamp(chatMessageList.get(0).getTimestamp());
unreadChatMessage.setMessage(context.getString(R.string.nc_new_messages));
adapter.addToStart(unreadChatMessage, false);
layoutManager.scrollToPosition(chatMessageList.size() - 1);
}
for (int i = 0; i < chatMessageList.size(); i++) {
@ -1200,6 +1199,10 @@ public class ChatController extends BaseController implements MessagesListAdapte
}
if (shouldAddNewMessagesNotice && adapter != null) {
layoutManager.scrollToPositionWithOffset(adapter.getMessagePositionByIdInReverse("-1"), messagesListView.getHeight() / 2);
}
String xChatLastGivenHeader;
if (response.headers().size() > 0 && !TextUtils.isEmpty((xChatLastGivenHeader = response.headers().get
("X-Chat-Last-Given")))) {

View File

@ -1,62 +0,0 @@
/**
* Nextcloud Talk application
*
* @author BlueLine Labs, Inc.
* Copyright (C) 2016 BlueLine Labs, Inc.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 com.nextcloud.talk.controllers.base;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import com.bluelinelabs.conductor.Controller;
public abstract class ButterKnifeController extends Controller {
private Unbinder unbinder;
public ButterKnifeController() {
}
public ButterKnifeController(Bundle args) {
super(args);
}
protected abstract View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container);
@NonNull
@Override
protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
View view = inflateView(inflater, container);
unbinder = ButterKnife.bind(this, view);
onViewBound(view);
return view;
}
protected void onViewBound(@NonNull View view) {
}
@Override
protected void onDestroyView(@NonNull View view) {
super.onDestroyView(view);
unbinder.unbind();
unbinder = null;
}
}

View File

@ -0,0 +1,57 @@
/**
* Nextcloud Talk application
*
* @author BlueLine Labs, Inc.
* Copyright (C) 2016 BlueLine Labs, Inc.
*
*
* 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 com.nextcloud.talk.controllers.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import butterknife.ButterKnife
import butterknife.Unbinder
import com.bluelinelabs.conductor.Controller
abstract class ButterKnifeController : Controller {
private var unbinder: Unbinder? = null
constructor() {}
constructor(args: Bundle) : super(args) {}
protected abstract fun inflateView(inflater: LayoutInflater, container: ViewGroup): View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflateView(inflater, container)
unbinder = ButterKnife.bind(this, view)
onViewBound(view)
return view
}
protected open fun onViewBound(view: View) {}
override fun onDestroyView(view: View) {
super.onDestroyView(view)
unbinder!!.unbind()
unbinder = null
}
}

View File

@ -343,21 +343,21 @@ public class NotificationWorker extends Worker {
groupName);*/
if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType().equals("room")) {
NotificationUtils.createNotificationChannel(context,
NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V3, context.getResources()
NotificationUtils.INSTANCE.createNotificationChannel(context,
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3(), context.getResources()
.getString(R.string.nc_notification_channel_messages), context.getResources()
.getString(R.string.nc_notification_channel_messages), true,
NotificationManager.IMPORTANCE_HIGH);
notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V3);
notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3());
} else {
NotificationUtils.createNotificationChannel(context,
NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V3, context.getResources()
NotificationUtils.INSTANCE.createNotificationChannel(context,
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3(), context.getResources()
.getString(R.string.nc_notification_channel_calls), context.getResources()
.getString(R.string.nc_notification_channel_calls_description), true,
NotificationManager.IMPORTANCE_HIGH);
notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V3);
notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3());
}
} else {
@ -380,7 +380,7 @@ public class NotificationWorker extends Worker {
crc32.update(stringForCrc.getBytes());
StatusBarNotification activeStatusBarNotification =
NotificationUtils.findNotificationForRoom(context,
NotificationUtils.INSTANCE.findNotificationForRoom(context,
signatureVerification.getUserEntity(), decryptedPushMessage.getId());
int notificationId;
@ -562,9 +562,9 @@ public class NotificationWorker extends Worker {
decryptedPushMessage.setTimestamp(System.currentTimeMillis());
if (decryptedPushMessage.isDelete()) {
NotificationUtils.cancelExistingNotificationWithId(context, signatureVerification.getUserEntity(), decryptedPushMessage.getNotificationId());
NotificationUtils.INSTANCE.cancelExistingNotificationWithId(context, signatureVerification.getUserEntity(), decryptedPushMessage.getNotificationId());
} else if (decryptedPushMessage.isDeleteAll()) {
NotificationUtils.cancelAllNotificationsForAccount(context, signatureVerification.getUserEntity());
NotificationUtils.INSTANCE.cancelAllNotificationsForAccount(context, signatureVerification.getUserEntity());
} else {
credentials = ApiUtils.getCredentials(signatureVerification.getUserEntity().getUsername(),
signatureVerification.getUserEntity().getToken());

View File

@ -1,89 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.receivers;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import autodagger.AutoInjector;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.utils.NotificationUtils;
import com.nextcloud.talk.utils.database.user.UserUtils;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import javax.inject.Inject;
@AutoInjector(NextcloudTalkApplication.class)
public class PackageReplacedReceiver extends BroadcastReceiver {
private static final String TAG = "PackageReplacedReceiver";
@Inject
UserUtils userUtils;
@Inject
AppPreferences appPreferences;
@Override
public void onReceive(Context context, Intent intent) {
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
if (intent != null && intent.getAction() != null &&
intent.getAction().equals("android.intent.action.MY_PACKAGE_REPLACED")) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context
.NOTIFICATION_SERVICE);
if (notificationManager != null) {
if (!appPreferences.getIsNotificationChannelUpgradedToV2()) {
for (NotificationChannelGroup notificationChannelGroup : notificationManager
.getNotificationChannelGroups()) {
notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.getId());
}
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS);
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES);
appPreferences.setNotificationChannelIsUpgradedToV2(true);
}
if ((!appPreferences.getIsNotificationChannelUpgradedToV3()) && packageInfo.versionCode > 51) {
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V2);
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V2);
appPreferences.setNotificationChannelIsUpgradedToV3(true);
}
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Failed to fetch package info");
}
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.receivers
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import autodagger.AutoInjector
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class PackageReplacedReceiver : BroadcastReceiver() {
@Inject
lateinit var userUtils: UserUtils
@Inject
lateinit var appPreferences: AppPreferences
override fun onReceive(context: Context, intent: Intent?) {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
if (intent != null && intent.action != null &&
intent.action == "android.intent.action.MY_PACKAGE_REPLACED") {
try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context
.NOTIFICATION_SERVICE) as NotificationManager
if (notificationManager != null) {
if (!appPreferences!!.isNotificationChannelUpgradedToV2) {
for (notificationChannelGroup in notificationManager
.notificationChannelGroups) {
notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id)
}
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS)
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES)
appPreferences!!.setNotificationChannelIsUpgradedToV2(true)
}
if (!appPreferences!!.isNotificationChannelUpgradedToV3 && packageInfo.versionCode > 51) {
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V2)
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V2)
appPreferences!!.setNotificationChannelIsUpgradedToV3(true)
}
}
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to fetch package info")
}
}
}
companion object {
private val TAG = "PackageReplacedReceiver"
}
}

View File

@ -1,185 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.utils;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import com.nextcloud.talk.R;
import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import java.util.zip.CRC32;
public class NotificationUtils {
public static final String NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS";
public static final String NOTIFICATION_CHANNEL_MESSAGES = "NOTIFICATION_CHANNEL_MESSAGES";
public static final String NOTIFICATION_CHANNEL_CALLS_V2 = "NOTIFICATION_CHANNEL_CALLS_V2";
public static final String NOTIFICATION_CHANNEL_MESSAGES_V2 = "NOTIFICATION_CHANNEL_MESSAGES_V2";
public static final String NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3";
public static final String NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3";
@TargetApi(Build.VERSION_CODES.O)
public static void createNotificationChannel(Context context,
String channelId, String channelName,
String channelDescription, boolean enableLights,
int importance) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O
&& notificationManager.getNotificationChannel(channelId) == null) {
NotificationChannel channel = new NotificationChannel(channelId, channelName,
importance);
channel.setDescription(channelDescription);
channel.enableLights(enableLights);
channel.setLightColor(R.color.colorPrimary);
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
}
}
@TargetApi(Build.VERSION_CODES.O)
public static void createNotificationChannelGroup(Context context,
String groupId, CharSequence groupName) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannelGroup notificationChannelGroup = new NotificationChannelGroup(groupId, groupName);
if (!notificationManager.getNotificationChannelGroups().contains(notificationChannelGroup)) {
notificationManager.createNotificationChannelGroup(notificationChannelGroup);
}
}
}
public static void cancelAllNotificationsForAccount(Context context, UserEntity conversationUser) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && conversationUser.getId() != -1 &&
context != null) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
Notification notification;
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
notification = statusBarNotification.getNotification();
if (notification != null && !notification.extras.isEmpty()) {
if (conversationUser.getId() == notification.extras.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID())) {
notificationManager.cancel(statusBarNotification.getId());
}
}
}
}
}
}
public static void cancelExistingNotificationWithId(Context context, UserEntity conversationUser, long notificationId) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && conversationUser.getId() != -1 &&
context != null) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
Notification notification;
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
notification = statusBarNotification.getNotification();
if (notification != null && !notification.extras.isEmpty()) {
if (conversationUser.getId() == notification.extras.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID()) &&
notificationId == notification.extras.getLong(BundleKeys.INSTANCE.getKEY_NOTIFICATION_ID())) {
notificationManager.cancel(statusBarNotification.getId());
}
}
}
}
}
}
public static StatusBarNotification findNotificationForRoom(Context context,
UserEntity conversationUser,
String roomTokenOrId) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && conversationUser.getId() != -1 &&
context != null) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
Notification notification;
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
notification = statusBarNotification.getNotification();
if (notification != null && !notification.extras.isEmpty()) {
if (conversationUser.getId() == notification.extras.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID()) &&
roomTokenOrId.equals(statusBarNotification.getNotification().extras.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()))) {
return statusBarNotification;
}
}
}
}
}
return null;
}
public static void cancelExistingNotificationsForRoom(Context context, UserEntity conversationUser,
String roomTokenOrId) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M && conversationUser.getId() != -1 &&
context != null) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
Notification notification;
for (StatusBarNotification statusBarNotification : statusBarNotifications) {
notification = statusBarNotification.getNotification();
if (notification != null && !notification.extras.isEmpty()) {
if (conversationUser.getId() == notification.extras.getLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID()) &&
roomTokenOrId.equals(statusBarNotification.getNotification().extras.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN()))) {
notificationManager.cancel(statusBarNotification.getId());
}
}
}
}
}
}
}

View File

@ -0,0 +1,162 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.utils
import android.annotation.TargetApi
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.service.notification.StatusBarNotification
import com.nextcloud.talk.R
import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.utils.bundle.BundleKeys
object NotificationUtils {
val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS"
val NOTIFICATION_CHANNEL_MESSAGES = "NOTIFICATION_CHANNEL_MESSAGES"
val NOTIFICATION_CHANNEL_CALLS_V2 = "NOTIFICATION_CHANNEL_CALLS_V2"
val NOTIFICATION_CHANNEL_MESSAGES_V2 = "NOTIFICATION_CHANNEL_MESSAGES_V2"
val NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3"
val NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3"
@TargetApi(Build.VERSION_CODES.O)
fun createNotificationChannel(context: Context,
channelId: String, channelName: String,
channelDescription: String, enableLights: Boolean,
importance: Int) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) {
val channel = NotificationChannel(channelId, channelName,
importance)
channel.description = channelDescription
channel.enableLights(enableLights)
channel.lightColor = R.color.colorPrimary
channel.setSound(null, null)
notificationManager.createNotificationChannel(channel)
}
}
@TargetApi(Build.VERSION_CODES.O)
fun createNotificationChannelGroup(context: Context,
groupId: String, groupName: CharSequence) {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationChannelGroup = NotificationChannelGroup(groupId, groupName)
if (!notificationManager.notificationChannelGroups.contains(notificationChannelGroup)) {
notificationManager.createNotificationChannelGroup(notificationChannelGroup)
}
}
}
fun cancelAllNotificationsForAccount(context: Context?, conversationUser: UserEntity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && context != null) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val statusBarNotifications = notificationManager.activeNotifications
var notification: Notification?
for (statusBarNotification in statusBarNotifications) {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID)) {
notificationManager.cancel(statusBarNotification.id)
}
}
}
}
}
fun cancelExistingNotificationWithId(context: Context?, conversationUser: UserEntity, notificationId: Long) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
context != null) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val statusBarNotifications = notificationManager.activeNotifications
var notification: Notification?
for (statusBarNotification in statusBarNotifications) {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID)) {
notificationManager.cancel(statusBarNotification.id)
}
}
}
}
}
fun findNotificationForRoom(context: Context?,
conversationUser: UserEntity,
roomTokenOrId: String): StatusBarNotification? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
context != null) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val statusBarNotifications = notificationManager.activeNotifications
var notification: Notification?
for (statusBarNotification in statusBarNotifications) {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) {
return statusBarNotification
}
}
}
}
return null
}
fun cancelExistingNotificationsForRoom(context: Context?, conversationUser: UserEntity,
roomTokenOrId: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
context != null) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val statusBarNotifications = notificationManager.activeNotifications
var notification: Notification?
for (statusBarNotification in statusBarNotifications) {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) {
notificationManager.cancel(statusBarNotification.id)
}
}
}
}
}
}