In Android, I remember that it was mandatory to request permission(s) to use the camera or to access the Internet in an app.
But I did a small test in which I did not request any of the above permissions and I expected my test app to crash and burn.
But this did not happen!!
I was able to use the camera and access the Internet without requesting for permissions, and I've tested on 3 devices, all with different versions of Android.
Here is the code:
public class MainActivity extends Activity implements View.OnClickListener
{
private int cameraCode = 0;
private Button start_cam;
private Button start_internet;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start_cam = (Button) findViewById(R.id.camera);
start_internet = (Button) findViewById(R.id.internet);
start_cam.setOnClickListener(this);
start_internet.setOnClickListener(this);
}
#Override
public void onClick(View v)
{
switch(v.getId())
{
case R.id.camera:
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, cameraCode);
break;
case R.id.internet:
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.imdb.com"));
startActivity(browserIntent);
break;
}
}
}
the manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.permissions.linux.android">
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="16" />
<application
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name">
<activity
android:name="com.permissions.linux.androi.android.MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The question is, why didn't it crash?
I was able to use the camera and access the Internet without requesting for permissions
No, you were not. You were able to ask other applications "to use the camera and access the Internet" on your behalf. Your application did not directly use the camera, and your application did not directly access the Internet. The other applications that you linked to will need the CAMERA and INTERNET permissions to do their jobs. While sometimes you may need to hold a certain permission even to get a third-party app to do something for you, that is not needed to take a picture or view a Web page.
Related
Today I got a rejection from Google for my app Facetocall
Your app does not appear to prompt the user to be a default handler prior to requesting related permissions as required by the policy.
Please make necessary changes in order to comply with policy
requirements and resubmit your app through a Declaration Form.
Default handler capability was listed on your declaration form, but your app has no default handler capability.
My goal is to make a default dialer app.
Here is my Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.gazman.beep"
android:installLocation="preferExternal">
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.SEND_SMS" />
... and other permissions
<application
android:name=".application.BeepApp"
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".system_intents.IntentsActivity"
android:launchMode="singleTask"
android:noHistory="true"
android:theme="#style/Theme.Transparent">
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
</activity>
<activity
android:name=".call.CallActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:showForAllUsers="true" />
<service
android:name="com.gazman.beep.call.MyInCallService"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true" />
<intent-filter>
<action android:name="android.telecom.InCallService" />
</intent-filter>
</service>
... And other declarations
</application>
</manifest>
And here is what I do when my app launches:
private void checkDefaultHandler() {
if (isAlreadyDefaultDialer()) {
return;
}
Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, getPackageName());
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER);
}
else{
throw new RuntimeException("Default phone functionality not found");
}
}
private boolean isAlreadyDefaultDialer() {
TelecomManager telecomManager = (TelecomManager) getSystemService(TELECOM_SERVICE);
return getPackageName().equals(telecomManager.getDefaultDialerPackage());
}
What am I missing here?
I tried submitting the form again and this time I add a video that shows my app on an emulator(I see the same behavior on all the real devices too) here is the reply that I got back:
Your app does not appear to prompt the user to be a default handler prior to requesting related permissions as required by the policy.
Please make necessary changes in order to comply with policy
requirements and resubmit your app through a Declaration Form.
to make default dialer app, you need to do 2 things :
1. add the following permissions in your android manifest
<activity>
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
actually performing the check :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_layout)
...
checkDefaultDialer()
...
}
const val REQUEST_CODE_SET_DEFAULT_DIALER=200
private fun checkDefaultDialer() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
val isAlreadyDefaultDialer = packageName == telecomManager.defaultDialerPackage
if (isAlreadyDefaultDialer)
return
val intent = Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName)
startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CODE_SET_DEFAULT_DIALER -> checkSetDefaultDialerResult(resultCode)
}
}
private fun checkSetDefaultDialerResult(resultCode: Int) {
val message = when (resultCode) {
RESULT_OK -> "User accepted request to become default dialer"
RESULT_CANCELED -> "User declined request to become default dialer"
else -> "Unexpected result code $resultCode"
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
You don't need to panic. This kind of conversation happened with me also, regarding AdMob content on my app. I declared everything perfectly but still they were saying content rating not fine due to type of Ads my app was showing. When more mails exchanged they sent me screenshot with the proof of wrong Ads, so finally i checked my whole code again and found my mistake.
The point here is that Google is good at what they do, and if they said so, then your app lacks something.
To be very honest, your app did not ask the user anywhere to allow it to be set as default, instead it set itself default in the background. You should ask for every permission required by your app that are critical and can be used by any app or virus or spyware to interfere with user privacy.
You can do that with a function like in the following example, which is asking for Camera permission from the user:
private void requestCameraPermission() {
Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission.");
// BEGIN_INCLUDE(camera_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
// For example if the user has previously denied the permission.
Log.i(TAG,
"Displaying camera permission rationale to provide additional context.");
Snackbar.make(mLayout, R.string.permission_camera_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
})
.show();
} else {
// Camera permission has not been granted yet. Request it directly.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// END_INCLUDE(camera_permission_request)
}
You can see the complete repository at Google Samples
And don't worry. If you rectify this problem, they will accept your application, as they did for mine.
in case anyone comes cross this post . . .
I used this for asking the user for changing the default dailer.
Know that the there gonna be 2 windows prompting(for me it was fine).
private void setDefaultDialer()
{
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(this);
builder.setMessage("Do you want to make Cricket your default Dialer?(it will not cover or replace your dialer)")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
defaultDialerPackage = "cricket";
Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
startActivityForResult(intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,getPackageName()),REQUEST_CODE_SET_DEFAULT_DIALER);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
Toast.makeText(getApplicationContext(),"Cancelled - No action was taken",
Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = builder.create();
alert.setTitle("Cricket need default dialer permission!!");
alert.show();
}
I'm using firebase auth in my app. I'm checking if the user is authenticated on my MainActivity just as explained on the Firebase docs and the Udacity course.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebaseAuth = FirebaseAuth.getInstance();
final FirebaseUser user = firebaseAuth.getCurrentUser();
if (user == null){
loadLogIn();
}
authListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if (user == null){
loadLogIn();
}
}
};
private void loadLogIn(){
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
//Flags prevent user from returning to MainActivity when pressing back button
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
When I step through the code with the debugger, everything works well, the user is null so the loadLogIn method is called, which sends the intent, but Login Activity never opens, and I get a crash because it keeps executing code that needs the user to be authenticated. The onCreate method if my login activity is never called. I cannot understand why the intent is not executing/ what is stopping it. This had been tested and worked perfectly previously.
When the intent line is executed I get the following output on the console :
I/Timeline: Timeline: Activity_launch_request id:com.example.tobias.run time:2369840341
This is my manifest if it helps :
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:name=".app.RunApplication"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".app.MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".editor.EditorActivity"
android:parentActivityName=".app.MainActivity" />
<activity
android:name=".login.LoginActivity"
android:screenOrientation="portrait"
android:theme="#style/LoginTheme" />
<activity
android:name=".settings.SettingsActivity"
android:parentActivityName=".app.MainActivity" />
<activity
android:name=".login.NewAccountActivity"
android:screenOrientation="portrait"
android:theme="#style/LoginTheme" />
<activity android:name=".login.ForgotPasswordActivity"
android:screenOrientation="portrait"
android:theme="#style/LoginTheme"/>
</application>
This issue first appeared when I tried to sign out from my app calling
FirebaseAuth.getInstance().signOut();
the user signed out, but the intent was not sent, which left the app in an invalid state, and then it crashed seconds after trying to execute code that required a user. It was not the first time I had signed out of the app, I have done it multiple times and it worked.
This is a weird issue, I hope someone can help me find the mistake. Thanks in advance.
Problem:
//...
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
//...
Solution:
//...
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
//...
When you use setFlags you are replacing the old flags... when you use addFlags you are appending new flags. Remember, a flag is just a integer which is power of two... in binary, flags look like this: 1, 10, 100, 1000, etc... (which in this case are 1, 2, 4, 8). So, what addFlags does is appending the integer you pass using the | operator.
It's a specific question and I've done a bit of Android development before, but not so deep into the system management.
So I need to create an app which run in background (this part is OK) and to launch automatically an activity of the app when a special shortcut (let's say #123*6) is typed from the phone app software keyboard on the phone.
Can you please indicate me if it's possible, and if yes, what API/Component I should use? Can't find something relevant on the Web.
Ok I finally got this working on the HTC device. The fact is Samsung phones doesn't seems to react to secret codes ...
I simply have this manifest :
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MenuBrowser"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ShortcodeService"
android:exported="false">
</service>
<receiver
android:name=".ShortcodeReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.provider.Telephony.SECRET_CODE" />
<data android:scheme="android_secret_code" android:host="1992" />
</intent-filter>
</receiver>
</application>
My service is simply checking for the right permissions for Android M (6.0), because the security has changed a bit. We now have to declare permission on-the-fly during the application runtime, and not at installation.
My activity:
public class MenuBrowser extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("USSDBrowser", "Start app");
//if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.PROCESS_OUTGOING_CALLS}, 10);
Intent serviceIntent = new Intent(this.getApplicationContext(), ShortcodeService.class);
startService(serviceIntent);
//setContentView(R.layout.activity_menu_browser);
finish();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_menu_browser, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
And my receiver is like this :
public class ShortcodeReceiver extends BroadcastReceiver {
private static String defaultCode = "1992";
#Override
public void onReceive(Context context, Intent intent) {
Log.d("USSDBrowser", "Intent received");
if(intent.getAction().equals("android.provider.Telephony.SECRET_CODE")) {
String code = intent.getDataString();
if(code.equals("android_secret_code://" + defaultCode)) {
Log.d("USSDBrowser", "Code received !!! ");
}
//Intent in = new Intent(context, MenuBrowser.class);
//context.startActivity(in);
Toast.makeText(context, "You typed a shortcode, hype !", Toast.LENGTH_LONG).show();
}
}
}
But now I tested that and it's working for HTC One S (Android 4.1.1) and Aquaris E4 (Android 4.4.2).
The phones tested that does not capture secret code intents are : Samsung Galaxy S4 and Galaxy S6 (Android 6.0).
I need to make an application "x" that has only service in it(no activity and no broadcast receiver) which can be called from activity of another application "y". I need to ask is it possible because on clicking a button in application "y" service of application "x" must be called but it is not happening. I don't see service running in the android phone. So is it even possible to have a single service in a project and nothing else?
application "y" code
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button = (Button) findViewById(R.id.button);
if(button!=null){
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startService(new Intent("com.example.sonali.serviceonly.OnlyService"));
Toast.makeText(getApplicationContext(), "text", Toast.LENGTH_SHORT).show();// Perform action on click
}
});
}
}
}
application "x" manifest file
``
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<service android:name="com.example.sonali.serviceonly.OnlyService" android:exported="true" android:enabled="true">
<intent-filter>
<action android:name="com.example.sonali.serviceonly.OnlyService"/>
</intent-filter>
</service>
</application>
``
Yes it's possible.
You have to start service like this.
Intent intentService = new Intent();
intentService.setComponent(new ComponentName("com.example.app", "com.example.app.servicename"));
startService(intentService);
You must have to set this exported as true in menifest file.
android:exported="true"
I have tested it's working fine on 4.4.4 Version.
Try Explicit Intent for version 5+
ComponentName n = new ComponentName("com.xxx.yyy", "com.xxx.yyy.OnlyService");
Intent a = new Intent();
a.setComponent(n);
a.setAction(Intent.ACTION_MAIN);
a.addCategory(Intent.CATEGORY_LAUNCHER);
a.putExtra("destinationAddress","5554");
ComponentName c = getApplication().startService(a);
if(c==null)
{
Log.e("error", "failed to start with " + a);
}
My application created files with a custom Mime type and stores them on Google Drive. The app can search and reopen these files just fine too. However, when I click the file in the Google Drive app (not my own app) the open flow does not work.
The Chooser Intent shows as expected with just my application listed, but when I select my application the Google Drive app briefly shows a downloading progress bar that never starts and then says there is an internal error.
My setup is below, I'm hoping somebody can tell me what causes this. although I would assume nothing is actually contacting my app by this point. The developer console has been filled in correctly as far as I know.
Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="...">
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:icon="#drawable/logo" android:label="#string/app_name"
android:allowBackup="true" android:theme="#style/AppTheme">
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
<activity android:name=".MainActivity" android:label="#string/app_name"
android:launchMode="singleTop" android:theme="#style/AppTheme">
<meta-data android:name="com.google.android.apps.drive.APP_ID" android:value="id=..." />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.apps.drive.DRIVE_OPEN" />
<data android:mimeType="#string/app_mime" />
<data android:mimeType="#string/file_mime" /> <!-- matches the file im clicking -->
</intent-filter>
</activity>
</application>
Activity
public class MainActivity extends Activity {
private static final String ACTION_DRIVE_OPEN = "com.google.android.apps.drive.DRIVE_OPEN";
private static final String EXTRA_DRIVE_OPEN_ID = "resourceId";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
#Override
protected void onResume() {
super.onResume();
handleIntent();
}
private void handleIntent() {
Intent intent = getIntent();
if (ACTION_DRIVE_OPEN.equals(intent.getAction())) {
if (intent.getType().equals(getString(R.string.file_mime))) {
String fileId = intent.getStringExtra(EXTRA_DRIVE_OPEN_ID);
if (fileId != null && !"".equals(fileId)) {
...
} else {
Log.e(TAG, "Drive_Open has no valid file id - " + fileId);
}
} else {
Log.e(TAG, "Drive_Open called on the wrong mime type - " + intent.getType() + " found, " + getString(R.string.file_mime) + " required");
}
}
}
After some testing this seems to happen after there is a timeout between the drive app connecting to the server to retrieve information and passing it to the other app (or any other fault).
The initial problem will give you a meaningful error but then the drive app seems to maintain a cache or something which cases the above error to be displayed on all future attempts.
The solution is to either clear the data of both apps (which can be problematic) or (I found easier) re-install the app you are developing/testing. Either method stops this problem reoccurring.