Skip to content

Commit

Permalink
Allows to use a microphone for Brave Talk in the background on Android
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeyZhukovsky committed Dec 13, 2024
1 parent 5d7fb2b commit 42c5569
Show file tree
Hide file tree
Showing 20 changed files with 356 additions and 5 deletions.
3 changes: 3 additions & 0 deletions android/brave_java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ brave_java_sources = [
"../../brave/android/java/org/chromium/chrome/browser/local_database/SavedBandwidthTable.java",
"../../brave/android/java/org/chromium/chrome/browser/local_database/TopSiteTable.java",
"../../brave/android/java/org/chromium/chrome/browser/media/BravePictureInPictureActivity.java",
"../../brave/android/java/org/chromium/chrome/browser/media/ui/BraveMediaNotificationControllerDelegate.java",
"../../brave/android/java/org/chromium/chrome/browser/media/ui/BraveMediaNotificationControllerServices.java",
"../../brave/android/java/org/chromium/chrome/browser/media/ui/BraveMediaSessionTabHelper.java",
"../../brave/android/java/org/chromium/chrome/browser/misc_metrics/MiscAndroidMetricsConnectionErrorHandler.java",
"../../brave/android/java/org/chromium/chrome/browser/misc_metrics/MiscAndroidMetricsFactory.java",
"../../brave/android/java/org/chromium/chrome/browser/multiwindow/BraveMultiInstanceManagerApi31.java",
Expand Down
7 changes: 7 additions & 0 deletions android/java/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@
</intent-filter>
</service>

<service android:name="org.chromium.chrome.browser.media.ui.BraveMediaNotificationControllerServices$PlaybackListenerMicService"
android:foregroundServiceType="mediaPlayback|microphone" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</service>

<!-- This activity is used for search provider widget -->
<activity android:name="org.chromium.chrome.browser.searchwidget.SearchWidgetProviderActivity"
android:theme="@style/Theme.Chromium.SearchActivity"
Expand Down
1 change: 1 addition & 0 deletions android/java/AndroidManifest_user_permissions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
15 changes: 15 additions & 0 deletions android/java/apk_for_test.flags
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
-keep class org.chromium.components.browser_ui.media.BraveMediaSessionHelper { *; }
-keep class org.chromium.content_public.browser.MediaSessionObserver { *; }
-keep class org.chromium.ui.ViewProvider { *; }
-keep class org.chromium.components.browser_ui.media.MediaSessionHelper { *; }
-keep class org.chromium.components.browser_ui.notifications.ForegroundServiceUtils { *; }
-keep class org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerDelegate { *; }
-keep class org.chromium.chrome.browser.media.ui.MediaSessionTabHelper { *; }
-keep class org.chromium.components.browser_ui.notifications.BraveForegroundServiceUtils { *; }
-keep class org.chromium.chrome.browser.media.ui.BraveMediaNotificationControllerDelegate { *; }
-keep class org.chromium.chrome.browser.media.ui.BraveMediaSessionTabHelper { *; }

-keep class org.chromium.chrome.browser.bookmarks.BookmarkBridge {
*** mNativeBookmarkBridge;
Expand Down Expand Up @@ -900,3 +907,11 @@
-keep class org.chromium.chrome.browser.settings.SettingsIntentUtil {
*** createIntent(...);
}

-keep class org.chromium.chrome.browser.media.ui.ChromeMediaNotificationControllerDelegate {
*** getContext(...);
}

-keep class org.chromium.chrome.browser.media.ui.MediaSessionTabHelper {
*** mTab;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.chromium.chrome.browser.media.ui;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;

import org.chromium.base.ContextUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.notifications.NotificationConstants;

public class BraveMediaNotificationControllerDelegate
extends ChromeMediaNotificationControllerDelegate {
BraveMediaNotificationControllerDelegate(int id) {
super(id);
ChromeMediaNotificationControllerDelegate.sMapNotificationIdToOptions.put(
PlaybackListenerMicServiceImpl.NOTIFICATION_ID,
new NotificationOptions(
BraveMediaNotificationControllerServices.PlaybackListenerMicService.class,
NotificationConstants.GROUP_MEDIA_PLAYBACK));
}

private static Context getContext() {
assert false;
return null;
}

/** Service used to run Brave Talk session */
public static final class PlaybackListenerMicServiceImpl extends ListenerServiceImpl {
static final int NOTIFICATION_ID = R.id.media_playback_mic_notification;

public PlaybackListenerMicServiceImpl() {
super(NOTIFICATION_ID);
}

@Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
ContextUtils.registerProtectedBroadcastReceiver(
getService(), mAudioBecomingNoisyReceiver, filter);
}

@Override
public void onDestroy() {
getService().unregisterReceiver(mAudioBecomingNoisyReceiver);
super.onDestroy();
}

private BroadcastReceiver mAudioBecomingNoisyReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
return;
}

Intent i =
new Intent(
getContext(),
BraveMediaNotificationControllerServices
.PlaybackListenerMicService.class);
i.setAction(intent.getAction());
try {
getContext().startService(i);
} catch (RuntimeException e) {
}
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.chromium.chrome.browser.media.ui;

import org.chromium.build.annotations.IdentifierNameString;
import org.chromium.chrome.browser.base.SplitCompatService;

public class BraveMediaNotificationControllerServices {
public static class PlaybackListenerMicService extends SplitCompatService {
private static @IdentifierNameString String sImplClassName =
"org.chromium.chrome.browser.media.ui."
+ "BraveMediaNotificationControllerDelegate$PlaybackListenerMicServiceImpl";

public PlaybackListenerMicService() {
super(sImplClassName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.chromium.chrome.browser.media.ui;

import org.chromium.chrome.R;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.browser_ui.media.BraveMediaSessionHelper;
import org.chromium.components.browser_ui.media.MediaNotificationInfo;
import org.chromium.components.browser_ui.media.MediaNotificationManager;

public class BraveMediaSessionTabHelper extends MediaSessionTabHelper {
/** Will be deleted in bytecode, value from the parent class will be used instead. */
private Tab mTab;

BraveMediaSessionTabHelper(Tab tab) {
super(tab);
}

@Override
public MediaNotificationInfo.Builder createMediaNotificationInfoBuilder() {
if (!BraveMediaSessionHelper.isBraveTalk(mTab.getWebContents())) {
return super.createMediaNotificationInfoBuilder();
}

return new MediaNotificationInfo.Builder()
.setInstanceId(mTab.getId())
.setId(R.id.media_playback_mic_notification);
}

@Override
public void hideMediaNotification() {
if (!BraveMediaSessionHelper.isBraveTalk(mTab.getWebContents())) {
super.hideMediaNotification();
return;
}
MediaNotificationManager.hide(mTab.getId(), R.id.media_playback_mic_notification);
}

@Override
public void activateAndroidMediaSession() {
if (!BraveMediaSessionHelper.isBraveTalk(mTab.getWebContents())) {
super.activateAndroidMediaSession();
return;
}
MediaNotificationManager.activateAndroidMediaSession(
mTab.getId(), R.id.media_playback_mic_notification);
}
}
3 changes: 3 additions & 0 deletions android/java/res/values/brave_ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@
<item type="id" name="menu_item_original_background" />
<item type="id" name="menu_item_background_drawable_position" />
<item type="id" name="contextmenu_copy_clean_link" />

<!-- Brave Talk notification -->
<item type="id" name="media_playback_mic_notification" />
</resources>
36 changes: 36 additions & 0 deletions android/javatests/org/chromium/chrome/browser/BytecodeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,17 @@ public void testClassesExist() throws Exception {
"org/chromium/chrome/browser/notifications/NotificationPlatformBridge"));

Assert.assertTrue(classExists("org/chromium/chrome/browser/settings/SettingsIntentUtil"));
Assert.assertTrue(classExists("org/chromium/content_public/browser/MediaSessionObserver"));
Assert.assertTrue(
classExists("org/chromium/components/browser_ui/media/MediaSessionHelper"));
Assert.assertTrue(
classExists(
"org/chromium/components/browser_ui/notifications/ForegroundServiceUtils"));
Assert.assertTrue(
classExists(
"org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate")); // presubmit: ignore-long-line
Assert.assertTrue(
classExists("org/chromium/chrome/browser/media/ui/MediaSessionTabHelper"));
}

@Test
Expand Down Expand Up @@ -895,6 +904,13 @@ public void testMethodsExist() throws Exception {
true,
MediaSessionObserver.class,
MediaSession.class));
Assert.assertTrue(
methodExists(
"org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate", // presubmit: ignore-long-line
"getContext",
MethodModifier.STATIC,
true,
Context.class));
}

@Test
Expand Down Expand Up @@ -1779,6 +1795,24 @@ public void testConstructorsExistAndMatch() throws Exception {
DoubleConsumer.class,
UserEducationHelper.class,
ObservableSupplier.class));

Assert.assertTrue(
constructorsMatch(
"org/chromium/components/browser_ui/notifications/ForegroundServiceUtils", // presubmit: ignore-long-line
"org/chromium/components/browser_ui/notifications/BraveForegroundServiceUtils")); // presubmit: ignore-long-line

Assert.assertTrue(
constructorsMatch(
"org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate", // presubmit: ignore-long-line
"org/chromium/chrome/browser/media/ui/BraveMediaNotificationControllerDelegate", // presubmit: ignore-long-line
int.class));

Assert.assertTrue(
constructorsMatch(
"org/chromium/chrome/browser/media/ui/MediaSessionTabHelper", // presubmit:
// ignore-long-line
"org/chromium/chrome/browser/media/ui/BraveMediaSessionTabHelper", // presubmit: ignore-long-line
Tab.class));
}

@Test
Expand Down Expand Up @@ -2150,6 +2184,8 @@ public void testFieldsExist() throws Exception {
fieldExists(
"org/chromium/components/browser_ui/media/MediaSessionHelper",
"mMediaSessionActions"));
Assert.assertTrue(
fieldExists("org/chromium/chrome/browser/media/ui/MediaSessionTabHelper", "mTab"));
}

@Test
Expand Down
3 changes: 3 additions & 0 deletions build/android/bytecode/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ java_binary("java_bytecode_rewriter") {
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveExternalNavigationHandlerClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveFeedSurfaceCoordinatorClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveFeedSurfaceMediatorClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveForegroundServiceUtilsClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveFreIntentCreatorClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveHelpAndFeedbackLauncherImplClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveHomepageManagerClassAdapter.java",
Expand All @@ -66,7 +67,9 @@ java_binary("java_bytecode_rewriter") {
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMainPreferenceBaseClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveManageAccountDevicesLinkViewClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveManageSyncSettingsClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMediaNotificationControllerDelegateAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMediaSessionHelperClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMediaSessionTabHelperClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMenuButtonCoordinatorClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMimeUtilsClassAdapter.java",
"//brave/build/android/bytecode/java/org/brave/bytecode/BraveMostVisitedTilesLayoutBaseClassAdapter.java",
Expand Down
1 change: 1 addition & 0 deletions build/android/bytecode/bytecode_rewriter.gni
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ brave_bytecode_jars = [
"obj/brave/browser/ui/android/logo/java.javac.jar",
"obj/brave/browser/ui/android/omnibox/java.javac.jar",
"obj/brave/browser/ui/android/theme/java.javac.jar",
"obj/brave/components/browser_ui/notifications/android/java.javac.jar",
"obj/brave/components/browser_ui/media/android/java.javac.jar",
"obj/brave/components/browser_ui/site_settings/android/java.javac.jar",
"obj/brave/components/variations/android/java.javac.jar",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static ClassVisitor createAdapter(ClassVisitor chain) {
chain = new BraveExternalNavigationHandlerClassAdapter(chain);
chain = new BraveFeedSurfaceCoordinatorClassAdapter(chain);
chain = new BraveFeedSurfaceMediatorClassAdapter(chain);
chain = new BraveForegroundServiceUtilsClassAdapter(chain);
chain = new BraveFreIntentCreatorClassAdapter(chain);
chain = new BraveHelpAndFeedbackLauncherImplClassAdapter(chain);
chain = new BraveHomepageManagerClassAdapter(chain);
Expand All @@ -65,7 +66,9 @@ public static ClassVisitor createAdapter(ClassVisitor chain) {
chain = new BraveMainPreferenceBaseClassAdapter(chain);
chain = new BraveManageAccountDevicesLinkViewClassAdapter(chain);
chain = new BraveManageSyncSettingsClassAdapter(chain);
chain = new BraveMediaNotificationControllerDelegateAdapter(chain);
chain = new BraveMediaSessionHelperClassAdapter(chain);
chain = new BraveMediaSessionTabHelperClassAdapter(chain);
chain = new BraveMenuButtonCoordinatorClassAdapter(chain);
chain = new BraveMimeUtilsClassAdapter(chain);
chain = new BraveMostVisitedTilesLayoutBaseClassAdapter(chain);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.brave.bytecode;

import org.objectweb.asm.ClassVisitor;

public class BraveForegroundServiceUtilsClassAdapter extends BraveClassVisitor {
static String sForegroundServiceUtilsClassName =
"org/chromium/components/browser_ui/notifications/ForegroundServiceUtils";

static String sBraveForegroundServiceUtilsClassName =
"org/chromium/components/browser_ui/notifications/BraveForegroundServiceUtils";

public BraveForegroundServiceUtilsClassAdapter(ClassVisitor visitor) {
super(visitor);

redirectConstructor(
sForegroundServiceUtilsClassName, sBraveForegroundServiceUtilsClassName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.brave.bytecode;

import org.objectweb.asm.ClassVisitor;

public class BraveMediaNotificationControllerDelegateAdapter extends BraveClassVisitor {
static String sChromeMediaNotificationControllerDelegate =
"org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerDelegate";
static String sBraveMediaNotificationControllerDelegate =
"org/chromium/chrome/browser/media/ui/BraveMediaNotificationControllerDelegate";

public BraveMediaNotificationControllerDelegateAdapter(ClassVisitor visitor) {
super(visitor);

redirectConstructor(
sChromeMediaNotificationControllerDelegate,
sBraveMediaNotificationControllerDelegate);
deleteMethod(sBraveMediaNotificationControllerDelegate, "getContext");
makePublicMethod(sChromeMediaNotificationControllerDelegate, "getContext");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.brave.bytecode;

import org.objectweb.asm.ClassVisitor;

public class BraveMediaSessionTabHelperClassAdapter extends BraveClassVisitor {
static String sMediaSessionTabHelper =
"org/chromium/chrome/browser/media/ui/MediaSessionTabHelper";
static String sBraveMediaSessionTabHelper =
"org/chromium/chrome/browser/media/ui/BraveMediaSessionTabHelper";

public BraveMediaSessionTabHelperClassAdapter(ClassVisitor visitor) {
super(visitor);

redirectConstructor(sMediaSessionTabHelper, sBraveMediaSessionTabHelper);
deleteField(sBraveMediaSessionTabHelper, "mTab");
makeProtectedField(sMediaSessionTabHelper, "mTab");
}
}
Loading

0 comments on commit 42c5569

Please sign in to comment.