As per Android Q new features, there is a inline settings panel showing key connectivity settings that lets the user modify different connectivity settings such as airplane mode, wifi, volume, NFC and internet connectivity.
How can I open that settings panel programmatically from my app? like in screenshot below.
This is very simple and easy to implement using Settings panel API available in Android Q.
Simple we need to trigger intent with one of the new Settings.Panel actions.
To open Internet Connectivity Panel:
Java:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Intent panelIntent = new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
startActivityForResult(panelIntent, 545)
}
Kotlin:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val panelIntent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
startActivityForResult(panelIntent, 545)
}
To open Volume control panel:
Java:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Intent panelIntent = new Intent(Settings.Panel.ACTION_VOLUME)
startActivityForResult(panelIntent, 545)
}
Kotlin:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val panelIntent = Intent(Settings.Panel.ACTION_VOLUME)
startActivityForResult(panelIntent, 545)
}
To open WIFI panel:
Java:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Intent panelIntent = new Intent(Settings.Panel.ACTION_WIFI)
startActivityForResult(panelIntent, 545)
}
Kotlin:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val panelIntent = Intent(Settings.Panel.ACTION_WIFI)
startActivityForResult(panelIntent, 545)
}
To open NFC panel:
Java:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Intent panelIntent = new Intent(Settings.Panel.ACTION_NFC)
startActivityForResult(panelIntent, 545)
}
Kotlin:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val panelIntent = Intent(Settings.Panel.ACTION_NFC)
startActivityForResult(panelIntent, 545)
}
Here you can check more about settings panel from Android official doc:
1) https://developer.android.com/preview/features#settings-panels
2) https://developer.android.com/reference/android/provider/Settings.Panel
Related
I'm trying to add vibration effects to my Android game. I found some code that seems to work, but it's deprecated. What's the current way to create and deploy a vibrator?
var vibrator:Vibrator = getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
vibrator.vibrate(createPredefined(EFFECT_CLICK))
}else{
vibrator.vibrate(50)
}
The parts that are showing up as deprecated are "VIBRATOR-SERVICE" and vibrate.vibrate(50).
Step 1 : Add permission to the AndroidMenifest.xml
<uses-permission android:name="android.permission.VIBRATE" />
Step 2 : You can use this function for vibration.
if (ctx != null) {
if (Build.VERSION.SDK_INT >= 31) {
val vibratorManager =
ctx.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
val vibrator = vibratorManager.defaultVibrator
vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
} else {
val v = ctx.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= 26) {
v.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
} else {
v.vibrate(200L)
}
}
}
fun showNotification(){
val path = Environment.getExternalStorageDirectory().toString() + "/Android/media/"
val uri = Uri.parse(path)
val intent = Intent(Intent.ACTION_PICK)
intent.setDataAndType(uri, "*/*")
val activityPendingIntent = PendingIntent.getActivity(
context,
1,
intent,
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
)
val notification = NotificationCompat.Builder(context, COUNTER_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_download_done)
.setContentTitle("Download Finished")
.setContentText("Files at Android/media/")
.setContentIntent(activityPendingIntent)
.build()
notificationManager.notify(1, notification)
}
here is my code, I only manage to open the folder but I couldn't open the files.
What I wanted to happen is simply redirection to the folder and access the files in there. Is this possible? Am I approaching it wrong?
After a thorough searching, I found an answer that works for me..
// my notification function
fun showNotification(){
// an intent to be initialized when:
lateinit var intent: Intent
// A) Android version is 10 or above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val sm = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
intent = sm.primaryStorageVolume.createOpenDocumentTreeIntent()
var startDir = "Android%2Fmedia" // here is the directory you wanted to open. feel free to test based on your needs.
var uri: Uri = intent.getParcelableExtra("android.provider.extra.INITIAL_URI")!!
var scheme = uri.toString()
scheme = scheme.replace("/root/", "/document/");
scheme += "%3A$startDir";
uri = Uri.parse(scheme);
intent.putExtra("android.provider.extra.INITIAL_URI", uri);
// B) Android Versions 9 and below
} else {
val path = Environment.getExternalStorageDirectory().toString() + "/Android/media/yourpackagename" // path of the directory you wanted to open
val uri = Uri.parse(path)
intent = Intent(Intent.ACTION_PICK)
intent.putExtra("android.provider.extra.INITIAL_URI", uri)
intent.setDataAndType(uri, "*/*")
}
val activityPendingIntent = PendingIntent.getActivity(
context,
1,
intent,
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
)
// shows notification
val notification = NotificationCompat.Builder(context, COUNTER_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_download_done)
.setContentTitle("Download Finished")
.setContentText("Locate at Files:Android/media/net.pregi.netmesh2/NetMesh")
.setContentIntent(activityPendingIntent)
.build()
notificationManager.notify(1, notification)
}
Note that for android 9 and below, I only managed to open the folder, but couldn't manage to open the file inside it. Still looking for an answer to this. For reference, here are the links that I've found related to this matter. Still open for suggestions.
Android 11 ACTION_OPEN_DOCUMENT_TREE
How to open specific folder in the storage using intent in android
As the title says, i upgraded to API 31. I had a function to perform a vibration, but in the line
val vib = this.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
the VIBRATOR_SERVICE is now shown as deprecated. How can i replace it? Or at least, what's the modern solution for API 31 and above?
EDIT: as Joachim Sauer wrote, the alternative is VibrationManager. What i need now is the equivalent line of code using VibrationManager.
val vib = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager =
getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
#Suppress("DEPRECATION")
getSystemService(VIBRATOR_SERVICE) as Vibrator
}
The docs for this field say this:
This constant was deprecated in API level 31.
Use VibratorManager to retrieve the default system vibrator.
The most direct translation of code needing a Vibrator instance would be this:
val vibratorManager = this.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
val vibrator = vibratorManager.getDefaultVibrator();
Generally speaking whenever a class/method/field is deprecated like this then you should first check the documentation. Almost every single time it will tell you what to use instead (or in some cases that it has no replacement).
This code works for both old and new android devices. Reference to the docs Vibrate constantly for the specified period of time.. You should use a VibrationEffect instead to create the vibration pattern.
In Java:
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
final int DELAY = 0, VIBRATE = 1000, SLEEP = 1000, START = 0;
long[] vibratePattern = {DELAY, VIBRATE, SLEEP};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(vibratePattern, START));
} else {
// backward compatibility for Android API < 26
// noinspection deprecation
vibrator.vibrate(vibratePattern, START);
}
In Kotlin:
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
val DELAY = 0
val VIBRATE = 1000
val SLEEP = 1000
val START = 0
val vibratePattern = longArrayOf(DELAY.toLong(), VIBRATE.toLong(), SLEEP.toLong())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(vibratePattern, START))
} else {
// backward compatibility for Android API < 26
// noinspection deprecation
vibrator.vibrate(vibratePattern, START)
}
Edit
This method works for API level 30 below properly, so to completely use this on API level 31 above you need to use VIBRATOR_MANAGER_SERVICE instead of VIBRATOR_SERVICE, to retrieve the default vibrator service.
The correct code is below (in Java) :
Vibrator vibrator;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
VibratorManager vibratorManager = (VibratorManager) getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
vibrator = vibratorManager.getDefaultVibrator();
} else {
// backward compatibility for Android API < 31,
// VibratorManager was only added on API level 31 release.
// noinspection deprecation
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
}
final int DELAY = 0, VIBRATE = 1000, SLEEP = 1000, START = 0;
long[] vibratePattern = {DELAY, VIBRATE, SLEEP};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(vibratePattern, START));
} else {
// backward compatibility for Android API < 26
// noinspection deprecation
vibrator.vibrate(vibratePattern, START);
}
The correct code is below (in Kotlin) :
val vibrator: Vibrator
vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager: VibratorManager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.getDefaultVibrator()
} else {
// backward compatibility for Android API < 31,
// VibratorManager was only added on API level 31 release.
// noinspection deprecation
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
val DELAY = 0
val VIBRATE = 1000
val SLEEP = 1000
val START = 0
val vibratePattern = longArrayOf(DELAY.toLong(), VIBRATE.toLong(), SLEEP.toLong())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(vibratePattern, START))
} else {
// backward compatibility for Android API < 26
// noinspection deprecation
vibrator.vibrate(vibratePattern, START)
}
Pulled together the various answers and cleaned them up to take into account changes in SDK 31 and 26, while providing backward compatibility.
#SuppressWarnings("deprecation")
private void vibrate() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
VibratorManager vibratorManager = (VibratorManager) getContext().getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
Vibrator vibrator = vibratorManager.getDefaultVibrator();
vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
}
else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
// API < 26
Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(500);
}
}
Handle SDK < 26, 26..32 and >= 33
private val vibrator: Vibrator by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
(getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).defaultVibrator
} else {
#Suppress("DEPRECATION")
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
}
#SuppressLint("MissingPermission")
private fun startVibrator() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
vibrator.vibrate(
VibrationEffect.createOneShot(1000, VibrationEffect.DEFAULT_AMPLITUDE),
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM)
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
#Suppress("DEPRECATION")
vibrator.vibrate(
VibrationEffect.createOneShot(1000, VibrationEffect.DEFAULT_AMPLITUDE),
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
)
} else {
#Suppress("DEPRECATION")
vibrator.vibrate(1000)
}
}
I created a wrapper class to handle the compatibility issue:
class VibratorHelper private constructor(private val context: Context) {
#Suppress("DEPRECATION")
fun vibrate(duration: Long) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator.run {
cancel()
vibrate(VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE))
}
} else {
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibrator.cancel()
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(duration)
}
}
}
companion object {
#JvmStatic
fun from(context: Context): VibratorHelper? {
val hasVibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator.hasVibrator()
} else {
#Suppress("DEPRECATION")
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibrator.hasVibrator()
}
return if (hasVibrator) VibratorHelper(context.applicationContext) else null
}
}
}
Here's how to use it:
val vibrator = VibratorHelper.from(context)
vibrator?.vibrate(500)
this is simple answer for both old and new api
Give permission for vibration
<uses-permission android:name="android.permission.VIBRATE" />
After that use this code for kotlin
#Suppress("DEPRECATION")
private fun vibrate(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator
vibrator.vibrate(10)
}
}
after that just call the method
This is what I use in my app (Kotlin). It handles all the old versions and hides the deprecated warnings. It does one short vibrate.
fun AppCompatActivity.vibrate() {
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
#Suppress("DEPRECATION")
getSystemService(AppCompatActivity.VIBRATOR_SERVICE) as Vibrator
}
val duration = 200L
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
#Suppress("DEPRECATION")
vibrator.vibrate(duration)
}
}
I need something really simple - I have an mp4 file that my app recorded and I want to insert it to the MediaStore to the user's video collection. Here is my code, including the recommended ways of dealing with the new Scoped Storage:
// fileToExpose is in internal storage with a name like video_1.mp4
fun copyVideoFileToMediaStore(fileToExpose: File, context: Context): Boolean {
val resolver = context.contentResolver
val volume = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) MediaStore.VOLUME_EXTERNAL_PRIMARY else MediaStore.VOLUME_EXTERNAL
val videoCollection = MediaStore.Video.Media.getContentUri(volume)
val videoDetails = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, fileToExpose.name)
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.TITLE, name)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Video.Media.IS_PENDING, 1)
}
}
val videoContentUri = resolver.insert(videoCollection, videoDetails) ?: return false
resolver.openFileDescriptor(videoContentUri, "w", null).use { pfd ->
pfd ?: return false
fileToExpose.inputStream().use { input ->
FileOutputStream(pfd.fileDescriptor).use { output ->
input.copyTo(output)
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
videoDetails.clear()
videoDetails.put(MediaStore.Video.Media.IS_PENDING, 0)
resolver.update(videoContentUri, videoDetails, null, null)
}
return true
}
However, after this function call the file with name video_1.mp4 becomes something like 1589077991588.3gp, so both the name and the extension are different (my device is Android 9). MediaStore.MediaColumns.DATA is deprecated. How can I fix this?
Late to the party, but just stumbled accross this issue and was looking for an answer here. And while trying around, I think I found a solution: Updating the MediaStore.Video.Media.DISPLAY_NAME after inserting the video data has done the trick (on Android 8, i.e.).
So while the above code worked for Android 10 and above, for Android 9 and below I've added to update the title after writing the content to the stream.
// [...] example as above, here we are after the `resolver.openFileDescriptor` section
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
videoDetails.clear()
videoDetails.put(MediaStore.Video.Media.IS_PENDING, 0)
resolver.update(videoContentUri, videoDetails, null, null)
} else {
// On Android <= 9 we need to update the display name after writing the data to ensure the DISPLAY_NAME is correctly set
videoDetails.clear()
videoDetails.put(MediaStore.Video.Media.DISPLAY_NAME, fileToExpose.name)
resolver.update(videoContentUri, videoDetails, null, null)
}
So it seems that on Android < 10 devices the display-name and title get overwritten when inserting the data via the contentResolver.openOutputStream.
I'm writing app that support two language and I'm changing language with Change app Locale here my code :
Locale locale = new Locale("fa");
Locale.setDefault(locale);
Configuration configs = new Configuration();
configs.locale = locale;
getBaseContext().getResources().updateConfiguration(configs, getBaseContext().getResources().getDisplayMetrics());
And
in manifest I'm set android:supportsRtl="true"
These codes working in many Devices but in some devices not working . for example Texts not Translating but Direction change .
Tested Devices :
Samsung J5Pro 2018 (android = 7.1): Worked
Pixel 2 API 26 : Worked
Samsung J7 2017 (android = 7): Worked
Nexus 5 (android = 6) : Not Worked
Samsung Galaxy G531 (android < lollipop) : Not Worked
i have found my solution , my problem was i'm inserted "fa" in Locale and my String Values Directory name was values-fa-rlIR, so names are different so not worked ,i'm wondering why it's working on some devices!
I'm just change the String Values Directory name from values-fa-rlIR to values-fa and it's working well.
had the same problem.
Android Studio names the language folder like values-xx-rXX. Android 6.0 and lower can't handle this so You have to rename the foldername (I did it directly in Windows Explorer) to values-de.
Now it works for me on all devices
or try this method , i used in many apps and it works
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
fun fixUpLocale(ctx: Context, newLocale: Locale) {
val res = ctx.resources
val config = res.configuration
val curLocale = getLocale(config)
if (curLocale != newLocale) {
Locale.setDefault(newLocale)
val conf = Configuration(config)
conf.setLocale(newLocale)
res.updateConfiguration(conf, res.displayMetrics);
}
}
fun getLocale(config: Configuration): Locale {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return config.locales[0]
} else {
//noinspection deprecation
return config.locale;
}
}
fun changeLang(context: Context, lang_code: String): ContextWrapper {
defaultLanguage = lang_code
var context = context
val sysLocale: Locale
val rs = context.resources
val config = rs.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
sysLocale = config.locales.get(0)
} else {
sysLocale = config.locale
}
if (lang_code != "" && !sysLocale.language.equals(lang_code)) {
val locale = Locale(lang_code)
Locale.setDefault(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(locale)
} else {
config.locale = locale
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
context = context.createConfigurationContext(config)
} else {
context.resources.updateConfiguration(config, context.resources.displayMetrics)
}
}
return ContextWrapper(context)
}
put this in every activity that you want to change the language
override fun attachBaseContext(newBase: Context) {
val lang_code = Shared.defaultLanguage //load it from SharedPref
val context = Shared.changeLang(newBase, lang_code)
super.attachBaseContext(context)
}
you can try this code , it's in Kotlin , and for more information you can check Android N change language programmatically
i think that it's an API matter.