I am trying to create an app that, upon launching, will lock the phone. (It acts as an alternative of pressing the physical lock button on the phone instead).
I was able to achieve it with this code:
MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LockmeTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
val deviceManger =
getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val compName = ComponentName(this, AdminReceiver::class.java)
val active: Boolean = deviceManger.isAdminActive(compName)
if (!active) {
val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, compName)
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "You should enable the app!")
startActivity(intent)
}else{
deviceManger.lockNow()
}
}
}
policies.xml
<?xml version="1.0" encoding="utf-8"?>
<device-admin>
<uses-policies>
<force-lock />
</uses-policies>
</device-admin>
AndroidManifest.xml
<receiver
android:name= ".AdminReceiver"
android:description= "#string/app_name"
android:label= "#string/app_name"
android:exported="false"
android:permission= "android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name= "android.app.device_admin"
android:resource= "#xml/policies" />
<intent-filter>
<action android:name= "android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
The issue here is that, after locking the phone, when I try to unlock it, it says Device is locked by admin and it needs strong authentication such as Pin / Password to unlock. I want it to use the non-strong authentication that was setup on the phone such as fingerprint or face unlock.
Any idea how i can achieve this ?
Acording to Android documentation, you need either admin previlages or LOCK_DEVICE permission.
With admin previlages, as mentioned the unlock will have to be done using pattern or pin.
LOCK_DEVICE permission is not available for 3rd party apps.
Looks like there is an option to acheive this if you are building a kiosk app. I havent explored more on this.
Hope this helps.
Related
I want to show a floating view when the user gets a phone call or a text message. But overlay permission check in broadcastReceiver returns false with built apk on Android 13.
I use BroadcastReceiver with RxWorker (more than 12) and Service (less than 12) to get a caller information. In Worker or Service, when you success to get a information, you call WindowManager.addview(mView). When the app is first launched, the user has already granted the permission to draw overlays.
<receiver
android:name=".receiver.PhoneCallReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
In this receiver, I check SYSTEM_ALERT_WINDOW permission by using Settings.canDrawOverlays(context).
When I run this in Android Studio, it works well. However, permission is not checked when built with apk only on Android 13. Returns false even though you have permission. Sometimes, an error occurs when calling addView within Worker even if the permission is correctly confirmed on the broadcast!
"android.view.WindowManager$BadTokenException: Unable to add window
android.view.ViewRootImpl$W#7c33a48 -- permission denied for window
type 2038"
<receiver
android:name=".receiver.MessageReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
When receiving a phone call or text message, WindowManager.addView is called within the same worker, but the view is normally displayed when receiving a text message, and there is no permission when receiving a phone call. For your information, I'm testing with Samsung Galaxy S21.
Is there any solution?
Asking permission when a call is received harms the user experience, but I tested it with reference to Fazle Rabbi's answer. Since the context within the broadcast is not an activity context, a separate activity was created to request permission. (The code has been modified. Check to allow permissions with Settings.canDrawOverlays() instead of comparing with result.resultCode.)
#RequiresApi(Build.VERSION_CODES.R)
class PermissionActivity : AppCompatActivity() {
private val requestOverlayPermission = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (Settings.canDrawOverlays(this)) {
//Permission granted
/*
do something
*/
} else {
//Permission denied
}
finish()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pemission)
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
intent.data = Uri.parse("package:$packageName")
requestOverlayPermission.launch(intent)
}
}
In BroadcastReceiver,
if (!Settings.canDrawOverlays(ctx)) {
val i = Intent(ctx,PermissionActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
return
}
Settings.canDrawOverlays returns false and goes to the Permission Request page whenever BroadcastReceiver is called, even if the app's settings confirm that it has overlay privileges. In addition, permissions are still disabled on the moved overlay permission request page.
It's really weird.
Funny how it's normal when it's running on Android studio. It is well confirmed that there is also permission. This only happens when executed with the built APK On Android 13! The problem seems to occur not only on one particular device, but on most Samsung phones updated with Android 13.
Did I miss anything?
It's an issue only with Android 13 Samsung phones.
I also contacted Samsung about the issue. And they replied:
they have SamsungRestrictOverlayProcessor. and Overlay-related permissions are temporarily disabled during calls for applications that have not been installed through an official store (Play store / Galaxy store) or ADB
so you need to distribute and test your application through proper stores.
I hope this was helpful to you.
is it possible to add queries in runtime? I know it is possible to change configurations and permissions but e.g if I want my app to interact with data of any app on my phone which is possible by having the packagename. But after API 30 I need to specify Apps I want to interact with as queries in the AndroidManifest.xml, or use the QUERY_ALL_PACKAGES permission which I don't want to use.
Here is an Example how I imagined it:
User enters my app and wants the icon of a specific app on his phone. Then enters the package name and clicks ok and now he can see the icon of a certain app.
is it possible to add queries in runtime?
You appear to be referring to <queries> elements in the manifest. If so, then no, that is not possible.
But after API 30 I need to specify Apps I want to interact with as queries in the AndroidManifest.xml, or use the QUERY_ALL_PACKAGES permission which I don't want to use
Or, you specify Intent patterns of relevance in <queries>.
User enters my app and wants the icon of a specific app on his phone. Then enters the package name and clicks ok and now he can see the icon of a certain app.
First, approximately 0% of your users know package names. A more user-friendly UI would be to present the member with a list to choose from.
Second, it is unclear what you mean by "the icon of a certain app". If you mean "a launcher icon from a certain app", then basically you want similar capabilities to a launcher: show a list (or grid or whatever) of launcher icons for the user to choose from, after which you can do something with the chosen icon.
For that scenario, this should suffice:
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries>
You can then use queryIntentActivities() to find the launcher activities on the device:
private val LAUNCHER_INTENT =
Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER)
data class MainViewState(
val launcherActivities: List<ResolveInfo>
)
class MainMotor(private val context: Context) : ViewModel() {
private val _states = MutableLiveData<MainViewState>()
val states: LiveData<MainViewState> = _states
fun load(force: Boolean = false) {
if (force || _states.value == null) {
val pm = context.packageManager
_states.value = MainViewState(
launcherActivities = pm.queryIntentActivities(LAUNCHER_INTENT, 0)
)
}
}
}
Hmh... read a bit more about Androidmanifest.xmls and Queries and found out the App i planned to do is exactly sth Google doesnt want to have so....
But still thanks for the answers ^^
I started to update our wear module to be up to date with latest guidelines of Google Play.
Since I'm doing so, I decided to add the Tiles API to the app. When ready for public release it will already be implemented.
Doc: https://developer.android.com/training/articles/wear-tiles#preview
Very unclear... Does anybody understand this?
The wear-tiles-renderer library provides a way to preview Tiles in an activity within your app.
To preview your Tile, create an activity that uses the renderer library to render the Tile. Add this activity in src/debug instead of src/main, as you’ll use this activity only for debugging purposes. See the following code sample for an example:
I tried adding the example code to the debug folder manually since I can't add it from Android Studio.
Added the xml file in the main and also added in debug folder to test.
When I load the app it opens my main file of folder src/main but freezes.
Do I need to add any code to load the example if in debug?
In Android Studio Dolphin you can now create a Launch configuration without the preview
What the quote from the documentation is trying to explain is that you need a module that looks something like this:
Your actual tile, and everything it needs, goes in the main folder. Then in the debug folder you need the following three things:
Manifest - Pretty standard
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tiles">
<uses-feature android:name="android.hardware.type.watch" />
<application
android:label="#string/tiles_app_name">
<activity
android:name=".MainActivity"
android:label="#string/tiles_app_name"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Activity - The important thing here is the TileManager
class MainActivity : ComponentActivity() {
private lateinit var tileManager: TileManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.tile_test)
val rootLayout = findViewById<FrameLayout>(R.id.tile_container)
tileManager = TileManager(
context = this,
component = ComponentName(this, SampleTile::class.java),
parentView = rootLayout
)
tileManager.create()
}
override fun onDestroy() {
super.onDestroy()
tileManager.close()
}
}
Layout - Also pretty standard
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tile_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
If you have all of this set up and it's still freezing, you probably have a bug in your Tile class. See if there are any error messages in Logcat that can help you understand what is going wrong.
If you split your TileService and Tile layout code using Horologist
You can use Android Studio previews for this
#WearSmallRoundDevicePreview
#WearLargeRoundDevicePreview
#Composable
fun Run() {
val context = LocalContext.current
LayoutRootPreview(
Run.layout(
context,
context.deviceParams(),
lastRunText = "2 days ago",
startRunClickable = emptyClickable,
moreChipClickable = emptyClickable
)
)
}
See the example here https://github.com/android/wear-os-samples/blob/main/WearTilesKotlin/app/src/debug/java/com/example/wear/tiles/golden/GoldenTilesPreviewsRow1.kt
I'm using a TileService as a shortcut to open my app's Activity. It does only that, and has no state.
It looks like this:
class QuickAccessTileService : TileService() {
override fun onClick() {
super.onClick()
val intent = Intent(this, SlideOverActivity::class.java)
.addFlags(FLAG_ACTIVITY_NEW_TASK)
startActivityAndCollapse(intent)
}
}
and it's declared in AndroidManifest as:
<service
android:name=".service.QuickAccessTileService"
android:icon="#drawable/ic_home"
android:label="#string/tile_label"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
As it stands, this works. However, when my application is stopped, the next time I try to open it via the quick settings panel, it takes several seconds to start the activity.
Here's what I know:
The Activity in itself isn't slow to start. Trying to open it via the launcher instead makes it quite clear.
The Service seems to be what's taking a while to start before onClick is even executed. It makes sense; the Service is probably not kept running in the background doing nothing while the app isn't running. However, this means that when the system detects a click on my Tile, the Service has to be recreated first, which takes way too long.
I'm not sure where to go from here — if my guesses are even right.
EDIT: As an important addition, I can reproduce this on a OnePlus 7 Pro running on Android Pie. This might be a OnePlus-specific issue as I cannot reproduce it on an emulator.
#RequiresApi(Build.VERSION_CODES.N)
class TileSettingClass : TileService() {
override fun onClick() {
super.onClick()
val tile = qsTile
if (tile.state == Tile.STATE_INACTIVE) {
tile.state = Tile.STATE_ACTIVE
val intent = Intent(this.applicationContext, YourActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.action = "yourAction"
applicationContext.startActivity(intent)
} else {
tile.state = Tile.STATE_INACTIVE
}
tile.updateTile()
}
}
Manifests
<service
android:name=".main.service.TileSettingClass"
android:icon="#drawable/ic_tile_setting"
android:label="#string/milliy"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
I have an application. I want it to appear when I tap "share" in other applications. Then I want it to process the data it has received and go back to the caller application.
E.g. I am sharing a facebook post:
1) My app opens
2) Saves info to DB
3) Returns to FB post
How can this return to FB post be achieved?
I tried referring to callingActivity, but it is null.
Intent filter:
<intent-filter android:label="#string/app_name">
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="*/*"/>
</intent-filter>
Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = intent
val action = intent.action
val type = intent.type
if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
// do some processing
// goes recursively
startActivity(intent)
}
}
Whatever app you shared from will be active behind your current application so you will be able to share to your app and then just stop your app using finish(). I did a quick test and was able to share from a couple of apps using this approach. Your app will flash up then the user will land back in the app they shared from after your database write is complete. I logged the intent to make sure it was coming through properly and was able to see links from chrome (as an example).
if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
// do some processing
Log.d("Extras", intent.getStringExtra(Intent.EXTRA_TEXT))
// goes recursively
finish()
}