Android differentiate between a TV and a STB - android

How can I differentiate between a TV and a STB/game console on AndroidTV programatically? This method (https://developer.android.com/training/tv/start/hardware) won't work because an STB running AndroidTV is considered a television.

What's Your Goal?
The obvious reason for doing this would be to determine if a device is suitable for a game to be played on. If it's for any other reason, then the purpose needs to be elaborated on in order to receive applicable assistance.
With that said ...
Since it's not possible to query the device type directly -- personally, I'd look for something that only a game console would be likely to have.
In other words: a game controller/gamepad.
public ArrayList<Integer> getGameControllerIds() {
ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
int[] deviceIds = InputDevice.getDeviceIds();
for (int deviceId : deviceIds) {
InputDevice dev = InputDevice.getDevice(deviceId);
int sources = dev.getSources();
// Verify that the device has gamepad buttons, control sticks, or both.
if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|| ((sources & InputDevice.SOURCE_JOYSTICK)
== InputDevice.SOURCE_JOYSTICK)) {
// This device is a game controller. Store its device ID.
if (!gameControllerDeviceIds.contains(deviceId)) {
gameControllerDeviceIds.add(deviceId);
}
}
}
return gameControllerDeviceIds;
}
Of course, it's not fool-proof. Obviously, nothing would be returned if the gamepad(s) were unplugged at the time (not sure when that would happen). Not to mention, some TVs support gamepads (Samsung comes to mind first) -- but, if you're intention is to verify that there's an adequate input available for the application, this would be ideal.
If a gamepad isn't present, a message could be displayed stating, "Please connect a gamepad." -- while continuously checking in the background, and automatically proceeding once one is detected.

Related

How to programmatically connect to wifi in Android

I've been searching on this for quite some time, and have found lots of solutions that ultimately use the Android GUI to prompt the end-user for connection/confirmation. We would like to present a user with a form and directly connect to the specified network. I understand from a security perspective why this is necessary, but in our case, it is a system app going on a fully managed Android device. It is not intended for general use in the Play store. We are using Xamarin, but I can adapt any Java/Kotlin code.
The closest I've found thus far is the following (using C#):
public void ConnectToWifi(string ssid, string password = "") {
var wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder().SetSsid(ssid);
if (!string.IsNullOrEmpty(password))
wifiNetworkSpecifier.SetWpa2Passphrase(password);
var networkRequest = new NetworkRequest.Builder().AddTransportType(TransportType.Wifi)?
.SetNetworkSpecifier(wifiNetworkSpecifier.Build())?.Build();
if (networkRequest is null)
return;
var theNetworkCallback = new TheNetworkCallback();
var connectivityManager = (ConnectivityManager?)MainActivity.Current.ApplicationContext?
.GetSystemService(Context.ConnectivityService);
connectivityManager?.RequestNetwork(networkRequest, theNetworkCallback);
}
It sorta works, but does prompt the end-user, and my understanding, this approach is deprecated and doesn't work well in newer versions of Android. We're hoping for a solution that works in Android 11.
I'm even fine if there's a solution to write directly to wifi files on the OS. I've seen various solutions to manually populate entries via ADB, but I'm having a tough time adapting that to Xamarin/Java (can't seem to access the /data/misc/wifi directories). Again, this is intended for use exclusively on our own managed devices.
I have a blog post about this topic here: https://blog.ostebaronen.dk/2020/11/android-11-wifi.html
Android Network API is not the greatest thing to work with as there are pitfals depending on the API level the code runs on.
From Android 10 and up a lot of the Network stuff has been restricted for "privacy" reasons, so you cannot work around not asking the user for input, unless the device is rooted or your App is set up as Device Admin.
For Android 11, there is a new API to present a system dialog and allow the user to save and connect to a network. This will look something like:
You can launch this through an Intent called android.settings.WIFI_ADD_NETWORKS:
var intent = new Intent(
"android.settings.WIFI_ADD_NETWORKS");
var bundle = new Bundle();
bundle.PutParcelableArrayList(
"android.provider.extra.WIFI_NETWORK_LIST",
new List<IParcelable>
{
new WifiNetworkSuggestion.Builder()
.SetSsid(ssid)
.SetWpa2Passphrase(password)
.Build()
});
intent.PutExtras(bundle);
StartActivityForResult(intent, AddWifiSettingsRequestCode);
You can then get the result in your Activity overriding OnActivityResult and fetching the result like so:
if (requestCode == AddWifiSettingsRequestCode)
{
if (data != null && data.HasExtra(
"android.provider.extra.WIFI_NETWORK_RESULT_LIST"))
{
var extras =
data.GetIntegerArrayListExtra(
"android.provider.extra.WIFI_NETWORK_RESULT_LIST")
?.Select(i => i.IntValue()).ToArray() ?? new int[0];
if (extras.Length > 0)
{
var ok = extras
.Select(GetResultFromCode)
.All(r => r == Result.Ok);
// if ok is true, BINGO!
return;
}
}
}
I have a repository here with the full sample: https://github.com/Cheesebaron/Android11WiFi
From using this in the wild, I've found that this API does not work nicely with some OEMs such as OnePlus and Huawei. Some of these either restrict or the System Settings App simply crashes due to a misconfiguration on their part. For those I fall back to the API's introduced in Android 10.

isActivityStartAllowedOnDisplay() method

I'm trying to launch a second activity on a secondary display. This works fine on the emulators, but I need the secondary display to handle touch events.
With Android Pie, launching an activity on some second displays would throw a security exception. Google recently pushed out this new API for Android Q - isActivityStartAllowedOnDisplay() (https://developer.android.com/reference/android/app/ActivityManager.html#isActivityStartAllowedOnDisplay(android.content.Context,%2520int,%2520android.content.Intent)) - to be able to tell if the second display has this security exception or not.
This new API is helpful, BUT, is there any way around it? Maybe I've misunderstood the documentation, but it seems like if the device doesn't support it, then there's no way around it. Does anyone know of any displays that will NOT throw this security exception?
In order to get touch events to register on the secondary display (GeChic Touch Monitor), I had a DisplayLink device connected between the Android device and touch display. At this point, it was mirroring the view on the phone/tablet but would handle touch events. So, I wrote an app that would attempt to launch a second activity on the second display using this code on Android Pie OS:
DisplayManager mgr = (DisplayManager) this.getBaseContext().getSystemService(Context.DISPLAY_SERVICE);
if (mgr != null) {
Display[] displays = mgr.getDisplays();
for (int i = 0; i < displays.length; i++) {
Display display = displays[i];
Point point = new Point();
display.getSize(point);
if (point.y == PX_HEIGHT_OF_SECONDARY_DISPLAY || point.x == PX_HEIGHT_OF_SECONDARY_DISPLAY) {
Context displayContext = createDisplayContext(display);
Intent newIntent = new Intent(displayContext, ActivityCID.class);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(display.getDisplayId());
newIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent, options.toBundle());
return;
}
}
}
Note that I did not use display.getDisplayId() and did a hacky way with the point.y and point.x values with a pixel width or height that did not match the pixel width or height of the Android phone/tablet. The displayId() was not always a consistent value which "should" be stable in Android Q. This is where the app would crash and the second activity would fail with a security permissions error. So, I used Android Q Beta to test the new isActivityStartAllowedOnDisplay() API. I ran this through Android Studio onto the phone (which was on Android Q Beta OS) to run it and to no surprise, the secondary display came back false. See code below:
public void launchOnSecondaryDisplay(Display display) {
Context displayContext = createDisplayContext(display);
Intent newIntent = new Intent(displayContext, ActivityTest.class);
ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Activity.ACTIVITY_SERVICE);
if (activityManager != null) {
boolean allowsDisplay = activityManager.isActivityStartAllowedOnDisplay(displayContext, display.getDisplayId(), newIntent);
if (allowsDisplay) {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(display.getDisplayId());
newIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent, options.toBundle());
} else {
Toast.makeText(this, "Cannot start activity on that display as it is not supported.", Toast.LENGTH_LONG).show();
}
}
}
I decided to try this through the command line. After networking the physical device to match my Mac's connected network, I was able to connect to the phone wirelessly and was able to make changes in adb. Using an adb command, I was able to get a secondary activity on the secondary display! It seemed to be working! But no, it was not... Touch events still continued to act like the device was being mirrored so this was still a problem and was not going to work.
I discussed this with the Googler as well and was explained that adb root can override these permissions. However, there was still no way to get the touch events to map to the second activity on the secondary display.
At the moment of writing this, the only supported way to test multi touch displays is to use a physical device running Android Q Beta and follow these steps:
enable developer options,
in developer options, enable these 4 options: Force All Activities to be Resizeable, Freeform Windows, Force Desktop, and Simulate Secondary Display (doesn't matter which option picked for simulate secondary display),
reboot the device,
connect a mouse to the device. The mouse will show up and be stuck inside the overlaying window that is "simulating the secondary display". This will handle touch events.
In the future, there will be emulators that have multiple displays to better test multi display applications but this is not available at the moment.

Android 5.0 (L) - Check data Roaming Setting

I have a problem identifying the data roaming setting in Android L. In previous versions of Android, I was able to access either Settings.Secure or Settings.Global (depending on the Android version), and get the setting.
But now, on Android L, this no longer works. Whether data roaming is on or off, the return from the Settings.Global is always 0.
Android L supports multi SIM out of the box, so, a new manager was created to handle this: SubscriptionManager. This subscription manager, handles the several settings of the several SIM cards in the form of SubInfoRecord classes. I can retrieve the settings per SIM card.
However, the dataRoaming filed inside that class is always 0 as well.
Does anyone know how can this be achieved on the new API?
My app is a system app that comes embedded in the phones from factory so, I should be able to access all the APIs available.
However, I've spent a long time looking in the source code but I found nothing. In the Settings.Global class there's no indication that that setting no longer works on Android.
Does anyone have a clue on where this setting was moved to?
Thanks in advance!
Check this DevicePolicyManager.setGlobalSetting
as from documentation this can only be called by device owner app.
Is your app is installed as device owner ?
If not you can check the following links
Create device owner without root
Create device owner with root
Do something like this
DevicePolicyManager manager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
manager.setGlobalSetting(<Admin_Component>, Settings.Global.DATA_ROAMING, <value>);
Admin_Component: Component instance
Value: "0" for disable or "1" for enable
Since android 5.0, android supports multiple SIM cards, use the following code to check for data roaming.
public static boolean isDataRoamingEnabled(Context context) {
SubscriptionManager subMngr = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
int id = SubscriptionManager.getDefaultDataSubscriptionId();
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return false;
}
SubscriptionInfo ino = subMngr.getActiveSubscriptionInfo(id);
if (ino == null)
return false;
return ino.getDataRoaming() == 1;
}

Wake lock for Mac OS X

In Android, there's wake lock to keep the screen on. So, is there Mac equivalent to keep the screen on for a PC running Mac OS X? If that is the case, what are the APIs?
To add to it, there are also command-line tools, like the built-in caffeinate.
Yes, in OS X it is done at the OS level through IOPMLib, the Power Management subsystem, which is also the subsystem that controls AppNap under OS X Mavericks.
Here's an example of what we do when performing heavy calculations. In our case, we keep the CPU from sleeping, but you can prevent the display from sleeping by using kIOPMAssertionTypePreventUserIdleDisplaySleep where we used kIOPMAssertionTypePreventUserIdleSystemSleep.
#property IOPMAssertionID currentPowerAssertion;
- (void)assertPowerRequirement:(NSString*)reason
{
// don't re-assert if we're already here
if (_currentPowerAssertion)
return;
IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(
kIOPMAssertionTypePreventUserIdleSystemSleep, // prevent CPU from going to sleep
kIOPMAssertionLevelOn, // we are turning this on
(__bridge CFStringRef)reason, // here's why
&assertionID); // reference for de-asserting
if (success == kIOReturnSuccess) {
_currentPowerAssertion = assertionID;
} else {
NSLog(#"Power assert failed");
}
}
- (void)deassertPowerRequirement
{
if (!_currentPowerAssertion)
return;
IOReturn success = IOPMAssertionRelease(_currentPowerAssertion);
if (success !=kIOReturnSuccess) {
NSLog(#"Power de-assert failed");
}
_currentPowerAssertion = 0;
}
In this case, this is in our App delegate, and we have the currentPowerAssertion property to keep track. Since we only use one assertion state and only for one purpose, we use a single storage mechanism. However, you can assert multiple times from different parts of your program, as long as you balance the assertions with de-assertions and use appropriate reason. Specifications from Apple mandate a reason be given (not NULL), and suggest that the Application name and task be described in the assertion.
It's important to make sure that you de-assert when you don't need this any longer, although assertions are kept on a per-app basis, so when your App quits, they will automatically be de-asserted.

How to prevent name caching and detect bluetooth name changes on discovery

I'm writing an Android app which receives information from a Bluetooth device. Our client has suggested that the Bluetooth device (which they produce) will change its name depending on certain conditions - for the simplest example its name will sometimes be "xxx-ON" and sometimes "xxx-OFF". My app is just supposed to seek this BT transmitter (I use BluetoothAdapter.startDiscovery() ) and do different things depending on the name it finds. I am NOT pairing with the Bluetooth device (though I suppose it might be possible, the app is supposed to eventually work with multiple Android devices and multiple BT transmitters so I'm not sure it would be a good idea).
My code works fine to detect BT devices and find their names. Also, if the device goes off, I can detect the next time I seek, that it is not there. But it seems that if it is there and it changes name, I pick up the old name - presumably it is cached somewhere. Even if the bluetooth device goes off, and we notice that, the next time I detect it, I still see the old name.
I found this issue in Google Code: here but it was unclear to me even how to use the workaround given ("try to connect"). Has anyone done this and had any luck? Can you share code?
Is there a simple way to just delete the cached names and search again so I always find the newest names? Even a non-simple way would be good (I am writing for a rooted device).
Thanks
I would suggest 'fetchUuidsWithSdp()'. It's significance is that, unlike the similar getUuids() method, fetchUuidsWithSdp causes the device to update cached information about the remote device. And I believe this includes the remote name as well as the SPD.
Note that both the methods I mentioned are hidden prior to 4.0.3, so your code would look l ike this:
public static void startServiceDiscovery( BluetoothDevice device ) {
// Need to use reflection prior to API 15
Class cl = null;
try {
cl = Class.forName("android.bluetooth.BluetoothDevice");
} catch( ClassNotFoundException exc ) {
Log.e(CTAG, "android.bluetooth.BluetoothDevice not found." );
}
if (null != cl) {
Class[] param = {};
Method method = null;
try {
method = cl.getMethod("fetchUuidsWithSdp", param);
} catch( NoSuchMethodException exc ) {
Log.e(CTAG, "fetchUuidsWithSdp not found." );
}
if (null != method) {
Object[] args = {};
try {
method.invoke(device, args);
} catch (Exception exc) {
Log.e(CTAG, "Failed to invoke fetchUuidsWithSdp method." );
}
}
}
}
You'll then need to listen for the BluetoothDevice.ACTION_NAME_CHANGED intent, and extract BluetoothDevice.EXTRA_NAME from it.
Let me know if that helps.

Categories

Resources