Is there any elegant way in the Android API for detecting new media when it is written to the device? I’m mainly interested in photos taken by the camera, video taken by the camera and audio recorded from the mic.
My current thinking is to periodically scan each media content provider and filter based on last scan time.
I’m just wondering if there is some service I can get realtime notifications.
There's a special broadcast Intent that should get called every time an application writes anything new to the Media Store:
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
The Broadcast Intent includes the path to the new file, accessible through the Intent.getDataString() method.
To listen for it, just create a BroadcastReceiver and register it using an IntentFilter as shown below:
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newFileURL = intent.getDataString();
// TODO React to new Media here.
}
}, new IntentFilter(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE));
This will only work for files being inserted into one of the Media Store Content Providers. Also, it depends on the application that's putting it there broadcasting the intent, which all the native (Google) application do.
Aha!
A content observer is what i need!
Here's where i found out about it
Related
i need to make a Equalizer for Android.
El audio session ID 0 is deprecated.
Is there a way to get the current audio session ID?
I want to equalize from my app the sound of other apps.
In Google play there are other apps that use the "compatibility mode". but i do not know how they do it. For example, the app detects that spotify is playing, the session is selected and it can equalized.
Does anyone know how do this?
Thanks.
Example applications:
https://play.google.com/store/apps/details?id=com.devdnua.equalizer.free
https://play.google.com/store/apps/details?id=devdnua.equalizerp.free
According to Android, you can use ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION to receive the id of a playing audio session:
Intent to signal to the effect control application or service that a new audio session is opened and requires audio effects to be applied.
I tried adding the constant (and many others) in the manifest, but it only worked for music apps such as Spotify and Youtube Music:
<receiver android:name=".receivers.AudioSessionReceiver">
<intent-filter>
<action android:name="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"/>
</intent-filter>
</receiver>
Then, you can use the id to create an equalizer attached to the session id.
public class AudioSessionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int id = intent.getIntExtra(Equalizer.EXTRA_AUDIO_SESSION, -1);
String packageName = intent.getStringExtra(Equalizer.EXTRA_PACKAGE_NAME);
}
}
When I play my own media file (with my own test app), there are equalizer apps that still work on it even though I didn't broadcast the session id of my media player. So there must be a solution involving a Service that doesn't rely on Broadcast Receivers.
I am using blescan with scanfilters to detect beacons it's working very fine in foreground and background up to oreo version but when it comes to android pie it's not able to send pending broadcast in background.
ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)).build();
final List<ScanFilter> scanFilters = new ArrayList<>();
scanFilters.add(getScanFilter());
BluetoothAdapter bluetoothAdapter;
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
Intent intent = new Intent(this.getApplicationContext(), MyBroadcastReceiver.class);
intent.putExtra("o-scan", true);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
bluetoothAdapter.getBluetoothLeScanner().startScan(scanFilters, settings, pendingIntent);
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int bleCallbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1);
if (bleCallbackType != -1) {
Log.d(TAG, "Passive background scan callback type: "+bleCallbackType);
ArrayList<ScanResult> scanResults = intent.getParcelableArrayListExtra(
BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT);
// Do something with your ScanResult list here.
// These contain the data of your matching BLE advertising packets
}
}
}
Android 9 introduces several behavior changes, such as limiting background apps' access to device sensors and Wi-Fi scans.
These changes affect all apps running on Android 9, regardless of target SDK version.
Sensors that use the continuous reporting mode, such as accelerometers and gyroscopes, don't receive events.
Android 9 Limited access to sensors in background:
Android 9 limits the ability for background apps to access user input and sensor data. If your app is running in the background on a device running Android 9, the system applies the following restrictions to your app:
Sensors that use the continuous reporting mode, such as accelerometers and gyroscopes, don't receive events.
Sensors that use the on-change or one-shot reporting modes don't receive events.
Solution:
If your app needs to detect sensor events on devices running Android 9 while the app is in the background, use a foreground service.
I an example test Android app using Oreo (API 26) and the the code above (slightly modified) to detect beacons. I am using the Pixel 3 XL (with Pie).
I think that the hard part about this is to know for sure if the code in onRecieve() in MyBroadcastReceiver is actually being run upon detection of a beacon when the device is running on battery only (disconnected from Android-studio and Logcat (USB)).
Using Volley (com.android.volley) to submit a HTTP request to a local http server, I was able to demonstrate that it works as documented - ie. I am able to receive the http request when beacon(s) are detected. However, Volley only sends these these requests when Android is awake or when it periodically wakes up and connects to the network - which in my simple tests was about every 15 minutes (plus some variation), but I did get all the beacon ScanResults on my HTTP server, just in delayed up to 15 minutes. I was even able to remove the app from the list of running apps (you know; swiping up to remove the app) and still see that the onRecieve() in MyBroadcastReceiver was receiving BLE ScanResults.
How do you know that the onRecieve() in MyBroadcastReceiver is being killed? I am very interested to know how you know this.
I am working on an android app with an email feature. I want my users to be able to compose and send emails while in airplane mode. For that I need some sort of queue that can check if there is network and send, etc. I image this must have been done 100s of times. But I am not really sure why my searches aren't turning up much. Does anyone know of a library or git project that I can use to accomplish this? If not, does anyone know how to accomplish this?
I believe it is called the Queue and send pattern.
Update
I am starting a bounty on this question. What I hope for is a working example that does not use SMS. For my particular case I am working on an Appengine Connected Android Project. The client needs to send data (String, Bitmap, etc under a particular POJO say Dog) to the server. I want to be able to queue up these data somehow. I can use Gson to save data to file, etc. The bottom line is that I need to be able to check for network. When there is network I dequeue my queue into the server. If there is no network, I keep saving into the queue.
My queue can be Queue<Dog>, where Dog is my class with fields such as Bitmap (or path to image), String, long, etc.
I am looking for a working example. It can be very simple, but the example must work. A git zip would be great. I am giving up close to half of my points for this question.
class Dog{
String dogname;
String pathToImage;
int dogAge;
//etc.
}
//Design pattern for sending Dog to server
0) Unmarshall queue from file using Gson
1) Add dog to queue
2) If there is network, loop through queue and send data to server
3) if there is no network save queue to file
//Ideally, as soon as there is network, the method should be able to detect so and run to send data to server
First you need to set up a receiver to watch the wifi connection to see when they have data, you could also check for normal 3g/4g connections and make a broadcast receiver for that as well. todo this let use implement a broadcast receiver for connection status changes. put something like this in the manifest in the application tag
<receiver android:name=".NetworkChangeReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
now we need to make the receiver we just defined in the manifest
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//here, check that the network connection is available. If yes, start your email service. If not, stop your email service.
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null) {
if (info.isConnected()) {
//start service
Intent intent = new Intent(this, ItemServiceManager.class);
startService(intent);
}
else {
//stop service
Intent intent = new Intent(this, ItemServiceManager.class);
stopService(intent);
}
}
}
}
What this does is puts a big fat antenna called NetworkChangeReceiver out in android land, that is fine tuned to listen in on when android has something to say about a change in the data connection status.
now you need to build your ItemServiceManager.class which should read from a database (it should also extend Service. It should choose the oldest item in the database, (email it, text it, upload to server, whatever), and if the connection was successful then remove the item from the database, and load the next oldest one. If there is no more then close the service and the broadcast receiver.
If you have a connection and the user needs to send more data, then add it to the database, and then make sure the service is started. Maybe notify it that it should double check the database (after a few seconds) before deciding it can close because nothing is there.
This is how you might disable your broadcast receiver.
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, NetworkChangeReceiver.class);
packageManager.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
When a new item is to be uploaded, if there is no web connection, the email should be saved to the database and the broadcast receiver should be started to know when internet is back so it can know when to upload. You might start it up like this.
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, NetworkChangeReceiver.class);
packageManager.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_ENABLED,PackageManager.DONT_KILL_APP);
The whole point is you only care about connection broadcasts when you have something stored to be uploaded but can not upload it because of no data connection. When you have nothing to upload, don't waste processing and battery by keeping your receiver/service around. And when you do have emails waiting, then start up you broadcastreceiver, to know when you have data connection so that you can start uploading.
I do not think anyone is going to write a whole working solution for you, hopefully this is more than enough to get you on your way.
Edit:
Another thing you can do, is let the server allow acceptance of an array of your items, that way you can just upload it all at once when you get a valid connection. Generally you would do this if each item was decently small. But if you are uploading pictures or videos or anything large, best to do it one at a time probably.
Here is the code snippet from our app now.
public void onClick(View v) {
// TODO Auto-generated method stub
Intent sms = new Intent(Intent.ACTION_VIEW, Uri.parse("sms:" ));
sms.putExtra("sms_body", text);
startActivity(sms);
}
This works with the stock SMS app, however a tester reported that with handcent SMS they get a blank message. This prompted me to try it with Google Voice, also get a blank message there.
Is there some way I can get my message text to work with all of these other SMS apps?
We have a clipboard functionality, so a poor workaround at least would be having user push clipboard button, then use their messaging app of choice.
Android tries to reuse Intents as much as possible. In some cases when you start an intent, android doesn't create a new Intent and reuse previous intents. To avoid this set this flag of Intent:
smsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
This flag forces Android to create a new Task with the intent's Activity on top of stack.
When you pass this intent , this will be delivered to all those apps which have registered an activity to receive it. In otherwords you have to use the intent that is registered by the application that u intent to handle your intent
I am writing an Android app where the user can take pictures and I am using my own camera functionality instead of Androids built in camera software. It all works fine except I want to be able to take a picture when the user presses the hard camera button. I registered a Broadcast receiver, and it works but Android still opens its camera program over my app. Is there a way to block the built in app from receiving the broadcast?
I am posting my code below.
Any help would be greatly appreciated.
Thankyou
//Listen for camera button to be pressed
cameraButtonListener = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_CAMERA_BUTTON)){
Toast.makeText(getApplicationContext(), "Camera Button Pressed", Toast.LENGTH_SHORT).show();
}
}
};
//register broadcast receiver to listen for camera button
getApplicationContext().registerReceiver(cameraButtonListener,new IntentFilter(Intent.ACTION_CAMERA_BUTTON) );
You can use abortBroadcast() in conjunction with android:priority set to high to "consume" the broadcast. However, this works only if the broadcast is an Ordered broadcast, and I don't know what type is ACTION_CAMERA_BUTTON. More info here.
try this code
if("android.intent.action.ACTION_CAMERA_BUTTON".equals(intent.getAction()))
in register
getApplicationContext().registerReceiver(cameraButtonListener,new IntentFilter(Intent.ACTION_CAMERA_BUTTON) );