I am getting the following exception in my android app.
No Activity found to handle Intent { act=android.media.action.STILL_IMAGE_CAMERA (has extras) }
I know that my device, an MC70, has a camera.
bool hasFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
int numCameras = android.hardware.Camera.getNumberOfCameras();
Both hasFeature is true and numCameras > 0
The device has an SD card installed:
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
In the AndroidManifest.xml file I have:
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
final PackageManager packageManager = context.getPackageManager();
This list comes back empty, probably a bad sign:
final Intent intent = new Intent(action);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
This code must run on the MC70, which is limited to KitKat 4 API 19.
But when I call:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, 1);
I get the exception:
No Activity found to handle Intent { act=android.media.action.STILL_IMAGE_CAMERA_SECURE (has extras) }
I have tried:
MediaStore.ACTION_IMAGE_CAPTURE
MediaStore.ACTION_IMAGE_CAPTURE_SECURE
MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA
MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE
All throw the exception. Why?
Why?
Because the device manufacturer did not install a camera app that supports any of those Intent structures. If the device does not legitimately ship with the Play Store on it, there is no requirement that the manufacturer have to meet any particular compatibility requirements.
Assuming that you do not need a camera app with any particular features, install an open source one, like Open Camera, so your device has an app that responds to ACTION_IMAGE_CAPTURE, etc.
Related
The goal is to update my app without using the google play store. I’m trying to download a .apk file from a server and then install it programmatically. I’m currently getting an error that the download is unsuccessful and that there was an error while parsing the package. I have the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions requested at runtime. I had to go into the tablet's settings to give permission to “Install unknown apps”. I cannot get REQUEST_INSTALL_PACKAGES to be requested at runtime.
If I change the fileName and update URL to get a .txt that I store in the same folder on the server as the .apk AND comment out “.setMimeType()” I can download and view a .txt file.
Have the updates to Android Studio made older tutorials or examples of this outdated?
Is there a new or better way to download .apk’s from servers programmatically?
Is the lack of REQUEST_INSTALL_PACKAGES permission at runtime what is preventing my .apk from downloading?
Any advice on how to fix my code?
Here are some code snippets to help
gradle:app
android {
compileSdk 31
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "farrpro.project"
minSdk 28
targetSdk 30
}
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.ACTION_INSTALL_PACKAGE"/>
MainActivity.java
private void hasInstallPermission() { // runs in onCreate()
if (getApplicationContext().checkSelfPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, 1);
}
}
void downloadAndInstallUpdate(){ // runs when users accepts request to download update
String fileName = "update.apk";
// example of update’s string
update = “https://myserver.net/update.apk”;
//set download manager
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(update));
request.setTitle(fileName)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDescription("Downloading")
//.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
.setMimeType("application/vnd.android.package-archive");
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
System.out.println("Max Bytes: "+DownloadManager.getMaxBytesOverMobile(this)); //returns null
//set BroadcastReceiver to install app when .apk is downloaded
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
System.out.println("Is download successful: "+ manager.getUriForDownloadedFile(downloadId)); //returns null
System.out.println("Mime: "+manager.getMimeTypeForDownloadedFile(downloadId)); //returns application/vnd.android.package-archive
Intent install = new Intent(Intent.ACTION_VIEW);
File file = new File(ctxt.getExternalFilesDir(null) + fileName);
Uri downloadedApk = FileProvider.getUriForFile(ctxt, "farrpro.project.provider", file);
install.setDataAndType(downloadedApk,
manager.getMimeTypeForDownloadedFile(downloadId));
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(install);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Log.e("TAG", "Error in opening the file!");// this never prints
}
unregisterReceiver(this);
finish();
}
};
//register receiver for when .apk download is complete
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
The problem was the server had restricted download types. My code worked more or less after that setting was changed. I ended up needing to change the intents in my broadcast receiver so that after the download was completed the new .apk would immediately install. Here are the changes I made to make the code posted work.
final long downloadId = manager.enqueue(request);
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent1) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(manager.getUriForDownloadedFile(downloadId), "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
getApplicationContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Log.e("TAG", "Error in opening the file!");
}
unregisterReceiver(this);
finish();
}
};
If your app is running on Android O or newer, you have to allow unknown app sources for your app.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!context.packageManager.canRequestPackageInstalls()) {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = Uri.parse(String.format("package:%s", context.packageName))
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}
}
I`m development network radio app for Android API 21-29 and all worked fine but one cases confuses me. On Huawei Honor 10 when my app started playing and trying to switch on the Bluetooth nothing works. Device turn-off Bluetooth and all. I add this permission in my AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
If Bluetooth turn on before playing, he turn off right after start play.
I used this method when app send mediasession.OnPlay
#Override
public void onPlay() {
if (!exoPlayer.getPlayWhenReady()) {
startService(new Intent(getApplicationContext(), PlayerService.class));
if (stationsRepository == null)
return;
station = stationsRepository.getCurrent();
bitrate = Connectivity.currentBitrate;
String stream = station.getStreamByBitrate(bitrate);
prepareToPlay(Uri.parse(stream));
mediaSession.setQueueTitle(station.getTitle());
if (!audioFocusRequested) {
audioFocusRequested = true;
int audioFocusResult;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioFocusResult = audioManager.requestAudioFocus(audioFocusRequest);
} else {
audioFocusResult = audioManager.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
if (audioFocusResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
return;
}
mediaSession.setActive(true);
registerReceiver(becomingNoisyReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
exoPlayer.setPlayWhenReady(true);
}
mediaSession.setPlaybackState(stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1).build());
currentState = PlaybackStateCompat.STATE_PLAYING;
refreshNotificationAndForegroundStatus(currentState);
}
On same devices uses Android 10, Android 8, Android 7 all work is fine.
Can anyone help me fix with this.
P.S. Sorry for my English. This is the first time when I ask a question in this language.
Update: Tested on Huawei P30 lite and catch this bug too.
i'm using the below app code to recieve locations updates from the GPS every 5 seconds and print an output to the log. this works fine for me as long as the app is in foreground, but the updates stop when it is moved to background until it is being brought back to foreground. i'm using galaxy s20 (android 10) to run and debug, and the application is set to never be put to sleep (android setting). here is the full process description:
i have these permissions at the manifest:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.android.hardware.location.gps"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
this is the main activity that enables the GPS and creates the service:
public class MainActivity extends AppCompatActivity {
private LocationManager _locManager;
private Intent _scanServiceIntent;
public void onStart()
{
super.onStart();
boolean gps_enabled = _locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (gps_enabled)
{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET}, 1);
_scanServiceIntent = new Intent(this, ScanService.class);
startService(_scanServiceIntent);
}
}
}
this is the service class - i'm creating a notification in order to make the serice run all the time - even when the app goes to background:
public class ScanService extends Service {
private LocationListener _locListener;
private LocationManager _locManager;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
String NOTIFICATION_CHANNEL_ID = "com.tandenkore.mychannel";
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MAX)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentIntent(pendingIntent)
.build();
startForeground(300, notification);
return START_STICKY;
}
#Override
public void onCreate() {
_locListener = new MyLocationListener();
_locManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
_locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, _locListener);
}
#Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
_locManager.removeUpdates(_locListener);
}
class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location location)
{
Log.v(TAG, "location changes");
}
}
}
when i debug it on the device and the application is in the foreground, i am getting an output in logcat of "location changes" line every 5 seconds. this is the behavior i am looking for.
the problem happens when the app is going to background, then i receive the following in logcat:
2021-04-11 00:00:26.648 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:31.657 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:36.656 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:41.656 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:46.653 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:50.170 7241-7307/? E/RequestManager_FLP: [LocationManager] Paused by AppOps 7e14624(Listener) gps interval=5000 from com.tandenkore.application (10367)
2021-04-11 00:00:50.173 7241-7307/? I/RequestManager_FLP: onOpChanged, op=0 / packageName=com.tandenkore.application
2021-04-11 00:00:50.174 7241-7307/? I/PackageInfoManager_FLP: checkLocationAccess for com.tandenkore.application[gps,5000,100], result is false
but after this last message - no more updates until i bring the app back to the foreground. i want it to keep it running ALL the time - what else needs to be done for this?
According to the official documentation :
When a feature in your app requests background location on a device
that runs Android 10 (API level 29), the system permissions dialog
includes an option named Allow all the time. If the user selects this
option, the feature in your app gains background location access.
On Android 11 (API level 30) and higher, however, the system dialog
doesn't include the Allow all the time option. Instead, users must
enable background location on a settings page, as shown in figure 3.
So you need to see this option named Allow all the time in the Setting page of your application
for that you must have this permission in your manifest :
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
On API 30 devices, and below so that the Allow All the time option will be visible in the Dialog you must Add the "Manifest.permission.ACCESS_BACKGROUND_LOCATION" when you ask the user to grant them permission. as below
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION ,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION},
1);
as you see the dialog has the Allow all the time
But on the Android Api 30 and higher you must invite the user to check this option manually against the API < 30
So if you want to load the Setings page you can use this method :
// load the permission setting screen
private void loadPermissionPage(Activity context) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", context.getPackageName(), null);
intent.setData(uri);
context.startActivityForResult(intent, 0);
}
And finally if you check the Allow all the time option your application will be able to listen to the location changes in the background
UPDATE :
For Android 30 and higher you can ask the user to garant the Background Location by opening the location permission pages directly using this line of code :
requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, REQUEST_CODE);
For more information see the Request background location
Mention the android.foregroundServiceType="location" in the service tag in the AndroidManifest.xml file.
Example :
<service
android:name=".LocationServicePackage.LocationService"
android:foregroundServiceType="location"
android:enabled="true"
android:exported="true" />
In Android 10 there apply new restrictions for apps.
We can no longer start an activity from background. While this may be fine for the majority of apps, it's a killing blow for voip-apps that need to show an incoming call after a push notification arrived.
According to this https://developer.android.com/guide/components/activities/background-starts there is a list of conditions that can be met to still allow opening an activity, but tbh I do not understand that fully (non-english-native here).
What I definitely know, is:
I do not have any running activity, task, backstack and the like
The app is NOT EVEN RUNNING
What I need to achieve:
The FCM service of the app receives a push from our server and shall present the incoming call screen (over lock screen and all - just as it did with android 9 and below)
What can I do to open an activity for an incoming voip call in android 10?
Over the lockscreen and all, just as a normal user would expect from a PHONE app.
Thanks in advance for any hints.
To open Activity over lock screen. you can use a high-notification with "full-screen intent" as CommonsWare's answer. But for more detail, you can try my solution as below code:
Create a foreground service then call buildNotification in onStartCommand method, the buildNotification method will return a notification which put into startForeground method parameter.
public class IncomingCallService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = buildNotification();
startForeground(1, notification);
return START_NOT_STICKY;
}
}
In buildNotification method, we will create notification with high priority, call category and a full screen intent.
private Notification buildNotification() {
Intent fullScreenIntent = new Intent(this, IncomingCallActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notification_icon)
.setContentTitle("Incoming call")
.setContentText("(919) 555-1234")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
// Use a full-screen intent only for the highest-priority alerts where you
// have an associated activity that you would like to launch after the user
// interacts with the notification. Also, if your app targets Android 10
// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
// order for the platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true);
notificationBuilder.setAutoCancel(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(new NotificationChannel("123", "123", NotificationManager.IMPORTANCE_HIGH));
notificationBuilder.setChannelId("123");
}
Notification incomingCallNotification = notificationBuilder.build();
return incomingCallNotification;
}
In onStartCommand, add a line of code to send ACTION_CLOSE_SYSTEM_DIALOGS broadcast action. This verify IMPORTANT to kick off full screen pending intent.
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = buildNotification();
startForeground(1, notification);
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
return START_NOT_STICKY;
}
Create full screen activity which you want to display over lock screen then you need to add setShowWhenLocked and setTurnScreenOn for display over lock screen. If not, your activity will be displayed behind lock screen. Below is my sample.
public class IncomingCallActivity extends AppCompatActivity {
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_explore);
setShowWhenLocked(true);
setTurnScreenOn(true);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
}
}
Now you must start IncomingCallService when you receive a call from your logic.
public void startCallService() {
Intent intent = new Intent(context, IncomingCallService.class);
startForegroundService(intent);
}
You must declare activity, service and some permission in your manifest as below:
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
...>
<activity android:name=".IncomingCallActivity" />
<service
android:name=".IncomingCallService"
android:enabled="true"
android:exported="true" />
</application>
I tested on google, samsung, vsmart phone. It work well. But for xaomi device. you need to enable some permission by flow below steps:
Long click to you app icon
Open app info
Click to "Other permission" item
Allow show on Lock screen
Now your app will work on xaomi device. If you face any problems with my solution, please leave a comment here. I will help you If I could.
Use a high-priority notification with a "full-screen intent". That will:
Invoke your "full-screen intent" if the device is locked
Otherwise, display a "heads-up" notification
Please go through my blog on how to open activity for OS 10 and also how to display heads up notification and handle clicks on the action buttons.
https://medium.com/#dcostalloyd90/show-incoming-voip-call-notification-and-open-activity-for-android-os-10-5aada2d4c1e4
Check this link this will help you
here
or
You need to ask for a permission "draw over other app" then you can make this as previous versions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (!Settings.canDrawOverlays(this)) {
RequestPermission();
}
}
private void RequestPermission() {
// Check if Android P or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + BuildConfig.APPLICATION_ID));
startActivityForResult(intent,
ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
}
or You can use my this answer
https://stackoverflow.com/a/63699960/7108113
I have a scenario in which I fire a intent to open camera through following code.
if(isCameraInUse())
return;
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mImageFile = getOutputFromCamera();
Uri tempuri = Uri.fromFile(mImageFile);
i.putExtra(MediaStore.EXTRA_OUTPUT, tempuri);
startActivityForResult(i, 0);
Now if I have more than one camera app, then I see all of them as option to open. But I want device camera only to be opened. Is this possible? Is it possible to fire an intent specify which app should catch that intent?
I guess you should create an object of android.hardware.Camera. And simply calling open method of it will do the work for you.
Calling open() method on that Camera camera = Camera.open(); it will create a new Camera object to access a particular hardware camera. If the same camera is opened by other applications, this will throw a RuntimeException.
You may use this in your code before
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Hope this helps. I believe that you have added these in your application's manifest.
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
If you want to launch a specific application use this code. I hope it will solve your problem.
PackageManager pm = getActivity().getPackageManager();
Intent intent = pm.getLaunchIntentForPackage("camera package name");
startActivity(intent);
You need to call setPackage on the Intent
i.setPackage("package.name");
startActivityForResult(i, 0);
Instead of this:
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Do this:
Intent i = new Intent("com.android.camera");
Call the camera by the package name think it should help.