-
Notifications
You must be signed in to change notification settings - Fork 781
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Xamarin samples for Android and iOS (#3584)
- Loading branch information
1 parent
0e89bbe
commit 2c56bdc
Showing
71 changed files
with
32,256 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.dll | ||
.java | ||
.class | ||
.jar | ||
.so | ||
/*/obj/ |
20 changes: 20 additions & 0 deletions
20
pjsip-apps/src/swig/csharp/pjsua2xamarin/pjsua2xamarin.Android/Assets/AboutAssets.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Any raw assets you want to be deployed with your application can be placed in | ||
this directory (and child directories) and given a Build Action of "AndroidAsset". | ||
|
||
These files will be deployed with your package and will be accessible using Android's | ||
AssetManager, like this: | ||
|
||
public class ReadAsset : Activity | ||
{ | ||
protected override void OnCreate (Bundle bundle) | ||
{ | ||
base.OnCreate (bundle); | ||
|
||
InputStream input = Assets.Open ("my_asset.txt"); | ||
} | ||
} | ||
|
||
Additionally, some Android functions will automatically load asset files: | ||
|
||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); | ||
|
245 changes: 245 additions & 0 deletions
245
pjsip-apps/src/swig/csharp/pjsua2xamarin/pjsua2xamarin.Android/CallPageRenderer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
using System; | ||
using System.IO; | ||
using Xamarin.Forms; | ||
using Xamarin.Forms.Platform.Android; | ||
using pjsua2xamarin; | ||
using pjsua2xamarin.Droid; | ||
using Android.App; | ||
using Android.Content; | ||
using Android.Hardware; | ||
using Android.Views; | ||
using Android.Graphics; | ||
using Android.Widget; | ||
using Android.Renderscripts; | ||
using static Xamarin.Essentials.Permissions; | ||
using pjsua2xamarin.pjsua2; | ||
using AndroidX.Lifecycle; | ||
using System.Runtime.InteropServices; | ||
using Android.Runtime; | ||
|
||
[assembly: ExportRenderer(typeof(CallPage), typeof(CallPageRenderer))] | ||
namespace pjsua2xamarin.Droid | ||
{ | ||
public class CallPageRenderer : PageRenderer, ISurfaceHolderCallback | ||
{ | ||
global::Android.Widget.Button acceptCallButton; | ||
global::Android.Widget.Button hangupCallButton; | ||
global::Android.Widget.TextView peerTxt; | ||
global::Android.Widget.TextView statusTxt; | ||
global::Android.Views.View view; | ||
private static CallInfo lastCallInfo; | ||
private CallPage callPage; | ||
SurfaceView incomingView; | ||
|
||
[DllImport("android")] | ||
private static extern IntPtr ANativeWindow_fromSurface(IntPtr jni, IntPtr surface); | ||
|
||
public CallPageRenderer(Context context) : base(context) | ||
{ | ||
MessagingCenter.Subscribe<BuddyPage, CallInfo> | ||
(this, "UpdateCallState", (obj, info) => { | ||
if (callPage == null) | ||
return; | ||
lastCallInfo = info as CallInfo; | ||
if (lastCallInfo.state == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) { | ||
Device.BeginInvokeOnMainThread(() => { | ||
callPage.Navigation.PopAsync(); | ||
}); | ||
} else { | ||
Device.BeginInvokeOnMainThread(() => { | ||
updateCallState(lastCallInfo); | ||
}); | ||
} | ||
}); | ||
MessagingCenter.Subscribe<BuddyPage, CallInfo> | ||
(this, "UpdateMediaCallState", (obj, info) => { | ||
lastCallInfo = info as CallInfo; | ||
if (MyApp.currentCall.vidWin != null) { | ||
incomingView.Visibility = ViewStates.Visible; | ||
} | ||
}); | ||
} | ||
|
||
~CallPageRenderer() { | ||
MessagingCenter.Unsubscribe<BuddyPage, CallInfo>(this, "UpdateCallState"); | ||
MessagingCenter.Unsubscribe<BuddyPage, CallInfo>(this, "UpdateMediaCallState"); | ||
} | ||
|
||
protected override void OnLayout(bool changed, int l, int t, int r, int b) | ||
{ | ||
base.OnLayout(changed, l, t, r, b); | ||
|
||
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly); | ||
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly); | ||
|
||
view.Measure(msw, msh); | ||
view.Layout(0, 0, r - l, b - t); | ||
} | ||
|
||
protected override void OnElementChanged(ElementChangedEventArgs<Page> e) | ||
{ | ||
base.OnElementChanged(e); | ||
|
||
if (e.OldElement != null || Element == null) { | ||
return; | ||
} | ||
|
||
try { | ||
SetupUserInterface(); | ||
SetupEventHandlers(); | ||
AddView(view); | ||
callPage = (CallPage)Element; | ||
System.Diagnostics.Debug.WriteLine(@"Call page done initialize"); | ||
if (MyApp.currentCall != null) { | ||
try { | ||
lastCallInfo = MyApp.currentCall.getInfo(); | ||
} catch (Exception ex) { | ||
System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); | ||
} | ||
Device.BeginInvokeOnMainThread(() => { | ||
updateCallState(lastCallInfo); | ||
}); | ||
} | ||
} catch (Exception ex) { | ||
System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); | ||
} | ||
} | ||
|
||
void SetupUserInterface() | ||
{ | ||
var activity = this.Context as Activity; | ||
view = activity.LayoutInflater.Inflate(Resource.Layout.activity_call, this, false); | ||
|
||
incomingView = view.FindViewById<SurfaceView>(Resource.Id.incomingVideoView); | ||
incomingView.Holder.AddCallback(this); | ||
|
||
peerTxt = view.FindViewById<TextView>(Resource.Id.peerTxt); | ||
statusTxt = view.FindViewById<TextView>(Resource.Id.statusTxt); | ||
|
||
if (MyApp.currentCall == null || MyApp.currentCall.vidWin == null) { | ||
incomingView.Visibility = ViewStates.Gone; | ||
} | ||
} | ||
|
||
void SetupEventHandlers() | ||
{ | ||
acceptCallButton = view.FindViewById<global::Android.Widget.Button>(Resource.Id.acceptCallButton); | ||
acceptCallButton.Click += AcceptCallButtonTapped; | ||
|
||
hangupCallButton = view.FindViewById<global::Android.Widget.Button>(Resource.Id.hangupCallButton); | ||
hangupCallButton.Click += HangupCallButtonTapped; | ||
} | ||
|
||
void AcceptCallButtonTapped(object sender, EventArgs e) | ||
{ | ||
CallOpParam prm = new CallOpParam(); | ||
prm.statusCode = pjsip_status_code.PJSIP_SC_OK; | ||
try { | ||
MyApp.currentCall.answer(prm); | ||
} catch (Exception ex) { | ||
System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); | ||
} | ||
|
||
acceptCallButton.Visibility = ViewStates.Gone; | ||
} | ||
|
||
void HangupCallButtonTapped(object sender, EventArgs e) | ||
{ | ||
if (MyApp.currentCall != null) { | ||
CallOpParam prm = new CallOpParam(); | ||
prm.statusCode = pjsip_status_code.PJSIP_SC_DECLINE; | ||
try { | ||
MyApp.currentCall.hangup(prm); | ||
} catch (Exception ex) { | ||
System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); | ||
} | ||
} | ||
} | ||
|
||
private void updateVideoWindow(bool show) | ||
{ | ||
if (MyApp.currentCall != null && | ||
MyApp.currentCall.vidWin != null && | ||
MyApp.currentCall.vidPrev != null) | ||
{ | ||
long windHandle = 0; | ||
VideoWindowHandle vidWH = new VideoWindowHandle(); | ||
if (show) { | ||
IntPtr winPtr = ANativeWindow_fromSurface(JNIEnv.Handle, | ||
incomingView.Holder.Surface.Handle); | ||
windHandle = winPtr.ToInt64(); | ||
} | ||
vidWH.handle.setWindow(windHandle); | ||
try { | ||
MyApp.currentCall.vidWin.setWindow(vidWH); | ||
} catch (Exception ex) { | ||
System.Diagnostics.Debug.WriteLine(@"ERROR: ", ex.Message); | ||
} | ||
} | ||
} | ||
|
||
private void updateCallState(CallInfo ci) | ||
{ | ||
String call_state = ""; | ||
|
||
if (ci == null) { | ||
acceptCallButton.Visibility = ViewStates.Gone; | ||
hangupCallButton.Text = "OK"; | ||
statusTxt.Text = "Call disconnected"; | ||
return; | ||
} | ||
|
||
if (ci.role == pjsip_role_e.PJSIP_ROLE_UAC) { | ||
acceptCallButton.Visibility = ViewStates.Gone; | ||
} | ||
|
||
if (ci.state < | ||
pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) | ||
{ | ||
if (ci.role == pjsip_role_e.PJSIP_ROLE_UAS) { | ||
call_state = "Incoming call.."; | ||
/* Default button texts are already 'Accept' & 'Reject' */ | ||
} else { | ||
hangupCallButton.Text = "Cancel"; | ||
call_state = ci.stateText; | ||
} | ||
} else if (ci.state >= | ||
pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) | ||
{ | ||
acceptCallButton.Visibility = ViewStates.Gone; | ||
call_state = ci.stateText; | ||
if (ci.state == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) { | ||
hangupCallButton.Text = "Hangup"; | ||
} else if (ci.state == | ||
pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) | ||
{ | ||
hangupCallButton.Text = "OK"; | ||
call_state = "Call disconnected: " + ci.lastReason; | ||
} | ||
if (ci.state == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) { | ||
updateVideoWindow(true); | ||
} | ||
} | ||
|
||
peerTxt.Text = ci.remoteUri; | ||
statusTxt.Text = call_state; | ||
} | ||
|
||
void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) | ||
{ | ||
updateVideoWindow(true); | ||
} | ||
|
||
void ISurfaceHolderCallback.SurfaceCreated(ISurfaceHolder holder) | ||
{ | ||
} | ||
|
||
void ISurfaceHolderCallback.SurfaceDestroyed(ISurfaceHolder holder) | ||
{ | ||
updateVideoWindow(false); | ||
} | ||
} | ||
} | ||
|
48 changes: 48 additions & 0 deletions
48
pjsip-apps/src/swig/csharp/pjsua2xamarin/pjsua2xamarin.Android/MainActivity.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using System; | ||
|
||
using Android.App; | ||
using Android.Content.PM; | ||
using Android.Runtime; | ||
using Android.OS; | ||
using Java.Lang; | ||
using Android.Hardware.Camera2; | ||
using Android.Content; | ||
|
||
namespace pjsua2xamarin.Droid | ||
{ | ||
[Activity(Label = "pjsua2xamarin", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )] | ||
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity | ||
{ | ||
protected override void OnCreate(Bundle savedInstanceState) | ||
{ | ||
base.OnCreate(savedInstanceState); | ||
|
||
Xamarin.Essentials.Platform.Init(this, savedInstanceState); | ||
global::Xamarin.Forms.Forms.Init(this, savedInstanceState); | ||
|
||
IntPtr class_ref = JNIEnv.FindClass("org/pjsip/PjCameraInfo2"); | ||
if (class_ref != null) { | ||
IntPtr method_id = JNIEnv.GetStaticMethodID(class_ref, | ||
"SetCameraManager", "(Landroid/hardware/camera2/CameraManager;)V"); | ||
|
||
if (method_id != null) { | ||
CameraManager manager = GetSystemService(Context.CameraService) as CameraManager; | ||
|
||
JNIEnv.CallStaticVoidMethod(class_ref, method_id, new JValue(manager)); | ||
|
||
Console.WriteLine("SUCCESS setting cameraManager"); | ||
} | ||
} | ||
JavaSystem.LoadLibrary("c++_shared"); | ||
JavaSystem.LoadLibrary("pjsua2"); | ||
|
||
LoadApplication(new App()); | ||
} | ||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) | ||
{ | ||
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); | ||
|
||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults); | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...p-apps/src/swig/csharp/pjsua2xamarin/pjsua2xamarin.Android/Properties/AndroidManifest.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.teluu.pjsua2xamarin"> | ||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" /> | ||
<application android:label="pjsua2xamarin.Android" android:theme="@style/MainTheme"></application> | ||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||
<uses-permission android:name="android.permission.CAMERA" /> | ||
<uses-permission android:name="android.permission.CALL_PHONE" /> | ||
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" /> | ||
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> | ||
<uses-permission android:name="android.permission.INTERNET" /> | ||
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" /> | ||
<uses-permission android:name="android.permission.RECORD_AUDIO" /> | ||
</manifest> |
Oops, something went wrong.