Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save yrezgui/2a710cbd32a29133cb31f575fbbaee2d to your computer and use it in GitHub Desktop.
Save yrezgui/2a710cbd32a29133cb31f575fbbaee2d to your computer and use it in GitHub Desktop.
Add rickroll Dialer option. Add rick.webm in assets
From f07ca2a26dcb0cc797dcc6fc0d2d4f16b89c481e Mon Sep 17 00:00:00 2001
From: Pierre-Hugues Husson <phh@phh.me>
Date: Mon, 14 Mar 2022 09:09:28 -0400
Subject: [PATCH] Add a rickroll button in heads-up notification to rickroll
caller
Change-Id: Ibe72535fb3e93f69a531723dc96ede05663ee251
---
assets/rick.webm | Bin 0 -> 1232413 bytes
.../NotificationBroadcastReceiver.java | 145 ++++++++++++++++++
.../android/incallui/StatusBarNotifier.java | 32 ++++
.../com/android/incallui/call/DialerCall.java | 7 +
4 files changed, 184 insertions(+)
create mode 100644 assets/rick.webm
diff --git a/java/com/android/incallui/NotificationBroadcastReceiver.java b/java/com/android/incallui/NotificationBroadcastReceiver.java
index 241d8ed48..6573e32ec 100644
--- a/java/com/android/incallui/NotificationBroadcastReceiver.java
+++ b/java/com/android/incallui/NotificationBroadcastReceiver.java
@@ -36,6 +36,24 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.android.incallui.call.state.DialerCallState;
+
+import android.util.Log;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaCodec;
+
+import android.content.res.AssetFileDescriptor;
+
+import java.nio.ByteBuffer;
+
+import java.lang.Thread;
+
/**
* Accepts broadcast Intents which will be prepared by {@link StatusBarNotifier} and thus sent from
* the notification manager. This should be visible from outside, but shouldn't be exported.
@@ -50,6 +68,9 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver {
public static final String ACTION_DECLINE_INCOMING_CALL =
"com.android.incallui.ACTION_DECLINE_INCOMING_CALL";
+ public static final String ACTION_RICKROLL_INCOMING_CALL =
+ "com.android.incallui.ACTION_RICKROLL_INCOMING_CALL";
+
public static final String ACTION_HANG_UP_ONGOING_CALL =
"com.android.incallui.ACTION_HANG_UP_ONGOING_CALL";
public static final String ACTION_ANSWER_VIDEO_INCOMING_CALL =
@@ -90,6 +111,9 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver {
Logger.get(context)
.logImpression(DialerImpression.Type.REJECT_INCOMING_CALL_FROM_NOTIFICATION);
declineIncomingCall();
+ } else if (action.equals(ACTION_RICKROLL_INCOMING_CALL)) {
+ rickroll(context);
+
} else if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
hangUpOngoingCall();
} else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) {
@@ -220,4 +244,125 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver {
}
}
}
+ private void rickroll(Context context) {
+ CallList callList = InCallPresenter.getInstance().getCallList();
+ DialerCall call = callList.getIncomingCall();
+ call.enterBackgroundAudioProcessing();
+ (new Thread() {
+ @Override
+ public void run() {
+ Log.d("PHH-Call", "Starting rickroll");
+ AudioManager am = context.getSystemService(AudioManager.class);
+ AssetFileDescriptor pfd;
+ try {
+ pfd = context.getResources().getAssets().openFd("rick.webm");
+ } catch(Exception e) {
+ Log.e("PHH-Call", "Failed grabbing rick.webm");
+ return;
+ }
+ MediaExtractor extractor = new MediaExtractor();
+ try {
+ extractor.setDataSource(pfd);
+ } catch(Exception e) {
+ Log.e("PHH-Call", "Failed setting extractor datasource");
+ return;
+ }
+
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ extractor.selectTrack(0);
+
+ MediaCodec decoder;
+ try {
+ decoder = MediaCodec.createDecoderByType(mime);
+ } catch(Exception e) {
+ Log.e("PHH-Call", "Failed creating decoder");
+ return;
+ }
+ decoder.configure(format, null, null, 0);
+ decoder.start();
+
+ AudioDeviceInfo[] devices = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ AudioDeviceInfo telephonyOut = null;
+ for(AudioDeviceInfo dev : devices) {
+ if(dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) telephonyOut = dev;
+ }
+ if(telephonyOut == null) {
+ Log.e("PHH-Call", "No TELEPHONY_TX audio device");
+ decoder.stop();
+ decoder.release();
+ return;
+ }
+
+
+ AudioTrack playbackTrack = new AudioTrack.Builder()
+ .setAudioAttributes(new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ .build())
+ .setAudioFormat(new AudioFormat.Builder()
+ .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE))
+ .build())
+ .build();
+
+ playbackTrack.setPreferredDevice(telephonyOut);
+ playbackTrack.play();
+
+ boolean sawInputEOS = false, sawOutputEOS = false;
+ while(call.getState() == DialerCallState.ACTIVE || call.getState() == DialerCallState.INCOMING) {
+ int inputBufIndex = decoder.dequeueInputBuffer(50000);
+ if(inputBufIndex >= 0) {
+ ByteBuffer dstBuf = decoder.getInputBuffer(inputBufIndex);
+ int sampleSize = extractor.readSampleData(dstBuf, 0);
+ long presentationTime = 0L;
+ if(sampleSize < 0) {
+ sawInputEOS = true;
+ sampleSize = 0;
+ } else {
+ presentationTime = extractor.getSampleTime();
+ }
+ decoder.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTime,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ if(!sawInputEOS) {
+ extractor.advance();
+ }
+ }
+
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ int outputBufIndex = decoder.dequeueOutputBuffer(info, 50000);
+ if(outputBufIndex >= 0) {
+ ByteBuffer buf = decoder.getOutputBuffer(outputBufIndex);
+ byte[] chunk = new byte[info.size];
+ buf.get(chunk);
+ buf.clear();
+
+ if(chunk.length > 0) {
+ playbackTrack.write(chunk, 0, chunk.length);
+ }
+ decoder.releaseOutputBuffer(outputBufIndex, false);
+ if( (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
+ sawOutputEOS = true;
+
+ } else if(outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat oformat = decoder.getOutputFormat();
+ playbackTrack.setPlaybackRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
+ }
+
+ if(sawOutputEOS) break;
+ }
+ Log.d("PHH-Call", "Stopped rickroll because of call state " + call.getState());
+ playbackTrack.stop();
+ playbackTrack.release();
+ decoder.stop();
+ decoder.release();
+ try {
+ //TODO: Do it from main thread
+ call.disconnect();
+ } catch(Exception e) {
+ Log.d("PHH-Call", "Tried disconnecting call", e);
+ }
+ }
+ }).start();
+ }
}
diff --git a/java/com/android/incallui/StatusBarNotifier.java b/java/com/android/incallui/StatusBarNotifier.java
index 99ff7255e..e5076f08c 100644
--- a/java/com/android/incallui/StatusBarNotifier.java
+++ b/java/com/android/incallui/StatusBarNotifier.java
@@ -27,6 +27,7 @@ import static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_
import static com.android.incallui.NotificationBroadcastReceiver.ACTION_HANG_UP_ONGOING_CALL;
import static com.android.incallui.NotificationBroadcastReceiver.ACTION_TURN_OFF_SPEAKER;
import static com.android.incallui.NotificationBroadcastReceiver.ACTION_TURN_ON_SPEAKER;
+import static com.android.incallui.NotificationBroadcastReceiver.ACTION_RICKROLL_INCOMING_CALL;
import android.Manifest;
import android.app.Notification;
@@ -35,6 +36,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -264,6 +266,7 @@ public class StatusBarNotifier
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
private void buildAndSendNotification(
CallList callList, DialerCall originalCall, ContactCacheEntry contactInfo) {
+ android.util.Log.d("PHH-Call", "buildAndSendNotification");
Trace.beginSection("StatusBarNotifier.buildAndSendNotification");
// This can get called to update an existing notification after contact information has come
// back. However, it can happen much later. Before we continue, we need to make sure that
@@ -290,6 +293,8 @@ public class StatusBarNotifier
call.getVideoTech().getSessionModificationState()
== SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
final int notificationType;
+
+ android.util.Log.d("PHH-Call", "Call state is " + callState);
if (callState == DialerCallState.INCOMING
|| callState == DialerCallState.CALL_WAITING
|| isVideoUpgradeRequest) {
@@ -439,6 +444,7 @@ public class StatusBarNotifier
private void createIncomingCallNotification(
DialerCall call, int state, CallAudioState callAudioState, Notification.Builder builder) {
setNotificationWhen(call, state, builder);
+ android.util.Log.d("PHH-Call", "Create incoming call notification");
// Add hang up option for any active calls (active | onhold), outgoing calls (dialing).
if (state == DialerCallState.ACTIVE
@@ -452,6 +458,7 @@ public class StatusBarNotifier
addVideoCallAction(builder);
} else {
addAnswerAction(builder);
+ addRickrollAction(builder);
addSpeakeasyAnswerAction(builder, call);
}
}
@@ -857,6 +864,15 @@ public class StatusBarNotifier
return spannable;
}
+ private Spannable getActionText(String string, int color) {
+ Spannable spannable = new SpannableString(string);
+ if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+ spannable.setSpan(
+ new ForegroundColorSpan(color), 0, spannable.length(), 0);
+ }
+ return spannable;
+ }
+
private void addAnswerAction(Notification.Builder builder) {
LogUtil.d(
"StatusBarNotifier.addAnswerAction",
@@ -929,6 +945,22 @@ public class StatusBarNotifier
.build());
}
+ private void addRickrollAction(Notification.Builder builder) {
+ LogUtil.d(
+ "StatusBarNotifier.addRickrollAction",
+ "will show \"rickroll\" action in the incoming call Notification");
+ android.util.Log.d("PHH-Call", "Adding rickroll button");
+ PendingIntent rickrollPendingIntent =
+ createNotificationPendingIntent(context, ACTION_RICKROLL_INCOMING_CALL);
+ builder.addAction(
+ new Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.quantum_ic_close_white_24),
+ getActionText(
+ "Rickroll", Color.YELLOW),
+ rickrollPendingIntent)
+ .build());
+ }
+
private void addHangupAction(Notification.Builder builder) {
LogUtil.d(
"StatusBarNotifier.addHangupAction",
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index 76d3e8b53..1b924e76a 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -439,6 +439,7 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
case Call.STATE_RINGING:
return DialerCallState.INCOMING;
case Call.STATE_ACTIVE:
+ case Call.STATE_AUDIO_PROCESSING:
return DialerCallState.ACTIVE;
case Call.STATE_HOLDING:
return DialerCallState.ONHOLD;
@@ -572,6 +573,7 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
videoTech = null;
// We want to potentially register a video call callback here.
updateFromTelecomCall();
+ android.util.Log.d("PHH-Call", "DialerCall new state is " + getState());
if (oldState != getState() && getState() == DialerCallState.DISCONNECTED) {
for (DialerCallListener listener : listeners) {
listener.onDialerCallDisconnect();
@@ -595,6 +597,7 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
Trace.beginSection("DialerCall.updateFromTelecomCall");
LogUtil.v("DialerCall.updateFromTelecomCall", telecomCall.toString());
+ android.util.Log.d("PHH-Call", "telecom call new state is " + telecomCall.getState());
videoTechManager.dispatchCallStateChanged(telecomCall.getState(), getAccountHandle());
final int translatedState = translateState(telecomCall.getState());
@@ -1991,4 +1994,8 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
public int getPeerDimensionHeight() {
return peerDimensionHeight;
}
+
+ public void enterBackgroundAudioProcessing() {
+ telecomCall.enterBackgroundAudioProcessing();
+ }
}
--
2.35.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment