So I created an app that connects to a wireless display on Android automatically for the user. The easiest way I found to do that is by opening the Screen Mirroring settings. This makes it easier for the user, so they don't have to go up to settings and enable it themselves.
Now that I have connected to the display, I want the Screen Mirroring screen to go away and return to the app or to the home screen if the user wants.
Here is the code I use to open Screen Mirroring settings to connect the user to the display after he clicks on a button:
try
{
activityint = 1;
Log.d("DEBUG", "open WiFi display settings in HTC");
startActivityForResult(new Intent("com.htc.wifidisplay.CONFIGURE_MODE_NORMAL"),activityint);
} catch (Exception e)
{
try
{
activityint = 2;
Log.d("DEBUG", "open WiFi display settings in Samsung");
startActivityForResult(new Intent("com.samsung.wfd.LAUNCH_WFD_PICKER_DLG"),activityint);
}catch (Exception e2)
{
activityint=3;
Log.d("DEBUG", "open WiFi display settings in stock Android");
startActivityForResult(new Intent("android.settings.WIFI_DISPLAY_SETTINGS"),activityint);
}
}
And then I have a broadcastreceiver that listens for WIFI_P2P_CONNECTION_CHANGED_ACTION. When this happens, it will look to see if we are now connected before launching other things and then attempting to close the settings activity.
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION))
{
/**
* What to do if the P2P connection has changed
*/
try
{
NetworkInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if(info!=null && info.isConnected())
{
connected(true);
//Kill the settings activity
finishActivity(activityint);
}else if(info!=null && !info.isConnected())
{
connected(false);
}
}catch(Exception e)
{
Log.e("DEBUG", "exception", e);
}
}
}
The problem is that it kills the settings activity before the connection is finalized. So it will back out of the settings activity and the Screen Mirroring connection will cancel a moment before it connects. Is there a better way or a different way to be able to back out of the Settings activity? Or am I listening for the wrong intent in my receiver?
Thanks
The only way I was able to close a settings page on some event, is to start my activity again with the Intent.FLAG_ACTIVITY_CLEAR_TOP flag.
This way, because your activity is the one that opened the settings activity, the flag will make the system finish it. To the user it will appear that the settings activity just went away.
I guess I'm answering my own questions. I found this code that allows me to send a delayed response. I know this is crummy programming because some devices will finish before others, but this works for me at the moment. If anyone knows of a better way, please feel free to let me know.
This goes in my broadcast receiver's onReceive method.
NetworkInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if(info!=null && info.isConnected())
{
connected(true);
new Handler().postDelayed(new Runnable()
{
public void run()
{
finishActivity(activityint);
}
}, 5000);
}else if(info!=null && !info.isConnected())
{
connected(false);
}
EDIT: This actually does not work for me. After testing further, I found that it will kick me back to the home screen after a few moments even when I just open the app. It's pretty annoying. Anyone know of a better idea?
Related
I've just started coding my app which uses Accessibility Service. I'll explain my problem in detail.
Below is my onServiceConnected method of MyAccessibilityService class
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo info = getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.WINDOWS_CHANGE_ADDED;
info.packageNames = new String[]
{THIRD_PARTY_APP_PACKAGE};
info.notificationTimeout = 100;
this.setServiceInfo(info);
}
The app is detecting events in onAccessibilityEvent() method
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
Toast.makeText(this, "Event Occured", Toast.LENGTH_SHORT).show();
}
Now when I open the third party app, I'm getting the Toast "Event occured". Now I close the app and when I open it again, the method is not called and I don't get any Toast. To make it working again, I have to disable the accessibility service of my app in my phone's Settings and again enable it.
I know I'm missing something and my only question is what should be the additional part of code or what modifications I need in order to detect the event every time I open the third party app?
Have you tried getting rid of the notification timeout? You probably don't need it, and it isn't the best-tested API.
Application Info
I've got an application that is able to communicate with a USB accessory when the screen is on.
The first thing I do, after installing the app, is turn the screen on and then plug in the USB accessory. I get the message "Open when this USB accessory is connected?". Additionally, there is a checkbox that says "Use by default for this USB accessory". I click the checkbox and press OK.[1]
Now, whenever I plug in the USB accessory, my application pops to the foreground. The reason this happens is because I have the following in my AndroidManifest.xml:
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="#xml/accessory_filter" />
After this happens, I then call enable() that is defined here:
public boolean enable() {
UsbManager deviceManager = null;
UsbAccessory[] accessories = null;
UsbAccessory accessory = null;
deviceManager = (UsbManager) context
.getSystemService(Context.USB_SERVICE);
accessories = deviceManager.getAccessoryList();
accessory = accessories[0];
if (accessory == null) {
return false;
}
logDRemote("Accessory is " + accessory);
// If we have permission to access the accessory,
if (deviceManager.hasPermission(accessory)) {
parcelFileDescriptor = deviceManager
.openAccessory(accessory);
if (parcelFileDescriptor != null) {
logDRemote("descriptor is not null");
if(parcelFileDescriptor == null) {
logDRemote("parcelFileDescriptor == null");
return false;
}
outputStream = new FileOutputStream(
parcelFileDescriptor.getFileDescriptor());
if(outputStream == null) {
logDRemote( "outputStream == null");
return false;
}
logDRemote("outputStream open");
byte[] data = new byte[2];
data[0] = (byte) APP_CONNECT;
data[1] = 0;
try {
outputStream.write(data);
} catch (IOException e) {
logDRemote("Could not open accessory");
closeAccessory();
return RETURN_CODES.FILE_DESCRIPTOR_WOULD_NOT_OPEN;
}
return true;
} else {
return false;
}
} else {
return false;
}
}
When I call this after the activity pops to the foreground, I call this function and it returns true, showing that the USB device is properly communicating.
Failure with swipe up screen
The problem is, if I have my screen off without a PIN but I have to swipe up to unlock the phone, the following happens:
When I plug in the USB accessory, the screen turns on and says "swipe up"
My activity does not pop up until after I swipe up on the screen
If I never swipe on on the screen and try calling enable(), I get a failure at "Could not open accessory"
That is, the device is able to open properly, and the permissions exist. But it just can't actually write to the accessory when it tries.
Fix for swipe up screen failure
I found a way to fix this first issue. What I was able to do is open up the activity whenever I detect that a USB charger is present[3]. Now the following happens:
I start by installing the application with the USB unplugged
I turn off the screen
I plug in the USB device
The activity pops up
A second activity pops up. I believe that second activity popping up is the Android system opening the popup as a result of the USB attach event[2]
I call enable(), and the write succeeds.
Failure with pin lock
However, this fix does not work when I have a pin set on the phone. Now the following happens:
I start by installing the application with the USB unplugged.
I turn off the screen
I plug in the USB device
The activity pops up
A second activity does not pop up
I try calling enable(), and the write fails.
The Final Question
How do I connect to a USB device when my Android phone is off and has a pin set? I have root, if that helps.
Footnotes
As an aside, this writes to /data/system/users/0/usb_device_manager.xml, as seen in the source here: http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java#mSettingsFile
http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java#775
I'm putting the following in onCreate of the activity, so that it can show up while the screen is off:
-
final Window win= getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
My HomePage is a CarouselPage and it contains three pages of type ContentPage.
<CarouselPage
...some namespaces...
<CarouselPage.Children>
<pages:HomePageA />
<pages:HomePageB />
<pages:HomePageC />
</CarouselPage.Children>
</CarouselPage>
I'm using JamesMontemagno's ConnectivityPlugin to check if the device has internet connection:
public partial class HomePage : CarouselPage
{
public HomePage()
{
InitializeComponent();
if (IsConnectionAvailable())
{
// download content from external db to device (SQLite db)
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
}
else
{
DisplayAlert("No internet connection found.", "Application data may not be up to date. Connect to a working network.", "OK");
}
}
public bool IsConnectionAvailable()
{
if (!CrossConnectivity.IsSupported)
return false;
bool isConnected = CrossConnectivity.Current.IsConnected;
//return CrossConnectivity.Current.IsConnected;
return isConnected;
}
}
After splash screen is gone, the message box doesn't show. Based on stepping through code while debugging I assume it kinda shows and then goes away before the splash screen disappears.
So I tried to put the ConnectivityPlugin code into HomePageA:ContentPage, which one of the children of HomePage:CarouselPage. Like so:
public partial class HomePageA : ContentPage
{
public HomePageA()
{
InitializeComponent();
if (IsConnectionAvailable())
{
// download content from external db to device (SQLite db)
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
}
else
{
DisplayAlert("No internet connection found.", "Application data may not be up to date. Connect to a working network.", "OK");
}
}
public bool IsConnectionAvailable()
{
if (!CrossConnectivity.IsSupported)
return false;
bool isConnected = CrossConnectivity.Current.IsConnected;
//return CrossConnectivity.Current.IsConnected;
return isConnected;
}
private void RegistrationButton_Clicked(object sender, System.EventArgs e)
{
}
}
Now when I run the application, the HomePageA gets displayed, but no message box. Only after I click the BurgerMenu and select Home (effectively selecting HomePageA) the message box pops up.
Same thing happens again:
After splash screen is gone, the message box doesn't show. Based on stepping through code while debugging I assume it kinda shows and then goes away before the splash screen disappears.
Could somebody explain this behavior to me?
How do I make the message box appear AFTER the splash screen goes away?
Thank you all.
===================== U P D A T E =====================
Pavan didn't explain the problem the way I would like to have it explained. For all you good people I found a couple of similar questions and answers with explanations that are adequate.
A decent explanation can be found here and here's another one.
However, I will accept Pavan's answer, because it works and there has been no better answer provided at the time.
DisplayAlert is used in order to do something on the UI thread. and it’s called from a background thread, in order to manipulate the UI, which can only be done on the UI thread.
Try below code for display alert message,
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
App.Current.MainPage.DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
});
it will help you
I'm new here.
I have a problem, i try to shutdown a 4.2.2 android device (not root).
I know that ACTION_SHUTDOWN not works with unroot device.
So i want to open the existing shutdown/reboot/airplane dialog, the same we get when we maintain the on/off button. Then the user just have to click shutdown button.
I try to create by this way, without result...
Intent intent = new Intent(Settings.ACTION_DISPLAY_SETTINGS); // or others settings
startActivity(intent);
Thanks,
The is no public sdk access to open the power button menu programatically.
This link has all the approches Here.Simulating power button press to display switch off dialog box
InputManager.getInstance().injectInputEvent(new InputEvent(KeyEvent.KEYCODE_POWER, keyCode), sync);
'sync' becomes either of these:
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT
and you need
import android.hardware.input.InputManager;
This is untested, but puts you in the right direction, also bare in mind, functionality like this is NOT recommend.
failing that:
public static void simulateKey(final int KeyCode) {
new Thread() {
#Override
public void run() {
try {
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyCode);
} catch (Exception e) {
Log.e("Exception when sendKeyDownUpSync", e.toString());
}
}
}.start();
}
and simply call it like
simulateKey(KeyEvent.KEYCODE_POWER);
I have to Users (User A and B) and one Chromecast device (C1).
User B starts a stream on C1.
User A connects to C1
Now User A should be able to control the stream running on C1. But every time I want to start a session the running stream on C1 is shut down and the receiver app is restarting.
Is there a way to join an active session? Or is that a job which has to be done by the web app running on the Chromecast device?
EDIT:
my sender app is a native Android app
Thanks!
You should have a look to the TicTacToe application. I think it does exactly that where 2 players can join the same game :
https://github.com/googlecast/cast-android-tictactoe
Hope this helps.
JN
What sort of sender are you using? Is it a native app (i.e. using Android or iOs SDK on a mobile device) or the sender is a chrome app?
On the receiver, you create a Receiver object and a ChannelHandler. You use the receiver to generate a ChannelFactory which you then pass to the ChannelHandler. The ChannelHandler now handles the creation of channels on the receiver. You will want to add an EventListener to the handler to listen to messages. Based on those messages you can do various things.
receiver = new cast.receiver.Receiver(YOUR_APP_ID, [YOUR_PROTOCOL], "", 5);
var dashHandler = new cast.receiver.ChannelHandler(YOUR_PROTOCOL);
dashHandler.addChannelFactory(receiver.createChannelFactory(YOUR_PROTOCOL));
dashHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
receiver.start();
...
onMessage = function (e) {
var message = e.message;
switch (message.type) {
...
}
}
On the sender, after a session is created you will want to send a check status message to the receiver to see if there are already channels attached. You can do this via your MessageStream and your receiver needs to respond in such a way that the MessageStream gets its status updated. You check that status to see if there are channels. If there are you can start listening to updates for your receiver. If not you can send a load event to the receiver to start your activity.
MediaProtocolCommand cmd = mMessageStream.requestStatus();
cmd.setListener(new MediaProtocolCommand.Listener() {
#Override
public void onCompleted(MediaProtocolCommand mPCommand) {
if (mMessageStream.getState() == 'channelsExist') {
//Start New Activity
} else {
//Join Existing Activity
}
#Override
public void onCancelled(MediaProtocolCommand mPCommand) {
}
});
This is kind of a vague response, but it could be more specific if I knew what you were trying to do. My app is using Google's RAMP protocol to play videos so my MessageStream and all it's messages are already defined. If you're doing something different, you need to create your own MessageStream.
Sorry for the late answer, but I figured it out by myself: It wasn't such complicated at all
I started the an Application like this
try {
mSession.startSession(applicationName,applicationArgs);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
But it seems, that the MimeData applicationArgs is not needed at all. By removing the arguments and starting the session like below it works really fine!
try {
mSession.startSession(applicationName);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
I hope this works for you too!