Unity Android Screen Capture Plugin - c#

I'm in the process of developing a custom Unity Plugin (Android) that allows the Unity application to record the screen. I've looked at several existing Unity plugins and none seem to either work or do exactly what I'm needing. I was able to get a VERY simple Android plugin (.jar) built and loaded into Unity using this nice tutorial.
To be clear... this is my very first attempt at creating a Unity plugin.
My problem is that when I extend the Activity my plugin fails to function. When debugging, the log cat doesn't enter the onCreate() method and the Log.d() message isn't thrown. Any help on identifying if the below source code is valid would be great. This code is nothing like the tutorial I watched. Much more involved.
BTW, I first built the below code in Android Studio and ran on a device to make sure the code actually performed as expected. It did. I then took the code from that initial test and started developing the Unity plugin below based on the video tutorial and other resources.
Here's my Android Java code that builds the classes.jar file with no build errors.
package lib.rrpartners.com.unityscreencaptureplugin;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.Surface;
import java.io.IOException;
public class ScreenCapturePlugin extends Activity {
private static final String TAG = "ScreenCapturePlugin";
private static final int REQUEST_CODE = 1000;
private int mScreenDensity;
private MediaProjectionManager mProjectionManager;
private static final int DISPLAY_WIDTH = 1440;
private static final int DISPLAY_HEIGHT = 2560;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaProjectionCallback mMediaProjectionCallback;
private ToggleButton mToggleButton;
private MediaRecorder mMediaRecorder;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private static final int REQUEST_PERMISSIONS = 10;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
public String getText() {
return "Some text here";
}
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d("OverrideActivity", "onCreate called!");
super.onCreate(savedInstanceState);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mScreenDensity = metrics.densityDpi;
mMediaRecorder = new MediaRecorder();
mProjectionManager = (MediaProjectionManager) getSystemService
(Context.MEDIA_PROJECTION_SERVICE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE) {
Log.e(TAG, "Unknown request code: " + requestCode);
return;
}
if (resultCode != RESULT_OK) {
mToggleButton.setChecked(false);
return;
}
mMediaProjectionCallback = new MediaProjectionCallback();
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
mMediaProjection.registerCallback(mMediaProjectionCallback, null);
mVirtualDisplay = createVirtualDisplay();
mMediaRecorder.start();
}
public void startScreenRecording() {
initRecorder();
startScreenRecording();
if (mMediaProjection == null) {
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
return;
}
mVirtualDisplay = createVirtualDisplay();
mMediaRecorder.start();
}
private VirtualDisplay createVirtualDisplay() {
return mMediaProjection.createVirtualDisplay("MainActivity",
DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder.getSurface(), null /*Callbacks*/, null
/*Handler*/);
}
private void initRecorder() {
try {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setOutputFile(Environment
.getExternalStoragePublicDirectory(Environment
.DIRECTORY_DOWNLOADS) + "/video.mp4");
mMediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncodingBitRate(3000000);
mMediaRecorder.setVideoFrameRate(30);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int orientation = ORIENTATIONS.get(rotation + 90);
mMediaRecorder.setOrientationHint(orientation);
mMediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
private class MediaProjectionCallback extends MediaProjection.Callback {
#Override
public void onStop() {
if (mToggleButton.isChecked()) {
mToggleButton.setChecked(false);
mMediaRecorder.stop();
mMediaRecorder.reset();
Log.v(TAG, "Recording Stopped");
}
mMediaProjection = null;
stopScreenSharing();
}
}
private void stopScreenSharing() {
mMediaRecorder.stop();
mMediaRecorder.reset();
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
//mMediaRecorder.release(); //If used: mMediaRecorder object cannot
// be reused again
destroyMediaProjection();
}
#Override
public void onDestroy() {
super.onDestroy();
destroyMediaProjection();
}
private void destroyMediaProjection() {
if (mMediaProjection != null) {
mMediaProjection.unregisterCallback(mMediaProjectionCallback);
mMediaProjection.stop();
mMediaProjection = null;
}
Log.i(TAG, "MediaProjection Stopped");
}
}
Here's my Android Manifest file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="lib.rrpartners.com.unityscreencaptureplugin">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application android:allowBackup="true" android:label="#string/app_name"
android:supportsRtl="true">
</application>
</manifest>
Unity C# Code
frameCount = GameObject.Find("FrameCount").GetComponent<Text>();
using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
obj_Activity.CallStatic("onCreate");
}
}

Related

Is there a way to be notified of whenever the volume setting is changed in Xamarin C#? [duplicate]

I'm programming a small widget that needs to be updated whenever the user changes the ringer volume or the vibrate settings.
Capturing android.media.VIBRATE_SETTING_CHANGED works just fine for the vibrate settings, but I haven't found any way of getting notified when the ringer volume changes and although I could try to capture when the user presses the volume up/volume down physical keys, there are many other options for changing the volume without using these keys.
Do you know if there's any broadcast action defined for this or any way to create one or to solve the problem without it?
There is no broadcast action, but I did find you can hook up a content observer to get notified when the settings change, volume of streams being some of those settings. Register for the android.provider.Settings.System.CONTENT_URI to be notified of all settings changes:
mSettingsContentObserver = new SettingsContentObserver( new Handler() );
this.getApplicationContext().getContentResolver().registerContentObserver(
android.provider.Settings.System.CONTENT_URI, true,
mSettingsContentObserver );
The content observer might look something like this:
public class SettingsContentObserver extends ContentObserver {
public SettingsContentObserver(Handler handler) {
super(handler);
}
#Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.v(LOG_TAG, "Settings change detected");
updateStuff();
}
}
And be sure to unregister the content observer at some point.
Nathan's code works but gives two notifications for each change system settings. To avoid that, use the following
public class SettingsContentObserver extends ContentObserver {
int previousVolume;
Context context;
public SettingsContentObserver(Context c, Handler handler) {
super(handler);
context=c;
AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
}
#Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
int delta=previousVolume-currentVolume;
if(delta>0)
{
Logger.d("Decreased");
previousVolume=currentVolume;
}
else if(delta<0)
{
Logger.d("Increased");
previousVolume=currentVolume;
}
}
}
Then in your service onCreate register it with:
mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );
Then unregister in onDestroy:
getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
Yes, you can register a receiver for a volume change(this is kind of a hack, but works), I managed to do it this way (does not involve a ContentObserver):
In manifest xml file:
<receiver android:name="com.example.myproject.receivers.MyReceiver" >
<intent-filter>
<action android:name="android.media.VOLUME_CHANGED_ACTION" />
</intent-filter>
</receiver>
BroadcastReceiver:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION")) {
Log.d("Music Stream", "has changed");
}
}
}
hope it helps!
Based into Nathan's, adi's and swooby's code I created a full working example with some minor improvements.
Looking to the AudioFragment class we can see how easy is to listen for volume changes with our custom ContentObserver.
public class AudioFragment extends Fragment implements OnAudioVolumeChangedListener {
private AudioVolumeObserver mAudioVolumeObserver;
#Override
public void onResume() {
super.onResume();
// initialize audio observer
if (mAudioVolumeObserver == null) {
mAudioVolumeObserver = new AudioVolumeObserver(getActivity());
}
/*
* register audio observer to identify the volume changes
* of audio streams for music playback.
*
* It is also possible to listen for changes in other audio stream types:
* STREAM_RING: phone ring, STREAM_ALARM: alarms, STREAM_SYSTEM: system sounds, etc.
*/
mAudioVolumeObserver.register(AudioManager.STREAM_MUSIC, this);
}
#Override
public void onPause() {
super.onPause();
// release audio observer
if (mAudioVolumeObserver != null) {
mAudioVolumeObserver.unregister();
}
}
#Override
public void onAudioVolumeChanged(int currentVolume, int maxVolume) {
Log.d("Audio", "Volume: " + currentVolume + "/" + maxVolume);
Log.d("Audio", "Volume: " + (int) ((float) currentVolume / maxVolume) * 100 + "%");
}
}
public class AudioVolumeContentObserver extends ContentObserver {
private final OnAudioVolumeChangedListener mListener;
private final AudioManager mAudioManager;
private final int mAudioStreamType;
private int mLastVolume;
public AudioVolumeContentObserver(
#NonNull Handler handler,
#NonNull AudioManager audioManager,
int audioStreamType,
#NonNull OnAudioVolumeChangedListener listener) {
super(handler);
mAudioManager = audioManager;
mAudioStreamType = audioStreamType;
mListener = listener;
mLastVolume = audioManager.getStreamVolume(mAudioStreamType);
}
/**
* Depending on the handler this method may be executed on the UI thread
*/
#Override
public void onChange(boolean selfChange, Uri uri) {
if (mAudioManager != null && mListener != null) {
int maxVolume = mAudioManager.getStreamMaxVolume(mAudioStreamType);
int currentVolume = mAudioManager.getStreamVolume(mAudioStreamType);
if (currentVolume != mLastVolume) {
mLastVolume = currentVolume;
mListener.onAudioVolumeChanged(currentVolume, maxVolume);
}
}
}
#Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
}
public class AudioVolumeObserver {
private final Context mContext;
private final AudioManager mAudioManager;
private AudioVolumeContentObserver mAudioVolumeContentObserver;
public AudioVolumeObserver(#NonNull Context context) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
public void register(int audioStreamType,
#NonNull OnAudioVolumeChangedListener listener) {
Handler handler = new Handler();
// with this handler AudioVolumeContentObserver#onChange()
// will be executed in the main thread
// To execute in another thread you can use a Looper
// +info: https://stackoverflow.com/a/35261443/904907
mAudioVolumeContentObserver = new AudioVolumeContentObserver(
handler,
mAudioManager,
audioStreamType,
listener);
mContext.getContentResolver().registerContentObserver(
android.provider.Settings.System.CONTENT_URI,
true,
mAudioVolumeContentObserver);
}
public void unregister() {
if (mAudioVolumeContentObserver != null) {
mContext.getContentResolver().unregisterContentObserver(mAudioVolumeContentObserver);
mAudioVolumeContentObserver = null;
}
}
}
public interface OnAudioVolumeChangedListener {
void onAudioVolumeChanged(int currentVolume, int maxVolume);
}
Hope it's still useful for someone! :)
Nathan's and adi's code works, but can be cleaned up and self-contained to:
public class AudioStreamVolumeObserver
{
public interface OnAudioStreamVolumeChangedListener
{
void onAudioStreamVolumeChanged(int audioStreamType, int volume);
}
private static class AudioStreamVolumeContentObserver
extends ContentObserver
{
private final AudioManager mAudioManager;
private final int mAudioStreamType;
private final OnAudioStreamVolumeChangedListener mListener;
private int mLastVolume;
public AudioStreamVolumeContentObserver(
#NonNull
Handler handler,
#NonNull
AudioManager audioManager, int audioStreamType,
#NonNull
OnAudioStreamVolumeChangedListener listener)
{
super(handler);
mAudioManager = audioManager;
mAudioStreamType = audioStreamType;
mListener = listener;
mLastVolume = mAudioManager.getStreamVolume(mAudioStreamType);
}
#Override
public void onChange(boolean selfChange)
{
int currentVolume = mAudioManager.getStreamVolume(mAudioStreamType);
if (currentVolume != mLastVolume)
{
mLastVolume = currentVolume;
mListener.onAudioStreamVolumeChanged(mAudioStreamType, currentVolume);
}
}
}
private final Context mContext;
private AudioStreamVolumeContentObserver mAudioStreamVolumeContentObserver;
public AudioStreamVolumeObserver(
#NonNull
Context context)
{
mContext = context;
}
public void start(int audioStreamType,
#NonNull
OnAudioStreamVolumeChangedListener listener)
{
stop();
Handler handler = new Handler();
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioStreamVolumeContentObserver = new AudioStreamVolumeContentObserver(handler, audioManager, audioStreamType, listener);
mContext.getContentResolver()
.registerContentObserver(System.CONTENT_URI, true, mAudioStreamVolumeContentObserver);
}
public void stop()
{
if (mAudioStreamVolumeContentObserver == null)
{
return;
}
mContext.getContentResolver()
.unregisterContentObserver(mAudioStreamVolumeContentObserver);
mAudioStreamVolumeContentObserver = null;
}
}
If its only ringer mode change you can use Brodcast receiver with "android.media.RINGER_MODE_CHANGED" as the action. It will easy to implement
Hi i tried the code above and it did not work for me. But when i tried to add this line
getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC);
and put
mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );
It works now. My concern is how to hide the volume dialog onchange. See this image.
private const val EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"
private const val VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"
val filter = IntentFilter(VOLUME_CHANGED_ACTION)
filter.addAction(RINGER_MODE_CHANGED_ACTION)
val receiver = object : BroadcastReceiver() {
override fun onReceive(context1: Context, intent: Intent) {
val stream = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, UNKNOWN)
val mode = intent.getIntExtra(EXTRA_RINGER_MODE, UNKNOWN)
val volumeLevel = audioManager.getStreamVolume(stream)
}
}
100% working way in all cases
public class SettingsContentObserver extends ContentObserver {
SettingsContentObserver(Handler handler) {
super(handler);
}
#Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
volumeDialogContract.updateMediaVolume(getMediaVolume());
}
int getMediaVolume() {
return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
}
void unRegisterVolumeChangeListener() {
volumeDialogContract.getAppContext().getApplicationContext().getContentResolver().
unregisterContentObserver(settingsContentObserver);
}
void registerVolumeChangeListener() {
settingsContentObserver = new VolumeDialogPresenter.SettingsContentObserver(new Handler());
volumeDialogContract.getAppContext().getApplicationContext().getContentResolver().registerContentObserver(
android.provider.Settings.System.CONTENT_URI, true,
settingsContentObserver);
}

Install APK by programmatically Xamarin.Forms (Android)

So far my code looks like this:
1. Dowload the APK file and save it into internal storege:
Using DependencyServices
App.xaml.cs
IDownloader downloader = DependencyService.Get<IDownloader>();
protected override void OnStart(){
downloader.OnFileDownloaded+=OnFileDownloaded;
downloader.DownloadFile("http://localhost:8080/download","folder");
}
private void OnFileDownloaded(object sender,DownloadEventArgs e) {
if(e.FileSaved) {
App.Current.MainPage.DisplayAlert("XF Downloader","File Saved Successfully","Close");
} else {
App.Current.MainPage.DisplayAlert("XF Downloader","Error while saving the file","Close");
}
}
Android : AndroidDownloader.cs
[assembly: Dependency(typeof(NoguianaNucleo.Droid.AndroidDownloader))]
namespace NoguianaNucleo.Droid {
public class AndroidDownloader: IDownloader {
public event EventHandler<DownloadEventArgs> OnFileDownloaded;
public void DownloadFile(string url,string folder) {
string pathToNewFolder = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),folder);
Directory.CreateDirectory(pathToNewFolder);
try {
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted+=new AsyncCompletedEventHandler(Completed);
string pathToNewFile = Path.Combine(pathToNewFolder,"nucleo.apk");
webClient.DownloadFileAsync(new Uri(url),pathToNewFile);
} catch(Exception ex) {
if(OnFileDownloaded!=null)
OnFileDownloaded.Invoke(this,new DownloadEventArgs(false));
}
}
private void Completed(object sender,AsyncCompletedEventArgs e) {
if(e.Error!=null) {
App.Current.MainPage.DisplayAlert("Error", e.Error.Message,"Ok");
if(OnFileDownloaded!=null)
OnFileDownloaded.Invoke(this,new DownloadEventArgs(false));
} else {
if(OnFileDownloaded!=null)
OnFileDownloaded.Invoke(this,new DownloadEventArgs(true));
}
}
}
}
2. Intall the APK file from the internal storege:
App.xaml.cs
public void OpenApk(string filepath) {
Java.IO.File file = new Java.IO.File(filepath);
Intent install = new Intent(Intent.ActionView);
// Old Approach
if(Android.OS.Build.VERSION.SdkInt<Android.OS.BuildVersionCodes.N) {
install.SetFlags(ActivityFlags.NewTask|ActivityFlags.GrantReadUriPermission);
install.SetDataAndType(Android.Net.Uri.FromFile(file),"application/vnd.android.package-archive"); //mimeType
} else {
Android.Net.Uri apkURI = Android.Support.V4.Content.FileProvider.GetUriForFile(Android.App.Application.Context,Android.App.Application.Context.ApplicationContext.PackageName+".fileprovider",file);
install.SetDataAndType(apkURI,"application/vnd.android.package-archive");
install.AddFlags(ActivityFlags.NewTask);
install.AddFlags(ActivityFlags.GrantReadUriPermission);
}
Android.App.Application.Context.StartActivity(install);
}
This last function doesn't work. I think Android.Support its not support any more.
I also tried this:
var downloadUri = Android.Net.Uri.Parse("/data/user/0/noguiana.nucleo/files/noguiana/nucleo.apk");
Intent install = new Intent(Intent.ActionInstallPackage);
install.AddFlags(ActivityFlags.GrantReadUriPermission);
install.AddFlags(ActivityFlags.GrantWriteUriPermission);
install.AddFlags(ActivityFlags.GrantPersistableUriPermission);
install.SetDataAndType(downloadUri,"application/vnd.android.package-archive");
context.StartActivity(install);
Nothing works
Do you know other way to install APK by programmatically in Xamarin.Forms (Android)?
Use PackageInstaller instead.
ActionView and ACTION_INSTALL_PACKAGE had been deprecated in API level 29.
Have you tried this solution?
Android PackageInstaller not installing APK

Ads are working In Editor but not In Android

I am trying to use ads In my game . They are properly working in Unity but not in Android. Because of I am using unity 2018.4.25f1 personal so it's supporting older version of unity monetization asset. Maybe it's a problem but here is my code of Rewarded Video CSharp file
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Advertisements;
[RequireComponent(typeof(Button))]
public class RewardedVideo : MonoBehaviour, IUnityAdsListener
{
#if UNITY_IOS
private string gameId = "3853032";
#elif UNITY_ANDROID
private string gameId = "3853033";
#endif
[SerializeField]Button myButton;
[SerializeField]GameObject errorMessage;
public string myPlacementId = "rewardedVideo";
void Start()
{
// Map the ShowRewardedVideo function to the button’s click listener:
if (myButton)
myButton.onClick.AddListener(ShowRewardedVideo);
// Initialize the Ads listener and service:
Advertisement.AddListener(this);
Advertisement.Initialize(gameId, true);
}
public void okError()
{
errorMessage.SetActive(false);
}
// Implement a function for showing a rewarded video ad:
public void ShowRewardedVideo()
{
if(Advertisement.IsReady() == true)
Advertisement.Show(myPlacementId);
else
errorMessage.SetActive(true);
}
// Implement IUnityAdsListener interface methods:
public void OnUnityAdsReady(string placementId)
{
// If the ready Placement is rewarded, activate the button:
if (placementId == myPlacementId)
{
// myButton.interactable = true;
}
}
public void OnUnityAdsDidFinish(string placementId, ShowResult showResult)
{
// Define conditional logic for each ad completion status:
if (showResult == ShowResult.Finished)
{
int levels = PlayerPrefs.GetInt("unlockedLevel", 1);
if(levels != 5)
{
PlayerPrefs.SetInt("unlockedLevel", levels+1);
}
}
else if (showResult == ShowResult.Skipped)
{
// Do not reward the user for skipping the ad.
}
else if (showResult == ShowResult.Failed)
{
Debug.LogError("The ad did not finish due to an error");
}
}
public void OnUnityAdsDidError(string message)
{
// Log the error.
}
public void OnUnityAdsDidStart(string placementId)
{
// Optional actions to take when the end-users triggers an ad.
}
}
Downloaded asset from Assets store not from package manager. I saw Unity help, unity forum, stackoverflow but nothing solved my problem
Have you any suggestion?

Android C#:Listening for Volume button presses in background service

I'm creating an Android App in C# Xamarin.
Is there a way to "listen" for volume up/down key presses when an App goes into "background" mode, i.e. when a user "locks" their phone?
I've created several Service objects and made them "resident" by issuing the command 'StartCommandResult.Sticky'.
Any sample C# Xamarin code would be much appreciated.
You do not need to create a background service, just start a another task to listen the volume control. If the application do not be killed the task will run on the background.
public class MainActivity : Activity
{
private int currentVolume;
public AudioManager mAudioManager;
private int maxVolume;
private bool isDestory;
Android.Media.MediaPlayer player;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
player = Android.Media.MediaPlayer.Create(this, Resource.Raw.SampleAudio);
SetContentView (Resource.Layout.Main);
mAudioManager = (AudioManager)GetSystemService(Context.AudioService);
maxVolume = mAudioManager.GetStreamMaxVolume(Stream.Music);
onVolumeChangeListener();
player.Start();
}
protected override void OnDestroy()
{
base.OnDestroy();
isDestory = true;
}
private Task voluemChangeTask;
public void onVolumeChangeListener()
{
currentVolume = mAudioManager.GetStreamVolume(Stream.Music);
voluemChangeTask = new Task(ChangeVolume);
voluemChangeTask.Start();
}
public void ChangeVolume()
{
while (!isDestory)
{
int count = 0;
try
{
Thread.Sleep(20);
}
catch (Exception e)
{
}
if (currentVolume < mAudioManager.GetStreamVolume(Stream.Music))
{
System.Console.WriteLine("volunm+");
count++;
currentVolume = mAudioManager.GetStreamVolume(Stream.Music);
mAudioManager.SetStreamVolume(Stream.Music, currentVolume, VolumeNotificationFlags.RemoveSoundAndVibrate);
}
if (currentVolume > mAudioManager.GetStreamVolume(Stream.Music))
{
System.Console.WriteLine("volunm-");
count++;
currentVolume = mAudioManager.GetStreamVolume(Stream.Music);
mAudioManager.SetStreamVolume(Stream.Music, currentVolume, VolumeNotificationFlags.RemoveSoundAndVibrate);
}
}
}
}
I have tested it in the real device with screen lock and got the log:

C# Sql Server "loading window"

This is my first post here, but I've using this site regularly to help me with my own app's, and I should say that this site has been a great help to me, so thanks to everyone.
Now my question:
I'm developing my first software app that exchanges data between a sql server and the app itself. It's beeing developed in C#. Saving or retreiving data from the sql server database is no problem.
What I want is a way to inform the user of the delay between the local machine (where the app is installed) and the server. I can make some animations or simply display some text messages. What I need help with is how to create the code that activates/fires/runs when that server communication time is running.
If you can't understand the idea, picture a video game. When it's loading (in some games) you can see the loading screen before the game starts. I need some code that displays that "loading window" when the the app is downloading or uploading data from/to the server.
I would appreciate any code example or web site recommendation.
PS: Sorry for the extensive text, but I want to make sure everyone understand so I don't have to repeat it again :P
How do I implement a progress bar in C#?
How to create a smooth progress bar in Visual C#
ProgressBar Class
I have developed a simple PleaseWait class 2 years ago, but I didn't update this class, It works very well, have look hope this will give you an idea to implement your logic.
public partial class frmWait : Form
{
public frmWait()
{
InitializeComponent();
}
bool _isMoving = false;
int _moveStart_x = 0;
int _moveStart_y = 0;
private void tmrProgress_Tick(object sender, EventArgs e)
{
if (barProgress.Value == barProgress.Maximum)
barProgress.Value = barProgress.Minimum;
else
barProgress.Value += 1;
}
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
PleaseWait.Abort();
}
protected override CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams p = base.CreateParams;
p.ClassStyle += 0x20000;
p.ExStyle += 0x8000000;
return p;
}
}
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 132;
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
if (m.Result.ToInt32() == 1)
m.Result = new IntPtr(2);
break;
}
}
private void panelEx1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_isMoving = true;
_moveStart_x = e.X;
_moveStart_y = e.Y;
}
}
private void panelEx1_MouseUp(object sender, MouseEventArgs e)
{
_isMoving = false;
}
private void pnlContainer_MouseMove(object sender, MouseEventArgs e)
{
if (_isMoving)
this.Location = new Point(Location.X + e.X - _moveStart_x, Location.Y + e.Y - _moveStart_y);
}
}
public class PleaseWait
{
#region Static Operations
private static Boolean _isAborted = false;
private static Boolean _isVisible = false;
private static frmWait _waitForm;
private static String _waitingState = "";
private static Boolean _autoClose = false;
private static Boolean _cancelable = false;
private static System.Threading.Thread _waiterThred;
public delegate void CancelButtonPressed();
public static event CancelButtonPressed OnCancel;
public static Boolean AutoClose
{
get { return PleaseWait._autoClose; }
set { PleaseWait._autoClose = value; }
}
public static string WaitingState
{
get { return PleaseWait._waitingState; }
set { PleaseWait._waitingState = value; }
}
public static bool IsVisible
{
get { return _isVisible; }
internal set { _isVisible = value; }
}
public static void ShowPleaseWait()
{
ShowPleaseWait("", _autoClose, false);
}
public static void ShowPleaseWait(string waitingState)
{
ShowPleaseWait(waitingState, _autoClose, false);
}
public static void ShowPleaseWait(bool autoClose)
{
ShowPleaseWait("", autoClose, false);
}
public static void ShowPleaseWait(string waitingState, bool autoClose, bool cancelable)
{
if (_waiterThred != null)
{
if (_isVisible)
{
// the please wait it woking, just continue and apply the changes
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
return;
}
else
{
_waiterThred.Abort();
_waiterThred = null;
}
}
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
_isAborted = false;
_isVisible = false;
if (_autoClose)
Application.Idle += new EventHandler(Application_Idle);
_waiterThred = new System.Threading.Thread(DisplayWaitingForm);
_waiterThred.IsBackground = true;
_waiterThred.Name = "Please Wait....";
_waiterThred.Start();
Application.DoEvents();
}
public static void Abort()
{
_isAborted = true;
}
private static void Application_Idle(object sender, EventArgs e)
{
if (_autoClose)
_isAborted = true;
}
private static void DisplayWaitingForm()
{
if (_waitForm != null)
{
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
_isVisible = false;
}
try
{
if (_isAborted)
return;
_waitForm = new frmWait();
if (_cancelable)
{
_waitForm.btnCancel.Enabled = true;
_waitForm.btnCancel.Click += new EventHandler(btnCancel_Click);
}
try
{
_isVisible = true;
_waitForm.Show();
_waitForm.Focus();
while (!_isAborted)
{
System.Threading.Thread.Sleep(15);
_waitForm.lblMessage.Text = _waitingState;
Application.DoEvents();
_waitForm.lblMessage.Text = _waitingState;
}
_isVisible = false;
}
finally
{
FreeWaitingForm();
}
}
finally
{
_isVisible = false;
}
}
static void btnCancel_Click(object sender, EventArgs e)
{
if (_waitForm.InvokeRequired)
{
_waitForm.BeginInvoke(new EventHandler(btnCancel_Click), new object[] { e });
}
else
{
if (OnCancel != null)
OnCancel.Invoke();
}
}
private static void FreeWaitingForm()
{
_waitingState = "";
_isVisible = false;
if (_waitForm == null)
{
return;
}
_waitForm.Hide();
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
}
#endregion
}
use like following code :
PleaseWait.ShowPleaseWait("Please wait", true, false);
// If second param is true then it will close the form automatically.
// If third param is true the it will expose a cancel button, so you can cancel your Asynchronous operations.
I didn't insert design code, you can understand by looking at code.
hope this help.
First let me thank you for your replies.
Toby your answer got me thinking about thread monitoring my sql connections but it was a bit tricky and confusing since the app is still in develop and will use a lot more connections.
S.Amani answer it wasn't quite what I want, but thanks to that I found a easier way. I created a form (could be anything else), placed a label saying: Saving To Data Base, took out the top bar, defined location and defined it's parent to be disabled when shown and enabled when closed. The following code is what I put inside my DataBaseInteractionClass
private Wait myCustomWaitDialog = new Wait(); // My Waiting form
private void SaveToDatabase(myObjectToSave obj) // Method called to save data do DB
{
// Create the connections and queries
(...)
// This is what I did
// Show Waiting Form
myCustomWaitDialog.Show();
// Instanciate the command that will carry the query and to DB
SqlCommand command = new SqlCommand(Queries.GetData(code), conn);
// This is important
//Create event that will fire when the command completes
command.StatementCompleted += new StatementCompletedEventHandler(command_StatementCompleted);
// Execute the transaction
SqlDataReader reader = command.ExecuteReader();
// Rest of the code (validations, close connections, try/catch, etc
(...)
}
void command_StatementCompleted(object sender, StatementCompletedEventArgs e)
{
// This is the method that closes my Waiting Dialog
myCustomWaitDialog.CloseDialog();
myCustomWaitDialog.Dispose();
}
It's not quite what I want yet, but is the best solution that I found so far. For now it will do :)
Anyway, thanks for the replies and I hope this helps someone else.

Categories