The new one without a "get": https://developer.android.com/reference/android/view/Surface#setFrameRate(float,%20int)
While Unreal Engine uses the older to get:
public int AndroidThunkJava_GetNativeDisplayRefreshRate()
{
if(ANDROID_BUILD_VERSION >= 24)
{
WindowManager windowManager = getWindowManager();
Display display = windowManager.getDefaultDisplay();
Display.Mode currentmode = display.getMode();
return (int)currentmode.getRefreshRate();
}
return 60;
}
For the most part, I'd think setFrameRate is provided as a smoother way to set, if no new get is provided, the old get should work.
This further convinces me:
https://android-developers.googleblog.com/2020/04/high-refresh-rate-rendering-on-android.html
"For this reason, applications may need to know the current device refresh rate. This can be done in the following ways:
SDK:
Registering a display listener with DisplayManager.DisplayListener and querying the refresh rate via Display.getRefreshRate
NDK
Registering a callback with AChoreographer_registerRefreshRateCallback (API level 30)"
Related
Utilizing onSignalStrengthsChanged, getAllCellInfo(), and related methods, my app monitors cell signal data and displays the results in realtime. My code works very well when targeting API 28 and lower, automatically refreshing the data as it changes. Targeting API 29 results in some Android 10 devices failing to update the data -- but not all.
I discovered TelephonyManager.requestCellInfoUpdate() was added to API 29, which may(?) be needed to resolve this issue. However, I have been unable to find any information about this method beyond the concise definition on the Android Reference. Does this method need to be used to refresh cell info? Are any code samples or further explanations available?
If that method is not relevant, is there another change in API 29 that could cause this behavior? ACCESS_FINE_LOCATION is confirmed to be granted, which appears to be the only other relevant API change.
I have noticed the same behaviour targeting Android 10 (API Level 29). The only workaround I have found is to regularly poll the API and look for changes.
Example code below:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
tm.requestCellInfoUpdate(minThreadExecutor, new TelephonyManager.CellInfoCallback() {
#Override
public void onCellInfo(#NonNull List<CellInfo> list) {
//Extract needed data
}
});
}
}, 1000, 1000 );
Reading the docs there is a mention of this in the getAllCellInfo() documentation.
Apps targeting Android Q or higher will no longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps will receive the latest cached results, which may not be current. Apps targeting Android Q or higher that wish to request updated CellInfo should call requestCellInfoUpdate(); however, in all cases, updates will be rate-limited and are not guaranteed. To determine the recency of CellInfo data, callers should check CellInfo#getTimeStamp().
So the preference is if you are targeting Android Q or higher, you should be opting for requestCellInfoUpdate()
// 1. Create a TelephonyManager instance
telephonyManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
// 2. Define a CellInfoCallback callback
TelephonyManager.CellInfoCallback cellInfoCallback = new TelephonyManager.CellInfoCallback() {
#Override
public void onCellInfo(List<CellInfo> cellInfo) {
// DO SOMETHING
}
}
// 3. Now you can call the method to DO SOMETHING
telephonyManager.requestCellInfoUpdate(this.getMainExecutor(), cellInfoCallback);
I have a NotificationListenerService that intercepts all incoming notifications and records the information in a SQLite database. The only issue I'm running into is how to get the status bar icon aka small icon.
notification.icon has been deprecated since API 23, and extras.getInt(Notification.EXTRA_SMALL_ICON) has been deprecated since API 26.
Up until Android 10 (API 29), extras.getInt("android.icon") has worked fine, but now that returns 0 for every notification, despite it interestingly (as far as I can tell) being the same as extras.getInt(Notification.EXTRA_SMALL_ICON).
I know it's recommended to use getSmallIcon() now, but how can I store this in a database? In the past, I've been able to get the resource id from the above methods, but getSmallIcon() returns an Icon object. I know I can convert this to a Drawable, or a Bitmap, but how can I get the resource id of an object I don't know the name of? And from another app, nonetheless.
Note: I know there's a method of getSmallIcon() called getResId(), but the call requires API 28, an API higher than I'd like to have as my minimum.
Am I doing this right? Is there a better way to do this that I can't find?
I figured out a solution for anyone who finds this in the future:
int iconResId = 0;
// if the API is P or above, this is easy
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
iconResId = notificationSmallIcon.getResId();
}
// in case the getResId doesn't work, or the API is below P
if (iconResId == 0) {
/* first try to get it from the small icon
if the icon is from a resource, then the toString() method will contain the resource id,
but masked to a hex value, so we need to get it back to its integer value
*/
final String smallIconString = notificationSmallIcon.toString();
if (smallIconString.contains("id=")) {
final String iconHexId = smallIconString.substring(smallIconString.indexOf("id=") + 5).replace(")", "");
iconResId = Integer.parseInt(iconHexId, 16);
}
/* if still zero, above method didn't work, use the deprecated method as I've found it to still
be reliable despite it, you know, being deprecated since API 23
*/
if (iconResId == 0) {
iconResId = notification.icon;
}
}
// if still zero now, either there's no icon, or none of the above methods work anymore
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.
I've a problem with checking is device supports Mutli Window Mode. I'm using this function to check it isInMultiWindowMode() but it've added in API 24, and when i'm runs my app on device with lower api version it cause an exception. There is any replacement for this function for lower api versions?
There is any replacement for this function for lower api versions?
Not in the Android SDK. There is no multi-window mode (from the Android SDK's standpoint) prior to API Level 23. And, for whatever reason, Google elected not to add isInMultiWindowMode() to ActivityCompat, perhaps because they cannot support the corresponding event (onMultiWindowModeChanged()).
So, here's a free replacement method:
public static boolean isInMultiWindowMode(Activity a) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
return false;
}
return a.isInMultiWindowMode();
}
Add that to some utility class somewhere and call it as needed.
Also note that isInMultiWindowMode() suffers from a race condition that makes it unreliable, IMHO.
What #CommonsWare explained is true, it is a race condition. Hence, isInMultiWindowMode() will give actual result if you call it from inside post method:
View yourView = findViewById(R.id.yourViewId);
yourView.post(new Runnable() {
#Override
public void run() {
boolean actualResult = isInMultiWindowMode();
}
});
I am developing an android app, which will receive the current location and display it on the map with a marker and then will keep updating that in regular intervals. I followed the following tutorials:
Google Maps Android API v2
Retrieving the Current Location
Receiving Location Updates
I wanted to test my app with mock locations so I followed this tutorial:
Testing Using Mock Locations
However, because of Google's confusing documentation despite my best attempts the first approach failed. I was having this issue:
Android LocationClient mock location not set
Then, I decided to use the mock location provider app and discovered that even though the manifest of the downloaded app has android:minSdkVersion="7", it has a SendMockLocationService.java file which has two function calls elapsedRealtimeNanos() and setElapsedRealtimeNanos() which were added in API Level 17. I need to run this mock location provider app in a device with API Level 8. So, I have changed the line:
elapsedTimeNanos = SystemClock.elapsedRealtimeNanos();
to
elapsedTimeNanos = SystemClock.elapsedRealtime()*1000000;
It is not clear to me how to change this line:
mockLocation.setElapsedRealtimeNanos(elapsedTimeNanos);
How can I change the above-mentioned line so that the mock location provider app becomes compatible with API Level 8?
I updated the sample project you referenced to build with gradle this evening, and noticed the same issue with the use of setElapsedRealtimeNanos, introduced in API level 17. The app worked fine on my 4.4 device, but would crash with a NoSuchMethodError on my 4.1 device.
I went digging in the source code of the location class to see exactly what changed for API 17. Here's the corresponding diff:
https://android.googlesource.com/platform/frameworks/base/+/2eeeec248a38ff33999c83f4b8d5bab7d50e79d2%5E%21/
If you search for usages of the newly-introduced field mElapsedRealtimeNano, you'll see that its value is only queried in a single spot (+ retained to indicate newly-inserted lines):
+ public boolean isComplete() {
+ if (mProvider == null) return false;
+ if (!mHasAccuracy) return false;
+ if (mTime == 0) return false;
+ if (mElapsedRealtimeNano == 0) return false;
+ return true;
+ }
The newly-introduced notion of a Location being 'complete' is also only used in a single spot:
public void setTestProviderLocation(String provider, Location loc) {
+ if (!loc.isComplete()) {
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
+ // for backwards compatibility, allow mock locations that are incomplete
+ Log.w(TAG, "Incomplete Location object", new Throwable());
+ loc.makeComplete();
+ } else {
+ throw new IllegalArgumentException(
+ "Location object not complete. Missing timestamps or accuracy?");
+ }
+ }
+
// original setTestProviderLocation code is then executed here
}
Since this validation is missing from the Location class before API 17, the app should work just fine on all API levels >= 7 if you wrap both calls to SystemClock.elapsedRealtimeNanos() in version checks, i.e.
elapsedTimeNanos = SystemClock.elapsedRealtimeNanos();
should be replaced by
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
elapsedTimeNanos = SystemClock.elapsedRealtimeNanos();
}
and
mockLocation.setElapsedRealtimeNanos(elapsedTimeNanos);
should be replaced by
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mockLocation.setElapsedRealtimeNanos(elapsedTimeNanos);
}
within the SendMockLocationService class. Android Studio will complain that elapsedTimeNanos may not have been initialized - you can safely initialize it with value 0 to resolve this.
With these changes, the app runs fine on my 4.1 device. Let me know if you have any problems on older versions of Android.
Edit: repository for my updated version of the Google sample app.