Browse Source

Merge branch 'master' into Icon_size

Marcel Hibbe 1 year ago
parent
commit
9431dd4ddb
39 changed files with 352 additions and 236 deletions
  1. 1 1
      .github/workflows/analysis.yml
  2. 1 1
      .github/workflows/assembleFlavors.yml
  3. 1 1
      .github/workflows/check.yml
  4. 3 3
      .github/workflows/codeql.yml
  5. 1 1
      .github/workflows/command-rebase.yml
  6. 1 1
      .github/workflows/gradle-wrapper-validation.yml
  7. 1 1
      .github/workflows/qa.yml
  8. 2 2
      .github/workflows/scorecard.yml
  9. 8 6
      app/build.gradle
  10. 3 3
      app/src/main/AndroidManifest.xml
  11. 31 25
      app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt
  12. 2 2
      app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt
  13. 33 15
      app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt
  14. 3 4
      app/src/main/java/com/nextcloud/talk/activities/TakePhotoActivity.java
  15. 2 2
      app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt
  16. 3 3
      app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt
  17. 2 2
      app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt
  18. 87 57
      app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
  19. 2 2
      app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt
  20. 7 4
      app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt
  21. 9 6
      app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt
  22. 11 11
      app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt
  23. 38 29
      app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
  24. 34 2
      app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
  25. 2 2
      app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt
  26. 22 9
      app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt
  27. 2 2
      app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt
  28. 2 2
      app/src/main/java/com/nextcloud/talk/openconversations/ListOpenConversationsActivity.kt
  29. 2 2
      app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt
  30. 4 4
      app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt
  31. 10 10
      app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt
  32. 6 5
      app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt
  33. 8 8
      app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
  34. 3 3
      app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt
  35. 1 1
      app/src/main/res/layout/activity_full_screen_media.xml
  36. 1 0
      app/src/main/res/values-eu/strings.xml
  37. 1 1
      app/src/main/res/values-uk/strings.xml
  38. 0 1
      app/src/main/res/values/strings.xml
  39. 2 2
      build.gradle

+ 1 - 1
.github/workflows/analysis.yml

@@ -33,7 +33,7 @@ jobs:
                         echo "pr=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
                         echo "repo=${{ github.event.pull_request.head.repo.full_name }}" >> "$GITHUB_OUTPUT"
                     fi
-            -   uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+            -   uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
                 with:
                     repository: ${{ steps.get-vars.outputs.repo }}
                     ref: ${{ steps.get-vars.outputs.branch }}

+ 1 - 1
.github/workflows/assembleFlavors.yml

@@ -19,7 +19,7 @@ jobs:
       matrix:
         flavor: [ Generic, Gplay ]
     steps:
-      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
       - name: set up JDK 17
         uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
         with:

+ 1 - 1
.github/workflows/check.yml

@@ -19,7 +19,7 @@ jobs:
       matrix:
         task: [ detekt, ktlintCheck ]
     steps:
-      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
       - name: Set up JDK 17
         uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
         with:

+ 3 - 3
.github/workflows/codeql.yml

@@ -26,13 +26,13 @@ jobs:
         language: [ 'java' ]
     steps:
       - name: Checkout repository
-        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
       - name: Set Swap Space
         uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0
         with:
           swap-size-gb: 10
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
+        uses: github/codeql-action/init@00e563ead9f72a8461b24876bee2d0c2e8bd2ee8 # v2.21.5
         with:
           languages: ${{ matrix.language }}
       - name: Set up JDK 17
@@ -46,4 +46,4 @@ jobs:
           echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties"
           ./gradlew assembleDebug
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
+        uses: github/codeql-action/analyze@00e563ead9f72a8461b24876bee2d0c2e8bd2ee8 # v2.21.5

+ 1 - 1
.github/workflows/command-rebase.yml

@@ -31,7 +31,7 @@ jobs:
           reaction-type: "+1"
 
       - name: Checkout the latest code
-        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
         with:
           fetch-depth: 0
           token: ${{ secrets.COMMAND_BOT_PAT }}

+ 1 - 1
.github/workflows/gradle-wrapper-validation.yml

@@ -18,5 +18,5 @@ jobs:
         name: "Validation"
         runs-on: ubuntu-latest
         steps:
-            - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+            - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
             - uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # v1.1.0

+ 1 - 1
.github/workflows/qa.yml

@@ -19,7 +19,7 @@ jobs:
       - name: Check if secrets are available
         run: echo "::set-output name=ok::${{ secrets.KS_PASS != '' }}"
         id: check-secrets
-      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
         if: ${{ steps.check-secrets.outputs.ok == 'true' }}
       - name: set up JDK 17
         uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0

+ 2 - 2
.github/workflows/scorecard.yml

@@ -24,7 +24,7 @@ jobs:
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
         with:
           persist-credentials: false
 
@@ -37,6 +37,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
+        uses: github/codeql-action/upload-sarif@00e563ead9f72a8461b24876bee2d0c2e8bd2ee8 # v2.21.5
         with:
           sarif_file: results.sarif

+ 8 - 6
app/build.gradle

@@ -46,8 +46,8 @@ android {
 
         // mayor.minor.hotfix.increment (for increment: 01-50=Alpha / 51-89=RC / 90-99=stable)
         // xx   .xxx  .xx    .xx
-        versionCode 170100017
-        versionName "17.10.0 Alpha 17"
+        versionCode 170100020
+        versionName "17.10.0 Alpha 20"
 
         flavorDimensions "default"
         renderscriptTargetApi 19
@@ -152,6 +152,7 @@ ext {
     roomVersion = "2.5.2"
     workVersion = "2.8.1"
     espressoVersion = "3.5.1"
+    media3_version = "1.1.1"
 }
 
 configurations.all {
@@ -263,7 +264,9 @@ dependencies {
     implementation "com.afollestad.material-dialogs:lifecycle:${materialDialogsVersion}"
 
     implementation 'com.google.code.gson:gson:2.10.1'
-    implementation 'com.google.android.exoplayer:exoplayer:2.19.0'
+
+    implementation "androidx.media3:media3-exoplayer:$media3_version"
+    implementation "androidx.media3:media3-ui:$media3_version"
 
     implementation 'com.github.chrisbanes:PhotoView:2.3.0'
     implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.27'
@@ -273,8 +276,7 @@ dependencies {
     implementation "io.noties.markwon:ext-tasklist:$markwonVersion"
 
     implementation 'com.github.nextcloud-deps:ImagePicker:2.1.0.2'
-    implementation 'com.elyeproj.libraries:loaderviewlibrary:2.0.0'
-
+    implementation 'io.github.elye:loaderviewlibrary:3.0.0'
     implementation 'org.osmdroid:osmdroid-android:6.1.16'
     implementation ('fr.dudie:nominatim-api:3.4', {
         //noinspection DuplicatePlatformClasses
@@ -284,7 +286,7 @@ dependencies {
     implementation 'androidx.core:core-ktx:1.10.1'
 
     testImplementation 'junit:junit:4.13.2'
-    testImplementation 'org.mockito:mockito-core:5.4.0'
+    testImplementation 'org.mockito:mockito-core:5.5.0'
     testImplementation 'androidx.arch.core:core-testing:2.2.0'
 
     androidTestImplementation "androidx.test:core:1.5.0"

+ 3 - 3
app/src/main/AndroidManifest.xml

@@ -159,17 +159,17 @@
         <activity
             android:name=".activities.FullScreenImageActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
-            android:theme="@style/FullScreenImageTheme"></activity>
+            android:theme="@style/FullScreenImageTheme"/>
 
         <activity
             android:name=".activities.FullScreenMediaActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
-            android:theme="@style/FullScreenMediaTheme"></activity>
+            android:theme="@style/FullScreenMediaTheme"/>
 
         <activity
             android:name=".activities.FullScreenTextViewerActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
-            android:theme="@style/FullScreenTextTheme"></activity>
+            android:theme="@style/FullScreenTextTheme"/>
 
         <activity
             android:name=".activities.TakePhotoActivity"

+ 31 - 25
app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt

@@ -57,7 +57,6 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener
 import android.widget.AdapterView
 import android.widget.FrameLayout
 import android.widget.RelativeLayout
-import android.widget.Toast
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.annotation.DrawableRes
 import androidx.annotation.RequiresApi
@@ -67,6 +66,7 @@ import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
 import com.bluelinelabs.logansquare.LoganSquare
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.adapters.ParticipantDisplayItem
 import com.nextcloud.talk.adapters.ParticipantsAdapter
@@ -427,8 +427,11 @@ class CallActivity : CallBaseActivity() {
                 binding!!.callRecordingIndicator.visibility = View.VISIBLE
                 if (viewState.showStartedInfo) {
                     vibrateShort(context)
-                    Toast.makeText(context, context.resources.getString(R.string.record_active_info), Toast.LENGTH_LONG)
-                        .show()
+                    Snackbar.make(
+                        binding!!.root,
+                        context.resources.getString(R.string.record_active_info),
+                        Snackbar.LENGTH_LONG
+                    ).show()
                 }
             } else if (viewState is RecordingStartingState) {
                 if (isAllowedToStartOrStopRecording) {
@@ -459,10 +462,10 @@ class CallActivity : CallBaseActivity() {
                 }
             } else if (viewState is RecordingErrorState) {
                 if (isAllowedToStartOrStopRecording) {
-                    Toast.makeText(
-                        context,
+                    Snackbar.make(
+                        binding!!.root,
                         context.resources.getString(R.string.record_failed_info),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                 }
                 binding!!.callRecordingIndicator.visibility = View.GONE
@@ -567,10 +570,10 @@ class CallActivity : CallBaseActivity() {
             }
         } else {
             binding!!.microphoneButton.setOnClickListener {
-                Toast.makeText(
-                    context,
+                Snackbar.make(
+                    binding!!.root,
                     R.string.nc_not_allowed_to_activate_audio,
-                    Toast.LENGTH_SHORT
+                    Snackbar.LENGTH_SHORT
                 ).show()
             }
         }
@@ -579,10 +582,10 @@ class CallActivity : CallBaseActivity() {
             binding!!.cameraButton.setOnClickListener { onCameraClick() }
         } else {
             binding!!.cameraButton.setOnClickListener {
-                Toast.makeText(
-                    context,
+                Snackbar.make(
+                    binding!!.root,
                     R.string.nc_not_allowed_to_activate_video,
-                    Toast.LENGTH_SHORT
+                    Snackbar.LENGTH_SHORT
                 ).show()
             }
         }
@@ -612,8 +615,11 @@ class CallActivity : CallBaseActivity() {
                     callRecordingViewModel!!.clickRecordButton()
                 }
             } else {
-                Toast.makeText(context, context.resources.getString(R.string.record_active_info), Toast.LENGTH_LONG)
-                    .show()
+                Snackbar.make(
+                    binding!!.root,
+                    context.resources.getString(R.string.record_active_info),
+                    Snackbar.LENGTH_LONG
+                ).show()
             }
         }
         binding!!.lowerHandButton.setOnClickListener { l: View? -> raiseHandViewModel!!.lowerHand() }
@@ -1437,7 +1443,7 @@ class CallActivity : CallBaseActivity() {
                 }
 
                 override fun onError(e: Throwable) {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                     Log.e(TAG, "Failed to fetch capabilities", e)
                     // unused atm
                 }
@@ -1632,10 +1638,10 @@ class CallActivity : CallBaseActivity() {
     private fun showCallRunningSinceOneHourOrMoreInfo() {
         binding!!.callDuration.setTypeface(null, Typeface.BOLD)
         vibrateShort(context)
-        Toast.makeText(
-            context,
+        Snackbar.make(
+            binding!!.root,
             context.resources.getString(R.string.call_running_since_one_hour),
-            Toast.LENGTH_LONG
+            Snackbar.LENGTH_LONG
         ).show()
     }
 
@@ -1909,7 +1915,7 @@ class CallActivity : CallBaseActivity() {
                 }
 
                 override fun onError(e: Throwable) {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                     Log.e(TAG, "Error while leaving the call", e)
                 }
 
@@ -2091,10 +2097,10 @@ class CallActivity : CallBaseActivity() {
         } else {
             if (peerConnectionFactory == null) {
                 Log.e(TAG, "peerConnectionFactory was null in getOrCreatePeerConnectionWrapperForSessionIdAndType")
-                Toast.makeText(
-                    context,
+                Snackbar.make(
+                    binding!!.root,
                     context.resources.getString(R.string.nc_common_error_sorry),
-                    Toast.LENGTH_LONG
+                    Snackbar.LENGTH_LONG
                 ).show()
                 hangup(true)
                 return null
@@ -2723,10 +2729,10 @@ class CallActivity : CallBaseActivity() {
             }
             raisedHand = true
             val nick = callParticipantModel.nick
-            Toast.makeText(
-                context,
+            Snackbar.make(
+                binding!!.root,
                 String.format(context.resources.getString(R.string.nc_call_raised_hand), nick),
-                Toast.LENGTH_LONG
+                Snackbar.LENGTH_LONG
             ).show()
         }
 

+ 2 - 2
app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt

@@ -32,9 +32,9 @@ import android.util.Log
 import android.view.Menu
 import android.view.MenuItem
 import android.view.View
-import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.FileProvider
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.BuildConfig
 import com.nextcloud.talk.R
 import com.nextcloud.talk.databinding.ActivityFullScreenImageBinding
@@ -133,7 +133,7 @@ class FullScreenImageActivity : AppCompatActivity() {
         // info that 100MB is the limit comes from https://stackoverflow.com/a/53334563
         if (bitmapSize > HUNDRED_MB) {
             Log.e(TAG, "bitmap will be too large to display. It won't be displayed to avoid RuntimeException")
-            Toast.makeText(this, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         } else {
             binding.photoView.setImageBitmap(bitmap)
         }

+ 33 - 15
app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt

@@ -4,9 +4,11 @@
  * @author Marcel Hibbe
  * @author Andy Scherzinger
  * @author Ezhil Shanmugham
+ * @author Parneet Singh
  * Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
  * Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
  * Copyright (C) 2023 Ezhil Shanmugham <ezhil56x.contact@gmail.com>
+ * Copyright (c) 2023 Parneet Singh <gurayaparneet@gmail.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
@@ -30,13 +32,15 @@ import android.view.Menu
 import android.view.MenuItem
 import android.view.View
 import android.view.WindowManager
+import androidx.annotation.OptIn
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.FileProvider
+import androidx.media3.common.MediaItem
+import androidx.media3.common.Player
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.ui.PlayerView
 import autodagger.AutoInjector
-import com.google.android.exoplayer2.MediaItem
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.SimpleExoPlayer
-import com.google.android.exoplayer2.ui.StyledPlayerView
 import com.nextcloud.talk.BuildConfig
 import com.nextcloud.talk.R
 import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -49,7 +53,10 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.Listener {
     lateinit var binding: ActivityFullScreenMediaBinding
 
     private lateinit var path: String
-    private lateinit var player: SimpleExoPlayer
+    private var player: ExoPlayer? = null
+
+    private var playWhenReadyState: Boolean = true
+    private var playBackPosition: Long = 0L
 
     override fun onCreateOptionsMenu(menu: Menu): Boolean {
         menuInflater.inflate(R.menu.menu_preview, menu)
@@ -81,6 +88,7 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.Listener {
         }
     }
 
+    @OptIn(UnstableApi::class)
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
@@ -104,7 +112,7 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.Listener {
         }
 
         binding.playerView.setControllerVisibilityListener(
-            StyledPlayerView.ControllerVisibilityListener { v ->
+            PlayerView.ControllerVisibilityListener { v ->
                 if (v != 0) {
                     hideSystemUI()
                     supportActionBar?.hide()
@@ -119,11 +127,7 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.Listener {
     override fun onStart() {
         super.onStart()
         initializePlayer()
-
-        val mediaItem: MediaItem = MediaItem.fromUri(path)
-        player.setMediaItem(mediaItem)
-        player.prepare()
-        player.play()
+        preparePlayer()
     }
 
     override fun onStop() {
@@ -132,14 +136,28 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.Listener {
     }
 
     private fun initializePlayer() {
-        player = SimpleExoPlayer.Builder(applicationContext).build()
+        player = ExoPlayer.Builder(applicationContext).build()
         binding.playerView.player = player
-        player.playWhenReady = true
-        player.addListener(this)
+        player?.addListener(this)
+    }
+
+    private fun preparePlayer() {
+        val mediaItem: MediaItem = MediaItem.fromUri(path)
+        player?.let { exoPlayer ->
+            exoPlayer.setMediaItem(mediaItem)
+            exoPlayer.playWhenReady = playWhenReadyState
+            exoPlayer.seekTo(playBackPosition)
+            exoPlayer.prepare()
+        }
     }
 
     private fun releasePlayer() {
-        player.release()
+        player?.let { exoPlayer ->
+            playBackPosition = exoPlayer.currentPosition
+            playWhenReadyState = exoPlayer.playWhenReady
+            exoPlayer.release()
+        }
+        player = null
     }
 
     private fun hideSystemUI() {

+ 3 - 4
app/src/main/java/com/nextcloud/talk/activities/TakePhotoActivity.java

@@ -37,8 +37,7 @@ import android.view.OrientationEventListener;
 import android.view.ScaleGestureDetector;
 import android.view.Surface;
 import android.view.View;
-import android.widget.Toast;
-
+import com.google.android.material.snackbar.Snackbar;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.nextcloud.talk.R;
 import com.nextcloud.talk.application.NextcloudTalkApplication;
@@ -234,7 +233,7 @@ public class TakePhotoActivity extends AppCompatActivity {
                 binding.photoPreview.setMediumScale(MEDIUM_SCALE);
             } catch (IllegalArgumentException | ExecutionException | InterruptedException e) {
                 Log.e(TAG, "Error taking picture", e);
-                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
+                Snackbar.make(binding.getRoot(), e.getMessage(), Snackbar.LENGTH_LONG).show();
                 finish();
             }
         }, ContextCompat.getMainExecutor(this));
@@ -326,7 +325,7 @@ public class TakePhotoActivity extends AppCompatActivity {
                     });
             } catch (Exception e) {
                 Log.e(TAG, "error while taking picture", e);
-                Toast.makeText(this, R.string.take_photo_error_deleting_picture, Toast.LENGTH_SHORT).show();
+                Snackbar.make(binding.getRoot(), R.string.take_photo_error_deleting_picture, Snackbar.LENGTH_SHORT).show();
             }
         });
 

+ 2 - 2
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt

@@ -37,9 +37,9 @@ import android.view.MotionEvent
 import android.view.View
 import android.webkit.WebView
 import android.webkit.WebViewClient
-import android.widget.Toast
 import autodagger.AutoInjector
 import coil.load
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
@@ -263,7 +263,7 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) :
             browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
             context.startActivity(browserIntent)
         } else {
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
             Log.e(TAG, "locationGeoLink was null or empty")
         }
     }

+ 3 - 3
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt

@@ -32,12 +32,11 @@ import android.view.MotionEvent
 import android.view.View
 import android.webkit.WebView
 import android.webkit.WebViewClient
-import android.widget.Toast
 import androidx.appcompat.content.res.AppCompatResources
 import autodagger.AutoInjector
 import coil.load
 import com.google.android.flexbox.FlexboxLayout
-import com.nextcloud.android.common.ui.theme.utils.ColorRole
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
@@ -45,6 +44,7 @@ import com.nextcloud.talk.databinding.ItemCustomOutcomingLocationMessageBinding
 import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.models.json.chat.ReadStatus
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
+import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.UriUtils
@@ -249,7 +249,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) :
             browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
             context.startActivity(browserIntent)
         } else {
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
             Log.e(TAG, "locationGeoLink was null or empty")
         }
     }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt

@@ -30,11 +30,11 @@ import android.os.Handler
 import android.os.Looper
 import android.util.Log
 import android.view.View
-import android.widget.Toast
 import androidx.annotation.RequiresApi
 import androidx.core.app.NotificationManagerCompat
 import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.CallActivity
 import com.nextcloud.talk.activities.CallBaseActivity
@@ -210,7 +210,7 @@ class CallNotificationActivity : CallBaseActivity() {
                 }
 
                 is CallNotificationViewModel.GetRoomErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
 
                 else -> {}

+ 87 - 57
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

@@ -83,7 +83,6 @@ import android.widget.RelativeLayout.BELOW
 import android.widget.RelativeLayout.LayoutParams
 import android.widget.SeekBar
 import android.widget.TextView
-import android.widget.Toast
 import androidx.activity.OnBackPressedCallback
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.view.ContextThemeWrapper
@@ -114,6 +113,7 @@ import coil.transform.CircleCropTransformation
 import com.google.android.flexbox.FlexboxLayout
 import com.google.android.material.button.MaterialButton
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.BuildConfig
 import com.nextcloud.talk.R
@@ -348,6 +348,7 @@ class ChatActivity :
         AudioFormat.CHANNEL_IN_MONO,
         AudioFormat.ENCODING_PCM_16BIT
     )
+    private var voiceRecordDuration = 0L
 
     // messy workaround for a mediaPlayer bug, don't delete
     private var lastRecordMediaPosition: Int = 0
@@ -568,7 +569,7 @@ class ChatActivity :
                 }
 
                 is ChatViewModel.GetRoomErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
 
                 else -> {}
@@ -612,7 +613,7 @@ class ChatActivity :
                 }
 
                 is ChatViewModel.JoinRoomErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
 
                 else -> {}
@@ -1003,7 +1004,11 @@ class ChatActivity :
     @SuppressLint("ClickableViewAccessibility")
     private fun initVoiceRecordButton() {
         if (!isVoiceRecordingLocked) {
-            showMicrophoneButton(true)
+            if (binding.messageInputView.messageInput.text!!.isNotEmpty()) {
+                showMicrophoneButton(false)
+            } else {
+                showMicrophoneButton(true)
+            }
         } else if (isVoiceRecordingInProgress) {
             binding.messageInputView.playPauseBtn.visibility = View.GONE
             binding.messageInputView.seekBar.visibility = View.GONE
@@ -1163,13 +1168,13 @@ class ChatActivity :
                         showRecordAudioUi(false)
 
                         voiceRecordEndTime = System.currentTimeMillis()
-                        val voiceRecordDuration = voiceRecordEndTime - voiceRecordStartTime
+                        voiceRecordDuration = voiceRecordEndTime - voiceRecordStartTime
                         if (voiceRecordDuration < MINIMUM_VOICE_RECORD_DURATION) {
                             Log.d(TAG, "voiceRecordDuration: $voiceRecordDuration")
-                            Toast.makeText(
-                                context,
+                            Snackbar.make(
+                                binding.root,
                                 context.getString(R.string.nc_voice_message_hold_to_record_info),
-                                Toast.LENGTH_SHORT
+                                Snackbar.LENGTH_SHORT
                             ).show()
                             stopAndDiscardAudioRecording()
                             return true
@@ -1676,16 +1681,16 @@ class ChatActivity :
         if (conversationUser != null) {
             runOnUiThread {
                 if (currentConversation?.objectType == ObjectType.ROOM) {
-                    Toast.makeText(
-                        context,
+                    Snackbar.make(
+                        binding.root,
                         context.resources.getString(R.string.switch_to_main_room),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                 } else {
-                    Toast.makeText(
-                        context,
+                    Snackbar.make(
+                        binding.root,
                         context.resources.getString(R.string.switch_to_breakout_room),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                 }
             }
@@ -1847,7 +1852,7 @@ class ChatActivity :
                 }
             } catch (e: Exception) {
                 Log.e(TAG, "failed to initialize mediaPlayer", e)
-                Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
             }
         }
     }
@@ -2075,6 +2080,7 @@ class ChatActivity :
 
             try {
                 start()
+                Log.d(TAG, "recording started")
                 isVoiceRecordingInProgress = true
             } catch (e: IllegalStateException) {
                 Log.e(TAG, "start for audio recording failed")
@@ -2087,15 +2093,16 @@ class ChatActivity :
     private fun stopAndSendAudioRecording() {
         if (isVoiceRecordingInProgress) {
             stopAudioRecording()
+            Log.d(TAG, "stopped and sent audio recording")
+            val uri = Uri.fromFile(File(currentVoiceRecordFile))
+            uploadFile(uri.toString(), true)
         }
-
-        val uri = Uri.fromFile(File(currentVoiceRecordFile))
-        uploadFile(uri.toString(), true)
     }
 
     private fun stopAndDiscardAudioRecording() {
         if (isVoiceRecordingInProgress) {
             stopAudioRecording()
+            Log.d(TAG, "stopped and discarded audio recording")
         }
 
         val cachedFile = File(currentVoiceRecordFile)
@@ -2109,12 +2116,17 @@ class ChatActivity :
 
         recorder?.apply {
             try {
-                stop()
+                Log.d(TAG, "recording stopped with $voiceRecordDuration")
+                if (voiceRecordDuration > MINIMUM_VOICE_RECORD_TO_STOP) {
+                    stop()
+                }
                 release()
                 isVoiceRecordingInProgress = false
                 Log.d(TAG, "stopped recorder. isVoiceRecordingInProgress = false")
             } catch (e: java.lang.IllegalStateException) {
                 error("error while stopping recorder!" + e)
+            } catch (e: java.lang.RuntimeException) {
+                error("error while stopping recorder!" + e)
             }
 
             VibrationUtils.vibrateShort(context)
@@ -2365,12 +2377,22 @@ class ChatActivity :
                         )
                     }
                 } catch (e: IllegalStateException) {
-                    Toast.makeText(context, context.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
-                        .show()
+                    context.resources?.getString(R.string.nc_upload_failed)?.let {
+                        Snackbar.make(
+                            binding.root,
+                            it,
+                            Snackbar.LENGTH_LONG
+                        ).show()
+                    }
                     Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
                 } catch (e: IllegalArgumentException) {
-                    Toast.makeText(context, context.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
-                        .show()
+                    context.resources?.getString(R.string.nc_upload_failed)?.let {
+                        Snackbar.make(
+                            binding.root,
+                            it,
+                            Snackbar.LENGTH_LONG
+                        ).show()
+                    }
                     Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
                 }
             }
@@ -2420,20 +2442,22 @@ class ChatActivity :
                             UploadAndShareFilesWorker.requestStoragePermission(this)
                         }
                     } catch (e: IllegalStateException) {
-                        Toast.makeText(
-                            context,
-                            context.resources?.getString(R.string.nc_upload_failed),
-                            Toast.LENGTH_LONG
+                        Snackbar.make(
+                            binding.root,
+                            R.string.nc_upload_failed,
+                            Snackbar.LENGTH_LONG
                         )
                             .show()
                         Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
                     } catch (e: IllegalArgumentException) {
-                        Toast.makeText(
-                            context,
-                            context.resources?.getString(R.string.nc_upload_failed),
-                            Toast.LENGTH_LONG
-                        )
-                            .show()
+                        context.resources?.getString(R.string.nc_upload_failed)?.let {
+                            Snackbar.make(
+                                binding.root,
+                                it,
+                                Snackbar.LENGTH_LONG
+                            )
+                                .show()
+                        }
                         Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
                     }
                 }
@@ -2489,28 +2513,28 @@ class ChatActivity :
                     uploadFiles(filesToUpload)
                 }
             } else {
-                Toast
-                    .makeText(context, context.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG)
+                Snackbar
+                    .make(binding.root, context.getString(R.string.read_storage_no_permission), Snackbar.LENGTH_LONG)
                     .show()
             }
         } else if (requestCode == REQUEST_SHARE_FILE_PERMISSION) {
             if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 showLocalFilePicker()
             } else {
-                Toast.makeText(
-                    context,
+                Snackbar.make(
+                    binding.root,
                     context.getString(R.string.nc_file_storage_permission),
-                    Toast.LENGTH_LONG
+                    Snackbar.LENGTH_LONG
                 ).show()
             }
         } else if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
             if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 // do nothing. user will tap on the microphone again if he wants to record audio..
             } else {
-                Toast.makeText(
-                    context,
+                Snackbar.make(
+                    binding.root,
                     context.getString(R.string.nc_voice_message_missing_audio_permission),
-                    Toast.LENGTH_LONG
+                    Snackbar.LENGTH_LONG
                 ).show()
             }
         } else if (requestCode == REQUEST_READ_CONTACT_PERMISSION) {
@@ -2518,20 +2542,20 @@ class ChatActivity :
                 val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
                 startActivityForResult(intent, REQUEST_CODE_SELECT_CONTACT)
             } else {
-                Toast.makeText(
-                    context,
+                Snackbar.make(
+                    binding.root,
                     context.getString(R.string.nc_share_contact_permission),
-                    Toast.LENGTH_LONG
+                    Snackbar.LENGTH_LONG
                 ).show()
             }
         } else if (requestCode == REQUEST_CAMERA_PERMISSION) {
             if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                Toast
-                    .makeText(context, context.getString(R.string.camera_permission_granted), Toast.LENGTH_LONG)
+                Snackbar
+                    .make(binding.root, context.getString(R.string.camera_permission_granted), Snackbar.LENGTH_LONG)
                     .show()
             } else {
-                Toast
-                    .makeText(context, context.getString(R.string.take_photo_permission), Toast.LENGTH_LONG)
+                Snackbar
+                    .make(binding.root, context.getString(R.string.take_photo_permission), Snackbar.LENGTH_LONG)
                     .show()
             }
         }
@@ -2564,7 +2588,10 @@ class ChatActivity :
                 metaData
             )
         } catch (e: IllegalArgumentException) {
-            Toast.makeText(context, context.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
+            context.resources?.getString(R.string.nc_upload_failed)?.let {
+                Snackbar.make(binding.root, it, Snackbar.LENGTH_LONG)
+                    .show()
+            }
             Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
         }
     }
@@ -3565,7 +3592,7 @@ class ChatActivity :
             if (conversationUser != null) {
                 val pp = ParticipantPermissions(conversationUser!!, it)
                 if (!pp.canStartCall() && currentConversation?.hasCall == false) {
-                    Toast.makeText(context, R.string.startCallForbidden, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.startCallForbidden, Snackbar.LENGTH_LONG).show()
                 } else {
                     ApplicationWideCurrentRoomHolder.getInstance().isDialing = true
                     val callIntent = getIntentForCall(isVoiceOnlyCall, callWithoutNotification)
@@ -3726,7 +3753,7 @@ class ChatActivity :
                 "Deletion of message is skipped because of restrictions by permissions. " +
                     "This method should not have been called!"
             )
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         } else {
             var apiVersion = 1
             // FIXME Fix API checking with guests?
@@ -3751,10 +3778,10 @@ class ChatActivity :
 
                     override fun onNext(t: ChatOverallSingleMessage) {
                         if (t.ocs!!.meta!!.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
-                            Toast.makeText(
-                                context,
+                            Snackbar.make(
+                                binding.root,
                                 R.string.nc_delete_message_leaked_to_matterbridge,
-                                Toast.LENGTH_LONG
+                                Snackbar.LENGTH_LONG
                             ).show()
                         }
                     }
@@ -3766,7 +3793,7 @@ class ChatActivity :
                                 message?.id,
                             e
                         )
-                        Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                        Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                     }
 
                     override fun onComplete() {
@@ -3956,9 +3983,11 @@ class ChatActivity :
 
     private fun showMicrophoneButton(show: Boolean) {
         if (show && CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "voice-message-sharing")) {
+            Log.d(TAG, "Microphone shown")
             binding.messageInputView.messageSendButton.visibility = View.GONE
             binding.messageInputView.recordAudioButton.visibility = View.VISIBLE
         } else {
+            Log.d(TAG, "Microphone hidden")
             binding.messageInputView.messageSendButton.visibility = View.VISIBLE
             binding.messageInputView.recordAudioButton.visibility = View.GONE
         }
@@ -4126,7 +4155,7 @@ class ChatActivity :
 
                     override fun onError(e: Throwable) {
                         Log.e(TAG, "error after clicking on user mention chip", e)
-                        Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                        Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                     }
 
                     override fun onComplete() {
@@ -4160,7 +4189,7 @@ class ChatActivity :
                         )
                         File("$outputDir/$videoName$VIDEO_SUFFIX")
                     } catch (e: IOException) {
-                        Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                        Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                         Log.e(TAG, "error while creating video file", e)
                         null
                     }
@@ -4226,6 +4255,7 @@ class ChatActivity :
         private const val REQUEST_CODE_SELECT_REMOTE_FILES = 888
         private const val OBJECT_MESSAGE: String = "{object}"
         private const val MINIMUM_VOICE_RECORD_DURATION: Int = 1000
+        private const val MINIMUM_VOICE_RECORD_TO_STOP: Int = 200
         private const val VOICE_RECORD_CANCEL_SLIDER_X: Int = -50
         private const val VOICE_RECORD_LOCK_BUTTON_Y: Int = -130
         private const val VOICE_MESSAGE_META_DATA = "{\"messageType\":\"voice-message\"}"

+ 2 - 2
app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt

@@ -29,7 +29,6 @@ import android.os.Handler
 import android.text.TextUtils
 import android.util.Log
 import android.view.View
-import android.widget.Toast
 import androidx.work.Data
 import androidx.work.OneTimeWorkRequest
 import androidx.work.WorkManager
@@ -37,6 +36,7 @@ import autodagger.AutoInjector
 import com.bluelinelabs.conductor.RouterTransaction
 import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
 import com.bluelinelabs.logansquare.LoganSquare
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.api.NcApi
 import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -474,7 +474,7 @@ class AccountVerificationController(args: Bundle? = null) : BaseController(
                 }
             } else {
                 Log.e(TAG, "failed to set active user")
-                Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
             }
         }
     }

+ 7 - 4
app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt

@@ -39,7 +39,6 @@ import android.view.MenuItem
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
-import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import androidx.work.Data
 import androidx.work.OneTimeWorkRequest
@@ -50,6 +49,7 @@ import com.afollestad.materialdialogs.MaterialDialog
 import com.afollestad.materialdialogs.bottomsheets.BottomSheet
 import com.afollestad.materialdialogs.datetime.dateTimePicker
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
@@ -594,12 +594,15 @@ class ConversationInfoActivity :
                 }
 
                 override fun onNext(genericOverall: GenericOverall) {
-                    Toast.makeText(context, context.getString(R.string.nc_clear_history_success), Toast.LENGTH_LONG)
-                        .show()
+                    Snackbar.make(
+                        binding.root,
+                        context.getString(R.string.nc_clear_history_success),
+                        Snackbar.LENGTH_LONG
+                    ).show()
                 }
 
                 override fun onError(e: Throwable) {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                     Log.e(TAG, "failed to clear chat history", e)
                 }
 

+ 9 - 6
app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt

@@ -4,9 +4,9 @@ import android.content.Intent
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
-import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.databinding.ActivityConversationInfoBinding
@@ -145,14 +145,17 @@ class GuestAccessHelper(
 
         override fun onError(e: Throwable) {
             val message = context.getString(R.string.nc_guest_access_resend_invitations_failed)
-            Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
+            Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show()
             Log.e(TAG, message, e)
         }
 
         override fun onComplete() {
             if (resendInvitationsResult.successful) {
-                Toast.makeText(context, R.string.nc_guest_access_resend_invitations_successful, Toast.LENGTH_SHORT)
-                    .show()
+                Snackbar.make(
+                    binding.root,
+                    R.string.nc_guest_access_resend_invitations_successful,
+                    Snackbar.LENGTH_SHORT
+                ).show()
             }
         }
     }
@@ -167,7 +170,7 @@ class GuestAccessHelper(
 
         override fun onError(e: Throwable) {
             val message = context.getString(R.string.nc_guest_access_allow_failed)
-            Toast.makeText(context, message, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
             Log.e(TAG, message, e)
         }
 
@@ -210,7 +213,7 @@ class GuestAccessHelper(
 
         override fun onError(e: Throwable) {
             val message = context.getString(R.string.nc_guest_access_password_failed)
-            Toast.makeText(context, message, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
             Log.e(TAG, message, e)
         }
 

+ 11 - 11
app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt

@@ -31,12 +31,12 @@ import android.util.Log
 import android.view.Menu
 import android.view.MenuItem
 import android.view.View
-import android.widget.Toast
 import androidx.core.net.toFile
 import androidx.core.view.ViewCompat
 import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
 import com.github.dhaval2404.imagepicker.ImagePicker
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
 import com.nextcloud.talk.api.NcApi
@@ -141,7 +141,7 @@ class ConversationInfoEditActivity :
                 }
 
                 is ConversationInfoEditViewModel.GetRoomErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
 
                 is ConversationInfoEditViewModel.UploadAvatarSuccessState -> {
@@ -150,7 +150,7 @@ class ConversationInfoEditActivity :
                 }
 
                 is ConversationInfoEditViewModel.UploadAvatarErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
 
                 is ConversationInfoEditViewModel.DeleteAvatarSuccessState -> {
@@ -159,7 +159,7 @@ class ConversationInfoEditActivity :
                 }
 
                 is ConversationInfoEditViewModel.DeleteAvatarErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
 
                 else -> {}
@@ -249,10 +249,10 @@ class ConversationInfoEditActivity :
                 }
 
                 override fun onError(e: Throwable) {
-                    Toast.makeText(
-                        applicationContext,
+                    Snackbar.make(
+                        binding.root,
                         context.getString(R.string.default_error_msg),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                     Log.e(TAG, "Error while saving conversation name", e)
                 }
@@ -289,10 +289,10 @@ class ConversationInfoEditActivity :
                 }
 
                 override fun onError(e: Throwable) {
-                    Toast.makeText(
-                        applicationContext,
+                    Snackbar.make(
+                        binding.root,
                         context.getString(R.string.default_error_msg),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                     Log.e(TAG, "Error while saving conversation description", e)
                 }
@@ -315,7 +315,7 @@ class ConversationInfoEditActivity :
             }
 
             ImagePicker.RESULT_ERROR -> {
-                Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
+                Snackbar.make(binding.root, ImagePicker.getError(data), Snackbar.LENGTH_SHORT).show()
             }
 
             else -> {

+ 38 - 29
app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt

@@ -50,7 +50,6 @@ import android.view.MotionEvent
 import android.view.View
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
-import android.widget.Toast
 import androidx.activity.OnBackPressedCallback
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.widget.SearchView
@@ -68,6 +67,7 @@ import coil.transform.CircleCropTransformation
 import com.google.android.material.appbar.AppBarLayout
 import com.google.android.material.button.MaterialButton
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
@@ -267,7 +267,7 @@ class ConversationsListActivity :
             fetchRooms()
         } else {
             Log.e(TAG, "userManager.currentUser.blockingGet() returned null")
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         }
 
         showSearchOrToolbar()
@@ -309,7 +309,7 @@ class ConversationsListActivity :
             )
         } else {
             Log.e(TAG, "currentUser was null in loadUserAvatar")
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         }
     }
 
@@ -956,9 +956,11 @@ class ConversationsListActivity :
                     selectedMessageId = messageItem.messageEntry.messageId
                     showConversationByToken(conversationToken)
                 }
+
                 LoadMoreResultsItem.VIEW_TYPE -> {
                     loadMoreMessages()
                 }
+
                 ConversationItem.VIEW_TYPE -> {
                     handleConversation((Objects.requireNonNull(item) as ConversationItem).model)
                 }
@@ -989,14 +991,14 @@ class ConversationsListActivity :
                 ) {
                     handleSharedData()
                 } else {
-                    Toast.makeText(context, R.string.send_to_forbidden, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.send_to_forbidden, Snackbar.LENGTH_LONG).show()
                 }
             } else if (forwardMessage) {
                 if (hasChatPermission && !isReadOnlyConversation(selectedConversation!!)) {
                     openConversation(intent.getStringExtra(KEY_FORWARD_MSG_TEXT))
                     forwardMessage = false
                 } else {
-                    Toast.makeText(context, R.string.send_to_forbidden, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.send_to_forbidden, Snackbar.LENGTH_LONG).show()
                 }
             } else {
                 openConversation()
@@ -1023,8 +1025,8 @@ class ConversationsListActivity :
         } else if (filesToShare != null && filesToShare!!.isNotEmpty()) {
             showSendFilesConfirmDialog()
         } else {
-            Toast
-                .makeText(context, context.resources.getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG)
+            Snackbar
+                .make(binding.root, context.resources.getString(R.string.nc_common_error_sorry), Snackbar.LENGTH_LONG)
                 .show()
         }
     }
@@ -1125,18 +1127,18 @@ class ConversationsListActivity :
                         }
                     }
                     if (filesToShare!!.isEmpty() && textToPaste!!.isEmpty()) {
-                        Toast.makeText(
-                            context,
+                        Snackbar.make(
+                            binding.root,
                             context.resources.getString(R.string.nc_common_error_sorry),
-                            Toast.LENGTH_LONG
+                            Snackbar.LENGTH_LONG
                         ).show()
                         Log.e(TAG, "failed to get data from intent")
                     }
                 } catch (e: Exception) {
-                    Toast.makeText(
-                        context,
+                    Snackbar.make(
+                        binding.root,
                         context.resources.getString(R.string.nc_common_error_sorry),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                     Log.e(TAG, "Something went wrong when extracting data from intent")
                 }
@@ -1146,10 +1148,10 @@ class ConversationsListActivity :
 
     private fun upload() {
         if (selectedConversation == null) {
-            Toast.makeText(
-                context,
+            Snackbar.make(
+                binding.root,
                 context.resources.getString(R.string.nc_common_error_sorry),
-                Toast.LENGTH_LONG
+                Snackbar.LENGTH_LONG
             ).show()
             Log.e(TAG, "not able to upload any files because conversation was null.")
             return
@@ -1164,21 +1166,25 @@ class ConversationsListActivity :
                 )
             }
         } catch (e: IllegalArgumentException) {
-            Toast.makeText(context, context.resources.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, context.resources.getString(R.string.nc_upload_failed), Snackbar.LENGTH_LONG)
+                .show()
             Log.e(TAG, "Something went wrong when trying to upload file", e)
         }
     }
 
     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults)
-        if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION &&
-            grantResults.isNotEmpty() &&
-            grantResults[0] == PackageManager.PERMISSION_GRANTED
-        ) {
-            Log.d(TAG, "upload starting after permissions were granted")
-            showSendFilesConfirmDialog()
-        } else {
-            Toast.makeText(context, context.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG).show()
+        if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION) {
+            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                Log.d(TAG, "upload starting after permissions were granted")
+                showSendFilesConfirmDialog()
+            } else {
+                Snackbar.make(
+                    binding.root,
+                    context.getString(R.string.read_storage_no_permission),
+                    Snackbar.LENGTH_LONG
+                ).show()
+            }
         }
     }
 
@@ -1186,10 +1192,10 @@ class ConversationsListActivity :
         if (CallActivity.active &&
             selectedConversation!!.token != ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken
         ) {
-            Toast.makeText(
-                context,
+            Snackbar.make(
+                binding.root,
                 context.getString(R.string.restrict_join_other_room_while_call),
-                Toast.LENGTH_LONG
+                Snackbar.LENGTH_LONG
             ).show()
             return
         }
@@ -1217,6 +1223,7 @@ class ConversationsListActivity :
                 EventStatus.EventType.CONVERSATION_UPDATE -> if (eventStatus.isAllGood && !isRefreshing) {
                     fetchRooms()
                 }
+
                 else -> {}
             }
         }
@@ -1493,7 +1500,9 @@ class ConversationsListActivity :
         filterState[FilterConversationFragment.UNREAD] = unread
     }
 
-    fun setFilterableItems(items: MutableList<AbstractFlexibleItem<*>>) { filterableConversationItems = items }
+    fun setFilterableItems(items: MutableList<AbstractFlexibleItem<*>>) {
+        filterableConversationItems = items
+    }
 
     fun updateFilterConversationButtonColor() {
         if (filterState.containsValue(true)) {

+ 34 - 2
app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt

@@ -23,10 +23,12 @@
  */
 package com.nextcloud.talk.jobs
 
+import android.Manifest
 import android.app.Notification
 import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.graphics.Bitmap
 import android.media.AudioAttributes
 import android.media.MediaPlayer
@@ -40,7 +42,8 @@ import android.service.notification.StatusBarNotification
 import android.text.TextUtils
 import android.util.Base64
 import android.util.Log
-import android.widget.Toast
+import android.view.View
+import androidx.core.app.ActivityCompat
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import androidx.core.app.Person
@@ -53,6 +56,7 @@ import androidx.work.Worker
 import androidx.work.WorkerParameters
 import autodagger.AutoInjector
 import com.bluelinelabs.logansquare.LoganSquare
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.BuildConfig
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.MainActivity
@@ -325,7 +329,11 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
                     Log.e(TAG, "Failed to get NC notification", e)
                     if (BuildConfig.DEBUG) {
                         Handler(Looper.getMainLooper()).post {
-                            Toast.makeText(context, "Failed to get NC notification", Toast.LENGTH_LONG).show()
+                            Snackbar.make(
+                                View(applicationContext),
+                                "Failed to get NC notification",
+                                Snackbar.LENGTH_LONG
+                            ).show()
                         }
                     }
                 }
@@ -734,6 +742,18 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
 
     private fun sendNotification(notificationId: Int, notification: Notification) {
         Log.d(TAG, "show notification with id $notificationId")
+        if (ActivityCompat.checkSelfPermission(
+                applicationContext,
+                Manifest.permission.POST_NOTIFICATIONS
+            ) != PackageManager.PERMISSION_GRANTED
+        ) {
+            // here to request the missing permissions, and then overriding
+            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            //                                          int[] grantResults)
+            // to handle the case where the user grants the permission. See the documentation
+            // for ActivityCompat#requestPermissions for more details.
+            return
+        }
         notificationManager.notify(notificationId, notification)
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -902,6 +922,18 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
                             .build()
 
                         val notificationId: Int = SystemClock.uptimeMillis().toInt()
+                        if (ActivityCompat.checkSelfPermission(
+                                applicationContext,
+                                Manifest.permission.POST_NOTIFICATIONS
+                            ) != PackageManager.PERMISSION_GRANTED
+                        ) {
+                            // here to request the missing permissions, and then overriding
+                            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+                            //                                          int[] grantResults)
+                            // to handle the case where the user grants the permission. See the documentation
+                            // for ActivityCompat#requestPermissions for more details.
+                            return
+                        }
                         notificationManager.notify(notificationId, notification)
                         Log.d(TAG, "'you missed a call' notification was created")
                     }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt

@@ -32,11 +32,11 @@ import android.view.Menu
 import android.view.MenuItem
 import android.view.inputmethod.EditorInfo
 import android.widget.AdapterView
-import android.widget.Toast
 import androidx.appcompat.widget.SearchView
 import androidx.core.view.MenuItemCompat
 import androidx.preference.PreferenceManager
 import autodagger.AutoInjector
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
 import com.nextcloud.talk.adapters.GeocodingAdapter
@@ -216,7 +216,7 @@ class GeocodingActivity :
             }
         } catch (e: Exception) {
             Log.e(TAG, "Failed to get geocoded addresses", e)
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         }
         updateResultsOnMainThread(results)
     }

+ 22 - 9
app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt

@@ -40,7 +40,6 @@ import android.view.Menu
 import android.view.MenuItem
 import android.view.View
 import android.view.inputmethod.EditorInfo
-import android.widget.Toast
 import androidx.activity.OnBackPressedCallback
 import androidx.appcompat.widget.SearchView
 import androidx.core.content.PermissionChecker
@@ -48,6 +47,7 @@ import androidx.core.content.res.ResourcesCompat
 import androidx.core.view.MenuItemCompat
 import androidx.preference.PreferenceManager
 import autodagger.AutoInjector
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
 import com.nextcloud.talk.api.NcApi
@@ -319,7 +319,11 @@ class LocationPickerActivity :
 
         binding.centerMapButton.setOnClickListener {
             if (myLocation.latitude == COORDINATE_ZERO && myLocation.longitude == COORDINATE_ZERO) {
-                Toast.makeText(context, context.getString(R.string.nc_location_unknown), Toast.LENGTH_LONG).show()
+                Snackbar.make(
+                    binding.root,
+                    context.getString(R.string.nc_location_unknown),
+                    Snackbar.LENGTH_LONG
+                ).show()
             } else {
                 mapController.animateTo(myLocation)
                 moveToCurrentLocation = true
@@ -341,11 +345,13 @@ class LocationPickerActivity :
                             setLocationDescription(isGpsLocation = true, isGeocodedResult = false)
                             moveToCurrentLocation = false
                         }
+
                         geocodingResult != null -> {
                             binding.shareLocation.isClickable = true
                             setLocationDescription(isGpsLocation = false, isGeocodedResult = true)
                             geocodingResult = null
                         }
+
                         else -> {
                             binding.shareLocation.isClickable = true
                             setLocationDescription(isGpsLocation = false, isGeocodedResult = false)
@@ -377,6 +383,7 @@ class LocationPickerActivity :
                         this
                     )
                 }
+
                 locationManager!!.isProviderEnabled(LocationManager.GPS_PROVIDER) -> {
                     locationManager!!.requestLocationUpdates(
                         LocationManager.GPS_PROVIDER,
@@ -386,6 +393,7 @@ class LocationPickerActivity :
                     )
                     Log.d(TAG, "LocationManager.NETWORK_PROVIDER falling back to LocationManager.GPS_PROVIDER")
                 }
+
                 else -> {
                     Log.e(
                         TAG,
@@ -393,16 +401,16 @@ class LocationPickerActivity :
                             " and there is no alternative like UnifiedNlp installed. Furthermore no GPS is " +
                             "supported."
                     )
-                    Toast.makeText(context, context.getString(R.string.nc_location_unknown), Toast.LENGTH_LONG)
+                    Snackbar.make(binding.root, context.getString(R.string.nc_location_unknown), Snackbar.LENGTH_LONG)
                         .show()
                 }
             }
         } catch (e: SecurityException) {
             Log.e(TAG, "Error when requesting location updates. Permissions may be missing.", e)
-            Toast.makeText(context, context.getString(R.string.nc_location_unknown), Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, context.getString(R.string.nc_location_unknown), Snackbar.LENGTH_LONG).show()
         } catch (e: Exception) {
             Log.e(TAG, "Error when requesting location updates.", e)
-            Toast.makeText(context, context.getString(R.string.nc_common_error_sorry), Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, context.getString(R.string.nc_common_error_sorry), Snackbar.LENGTH_LONG).show()
         }
     }
 
@@ -413,11 +421,13 @@ class LocationPickerActivity :
                 binding.placeName.visibility = View.GONE
                 binding.placeName.text = ""
             }
+
             isGeocodedResult -> {
                 binding.shareLocationDescription.text = context!!.getText(R.string.nc_share_this_location)
                 binding.placeName.visibility = View.VISIBLE
                 binding.placeName.text = geocodingResult?.displayName
             }
+
             else -> {
                 binding.shareLocationDescription.text = context!!.getText(R.string.nc_share_this_location)
                 binding.placeName.visibility = View.GONE
@@ -476,7 +486,7 @@ class LocationPickerActivity :
 
                 override fun onError(e: Throwable) {
                     Log.e(TAG, "error when trying to share location", e)
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                     finish()
                 }
 
@@ -531,8 +541,11 @@ class LocationPickerActivity :
         if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE && areAllGranted(grantResults)) {
             initMap()
         } else {
-            Toast.makeText(context, context!!.getString(R.string.nc_location_permission_required), Toast.LENGTH_LONG)
-                .show()
+            Snackbar.make(
+                binding.root,
+                context!!.getString(R.string.nc_location_permission_required),
+                Snackbar.LENGTH_LONG
+            ).show()
         }
     }
 
@@ -556,7 +569,7 @@ class LocationPickerActivity :
             address = nominatimClient!!.getAddress(lon, lat)
         } catch (e: Exception) {
             Log.e(TAG, "Failed to get geocoded addresses", e)
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         }
         updateResultOnMainThread(lat, lon, address?.displayName)
     }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt

@@ -30,11 +30,11 @@ import android.text.TextUtils
 import android.view.Menu
 import android.view.MenuItem
 import android.view.View
-import android.widget.Toast
 import androidx.activity.OnBackPressedCallback
 import androidx.appcompat.widget.SearchView
 import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
 import com.nextcloud.talk.adapters.items.LoadMoreResultsItem
@@ -127,7 +127,7 @@ class MessageSearchActivity : BaseActivity() {
 
     private fun showError() {
         displayLoading(false)
-        Toast.makeText(this, "Error while searching", Toast.LENGTH_SHORT).show()
+        Snackbar.make(binding.root, "Error while searching", Snackbar.LENGTH_SHORT).show()
     }
 
     private fun showLoading() {

+ 2 - 2
app/src/main/java/com/nextcloud/talk/openconversations/ListOpenConversationsActivity.kt

@@ -24,9 +24,9 @@ import android.content.Intent
 import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.View
-import android.widget.Toast
 import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
 import com.nextcloud.talk.api.NcApi
@@ -112,7 +112,7 @@ class ListOpenConversationsActivity : BaseActivity() {
                     binding.emptyList.emptyListViewText.visibility = View.VISIBLE
                 }
                 is OpenConversationsViewModel.FetchConversationsErrorState -> {
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
                 else -> {}
             }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt

@@ -30,12 +30,12 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.EditText
-import android.widget.Toast
 import androidx.fragment.app.DialogFragment
 import androidx.lifecycle.ViewModelProvider
 import androidx.recyclerview.widget.LinearLayoutManager
 import autodagger.AutoInjector
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.databinding.DialogPollCreateBinding
@@ -172,7 +172,7 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener
     private fun showError() {
         dismiss()
         Log.e(TAG, "Failed to create poll")
-        Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+        Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
     }
 
     override fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) {

+ 4 - 4
app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt

@@ -32,12 +32,12 @@ import android.widget.CheckBox
 import android.widget.CompoundButton
 import android.widget.LinearLayout
 import android.widget.RadioButton
-import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.databinding.DialogPollVoteBinding
@@ -94,10 +94,10 @@ class PollVoteFragment : Fragment() {
                 PollVoteViewModel.InitialState -> {}
                 is PollVoteViewModel.PollVoteFailedState -> {
                     Log.e(TAG, "Failed to vote on poll.")
-                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
                 }
                 is PollVoteViewModel.PollVoteHiddenSuccessState -> {
-                    Toast.makeText(context, R.string.polls_voted_hidden_success, Toast.LENGTH_LONG).show()
+                    Snackbar.make(binding.root, R.string.polls_voted_hidden_success, Snackbar.LENGTH_LONG).show()
                     parentViewModel.dismissDialog()
                 }
                 is PollVoteViewModel.PollVoteSuccessState -> {
@@ -182,7 +182,7 @@ class PollVoteFragment : Fragment() {
                             viewModel.selectOption(index, false)
                         } else {
                             checkBox.isChecked = false
-                            Toast.makeText(context, R.string.polls_max_votes_reached, Toast.LENGTH_LONG).show()
+                            Snackbar.make(binding.root, R.string.polls_max_votes_reached, Snackbar.LENGTH_LONG).show()
                         }
                     } else {
                         viewModel.deSelectOption(index)

+ 10 - 10
app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt

@@ -40,7 +40,6 @@ import android.view.Menu
 import android.view.MenuItem
 import android.view.View
 import android.view.ViewGroup
-import android.widget.Toast
 import androidx.annotation.DrawableRes
 import androidx.core.content.ContextCompat
 import androidx.core.net.toFile
@@ -49,6 +48,7 @@ import androidx.recyclerview.widget.RecyclerView
 import autodagger.AutoInjector
 import com.github.dhaval2404.imagepicker.ImagePicker
 import com.github.dhaval2404.imagepicker.ImagePicker.Companion.getError
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
@@ -459,13 +459,13 @@ class ProfileActivity : BaseActivity() {
 
                         override fun onError(e: Throwable) {
                             item.text = userInfo?.getValueByField(item.field)
-                            Toast.makeText(
-                                applicationContext,
+                            Snackbar.make(
+                                binding.root,
                                 String.format(
                                     resources!!.getString(R.string.failed_to_save),
                                     item.field
                                 ),
-                                Toast.LENGTH_LONG
+                                Snackbar.LENGTH_LONG
                             ).show()
                             adapter!!.updateFilteredList()
                             adapter!!.notifyDataSetChanged()
@@ -492,8 +492,8 @@ class ProfileActivity : BaseActivity() {
             if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 pickImage.takePicture()
             } else {
-                Toast
-                    .makeText(context, context.getString(R.string.take_photo_permission), Toast.LENGTH_LONG)
+                Snackbar
+                    .make(binding.root, context.getString(R.string.take_photo_permission), Snackbar.LENGTH_LONG)
                     .show()
             }
         }
@@ -510,7 +510,7 @@ class ProfileActivity : BaseActivity() {
                 ) { uploadAvatar(it.toFile()) }
             }
             ImagePicker.RESULT_ERROR -> {
-                Toast.makeText(this, getError(data), Toast.LENGTH_SHORT).show()
+                Snackbar.make(binding.root, getError(data), Snackbar.LENGTH_SHORT).show()
             }
             else -> {
                 Log.i(TAG, "Task Cancelled")
@@ -550,10 +550,10 @@ class ProfileActivity : BaseActivity() {
                 }
 
                 override fun onError(e: Throwable) {
-                    Toast.makeText(
-                        applicationContext,
+                    Snackbar.make(
+                        binding.root,
                         context.getString(R.string.default_error_msg),
-                        Toast
+                        Snackbar
                             .LENGTH_LONG
                     ).show()
                     Log.e(TAG, "Error uploading avatar", e)

+ 6 - 5
app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt

@@ -25,8 +25,9 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.util.Log
-import android.widget.Toast
+import android.view.View
 import autodagger.AutoInjector
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.api.NcApi
 import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -91,12 +92,12 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() {
                     // combined with addNextIntentWithParentStack. For further reading, see
                     // https://developer.android.com/develop/ui/views/notifications/navigation#DirectEntry
                     // As we are using the conductor framework it might be hard the combine this or to keep an overview.
-                    // For this reason there is only a toast for now until we got rid of conductor.
+                    // For this reason there is only a Snackbar for now until we got rid of conductor.
 
-                    Toast.makeText(
-                        context,
+                    Snackbar.make(
+                        View(context),
                         context.resources.getString(R.string.nc_all_ok_operation),
-                        Toast.LENGTH_LONG
+                        Snackbar.LENGTH_LONG
                     ).show()
                 }
 

+ 8 - 8
app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt

@@ -52,7 +52,6 @@ import android.view.View
 import android.view.WindowManager
 import android.widget.EditText
 import android.widget.LinearLayout
-import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.view.ContextThemeWrapper
 import androidx.core.content.ContextCompat
@@ -63,6 +62,7 @@ import androidx.work.WorkManager
 import autodagger.AutoInjector
 import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
 import com.google.android.material.textfield.TextInputLayout
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.talk.BuildConfig
@@ -471,7 +471,7 @@ class SettingsActivity : BaseActivity() {
     private fun removeCurrentAccount() {
         val otherUserExists = userManager.scheduleUserForDeletionWithId(currentUser!!.id!!).blockingGet()
         val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
-        WorkManager.getInstance(this).enqueue(accountRemovalWork)
+        WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork)
         if (otherUserExists) {
             // TODO: find better solution once Conductor is removed
             finish()
@@ -888,10 +888,10 @@ class SettingsActivity : BaseActivity() {
         } else {
             appPreferences.setPhoneBookIntegration(false)
             binding.settingsPhoneBookIntegrationSwitch.isChecked = appPreferences.isPhoneBookIntegrationEnabled
-            Toast.makeText(
-                context,
+            Snackbar.make(
+                binding.root,
                 context.resources.getString(R.string.no_phone_book_integration_due_to_permissions),
-                Toast.LENGTH_LONG
+                Snackbar.LENGTH_LONG
             ).show()
         }
     }
@@ -1082,12 +1082,12 @@ class SettingsActivity : BaseActivity() {
                     val statusCode = genericOverall.ocs?.meta?.statusCode
                     if (statusCode == HTTP_CODE) {
                         dialog.dismiss()
-                        Toast.makeText(
-                            context,
+                        Snackbar.make(
+                            binding.root,
                             context.resources.getString(
                                 R.string.nc_settings_phone_book_integration_phone_number_dialog_success
                             ),
-                            Toast.LENGTH_LONG
+                            Snackbar.LENGTH_LONG
                         ).show()
                     } else {
                         textInputLayout.helperText = context.resources.getString(

+ 3 - 3
app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt

@@ -29,13 +29,13 @@ import android.util.Log
 import android.view.View
 import android.widget.ImageView
 import android.widget.ProgressBar
-import android.widget.Toast
 import androidx.core.content.FileProvider
 import androidx.emoji2.widget.EmojiTextView
 import androidx.work.Data
 import androidx.work.OneTimeWorkRequest
 import androidx.work.WorkInfo
 import androidx.work.WorkManager
+import com.google.android.material.snackbar.Snackbar
 import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.FullScreenImageActivity
 import com.nextcloud.talk.activities.FullScreenMediaActivity
@@ -122,7 +122,7 @@ class FileViewerUtils(private val context: Context, private val user: User) {
                     "support it, it can't be handled by an external app and there is no link " +
                     "to open it in the nextcloud files app"
             )
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(View(context), R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         }
     }
 
@@ -175,7 +175,7 @@ class FileViewerUtils(private val context: Context, private val user: User) {
             }
         } else {
             Log.e(TAG, "can't open file with unknown mimetype")
-            Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+            Snackbar.make(View(context), R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
         }
     }
 

+ 1 - 1
app/src/main/res/layout/activity_full_screen_media.xml

@@ -36,7 +36,7 @@
         android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
         app:popupTheme="@style/ThemeOverlay.AppCompat.Dark" />
 
-    <com.google.android.exoplayer2.ui.StyledPlayerView
+    <androidx.media3.ui.PlayerView
         android:id="@+id/player_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"

+ 1 - 0
app/src/main/res/values-eu/strings.xml

@@ -10,6 +10,7 @@
     <string name="avatar">Avatarra</string>
     <string name="away">Kanpoan</string>
     <string name="call_more_actions_dialog_headline">Dei-ezarpen aurreratuak</string>
+    <string name="call_running_since_one_hour">Deia ordubetez egon da martxan.</string>
     <string name="call_without_notification">Deitu jakinarazpenik gabe</string>
     <string name="camera_permission_granted">Kameraren baimena eman da. Aukeratu kamera berriro.</string>
     <string name="choose_avatar_from_cloud">Aukeratu avatarra cloudetik</string>

+ 1 - 1
app/src/main/res/values-uk/strings.xml

@@ -166,7 +166,7 @@
     <string name="nc_guest_access_password_title">Захист паролем</string>
     <string name="nc_guest_access_password_weak_alert_title">Слабкий пароль</string>
     <string name="nc_guest_access_resend_invitations">Повторно надіслати запрошення</string>
-    <string name="nc_guest_access_resend_invitations_failed">Запрошення не були надіслані через помилку.</string>
+    <string name="nc_guest_access_resend_invitations_failed">Запрошення не було надіслано через помилку, що виникла.</string>
     <string name="nc_guest_access_resend_invitations_successful">Запрошення було розіслано повторно.</string>
     <string name="nc_guest_access_share_link">Поділитися посиланням на бесіду</string>
     <string name="nc_hint_enter_a_message">Введіть повідомлення ...</string>

+ 0 - 1
app/src/main/res/values/strings.xml

@@ -376,7 +376,6 @@ How to translate with transifex:
     <string name="nc_add_attachment">Add attachment</string>
     <string name="emoji_category_recent">Recent</string>
     <string name="emoji_backspace">Backspace</string>
-    <string name="emoji_search">Search emoji</string>
 
     <!-- Conversation info guest access -->
     <string name="nc_guest_access">Guest access</string>

+ 2 - 2
build.gradle

@@ -24,7 +24,7 @@
 buildscript {
 
     ext {
-        kotlinVersion = '1.8.22'
+        kotlinVersion = '1.9.10'
     }
 
     repositories {
@@ -34,7 +34,7 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:8.1.0'
+        classpath 'com.android.tools.build:gradle:8.1.1'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
         classpath "org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}"
         classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:5.1.3'