How do I open the Account Settings for a specific Account? - android

I know that via the Account Manager I can access to the list of the accounts on the device.
Account[] accounts = AccountManager.get(this).getAccounts();
I know as well that using the android.settings.ACCOUNT_SYNC_SETTINGS I can open the Sync Settings for a specific account
Intent in=new Intent("android.settings.ACCOUNT_SYNC_SETTINGS");
in.putExtra("account", accounts[3]);
What I would like to do is to open the "Account Settings" for a specific account, like this:
And not like this:

General Approach:
The tricky part is that Android's definition of Account is pretty abstract, so depending on your use case you'll encounter accounts without these screens, or with multiple screens. These screens may not look like Preferences/Settings screens at all - they could be any Activity as defined by the Account authenticator.
Generally though, I think this is what you could do:
Use the getAuthenticatorTypes() method of AccountManager. This
will give you all of the AuthenticatorDescriptions. Depending on
your use case, you can pick the AuthenticatorDescription you care
about (based on the "type" value) and just further investigate that.
http://developer.android.com/reference/android/accounts/AccountManager.html#getAuthenticatorTypes()
The AuthenticatorDescription will tell you the
accountPreferencesId that defines the "hierarchy of
PreferenceScreen to be added to the settings page for the account".
Which in turn defines a "PreferenceScreen xml hierarchy that
contains a list of PreferenceScreens that can be invoked to manage
the authenticator".
http://developer.android.com/reference/android/accounts/AuthenticatorDescription.html
Use the accountPreferencesId and the package provided by the
AuthenticatorDescription to parse the Intents you need, allowing you
to navigate your user to them. Reference for what the XML will look
like:
http://developer.android.com/reference/android/accounts/AbstractAccountAuthenticator.html
tl;dr - the code:
Here's a method that finds the Activity action/class and starts it. It doesn't account for the fact that there may be multiple Activities defined in the Preferences XML referenced above. In fact, it doesn't account for a lot of error scenarios and can surely be optimized, but should serve as a decent proof of concept and starting point:
//somewhere in your app call this
//com.android.email works to open the email preferences on my Galaxy s4
startPreferenceActivity("com.android.email");
private void startPreferenceActivity(String type) {
AccountManager accountManager = AccountManager.get(this);
AuthenticatorDescription[] descriptions = accountManager.getAuthenticatorTypes();
AuthenticatorDescription neededDescription = null;
for (AuthenticatorDescription description : descriptions) {
if (description.type.contains(type)) {
neededDescription = description;
break;
}
}
if (neededDescription != null) {
String packageName = neededDescription.packageName;
int prefsId = neededDescription.accountPreferencesId;
try {
Resources resources = getPackageManager().getResourcesForApplication(packageName);
XmlResourceParser xpp = resources.getLayout(prefsId);
xpp.next();
String action = null;
String targetPackage = packageName; //default to the account pref package name...?
String targetClass = null;
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equals("intent")) {
int count = xpp.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = xpp.getAttributeName(i);
if (name.equals("action")) {
action = xpp.getAttributeValue(i);
} else if (name.equals("targetPackage")) {
targetPackage = xpp.getAttributeValue(i);
} else if (name.equals("targetClass")) {
targetClass = xpp.getAttributeValue(i);
}
}
}
}
eventType = xpp.next();
}
if (action != null) {
startActivity(new Intent(action));
} else if (targetClass != null) {
startActivity(new Intent(createPackageContext(targetPackage, Context.CONTEXT_IGNORE_SECURITY), Class.forName(targetClass)));
}
} catch (PackageManager.NameNotFoundException e) {
} catch (Exception e) {
}
}
}

You may have database of your accounts info u can open it and design a similar style form for it this can solve your problem

Related

DocumentsUI shows "Anonymous" application when requesting access to directory

One user reported that my app fails to request directory access when selecting a folder via the ACTION_OPEN_DOCUMENT_TREE intent.
For some reason it does not show my application, instead "Anonymous":
Translated: "Allow Anonymous to access files in Camera. This will let Anonymous access current and future content stored in Camera".
The user has a MIUI 12 with Android 11 on a Mi Note 10 lite.
I have the same just with a Mi Note 10, no issues ofc.
Checked the Android source code:
https://android.googlesource.com/platform/packages/apps/DocumentsUI/+/refs/heads/master/src/com/android/documentsui/picker/ConfirmFragment.java#82
case TYPE_OEPN_TREE:
final Uri treeUri = mTarget.getTreeDocumentUri();
final BaseActivity activity = (BaseActivity) getActivity();
final String target = activity.getCurrentTitle();
final String text = getString(R.string.open_tree_dialog_title,
**getCallingAppName**(getActivity()), target);
message = getString(R.string.open_tree_dialog_message,
**getCallingAppName**(getActivity()), target);
builder.setTitle(text);
builder.setMessage(message);
builder.setPositiveButton(
R.string.allow,
(DialogInterface dialog, int id) -> {
pickResult.increaseActionCount();
mActions.finishPicking(treeUri);
});
break;
#NonNull
public static String getCallingAppName(Activity activity) {
final String anonymous = activity.getString(R.string.anonymous_application);
final String packageName = getCallingPackageName(activity);
if (TextUtils.isEmpty(packageName)) {
return anonymous;
}
final PackageManager pm = activity.getPackageManager();
ApplicationInfo ai;
try {
ai = pm.getApplicationInfo(packageName, 0);
} catch (final PackageManager.NameNotFoundException e) {
return anonymous;
}
CharSequence result = pm.getApplicationLabel(ai);
return TextUtils.isEmpty(result) ? anonymous : result.toString();
}
public static String getCallingPackageName(Activity activity) {
String callingPackage = activity.getCallingPackage();
// System apps can set the calling package name using an extra.
try {
ApplicationInfo info =
activity.getPackageManager().getApplicationInfo(callingPackage, 0);
if (isSystemApp(info) || isUpdatedSystemApp(info)) {
final String extra = activity.getIntent().getStringExtra(
Intent.EXTRA_PACKAGE_NAME);
if (extra != null && !TextUtils.isEmpty(extra)) {
callingPackage = extra;
}
}
} catch (NameNotFoundException e) {
// Couldn't lookup calling package info. This isn't really
// gonna happen, given that we're getting the name of the
// calling package from trusty old Activity.getCallingPackage.
// For that reason, we ignore this exception.
}
return callingPackage;
}
...and it seems that for whatever reason my packagename isn't found. How can can happen?
Asked him to install one of my other apps, and it happens there as well.
Asked him then to install another app from the playstore (FX File Explorer) and there it does not happen.
So it is specific to his device and my app.
So it turned out that this user having that issue turned off the MIUI Optimizations in the developer settings.
Bug report: συσκευη, εκδοση miui, Play store install (alpha 1021). It was impossible to specify a b i o s file or specify a game image directory in when MIUI optimizations are off. Turning them back on fixed the issue and directories are scanned normally. Also on the popup to allow folder access the app displays as "Anonymous" instead of AetherSX2 on my system. Some developer was talking about having the same issue here.

Preventing an android app being cloned by an app cloner

Created an app that used the device's uniqueID which is fetched by the following code snippet
String deviceId = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ANDROID_ID);
When the user tries to clone the app by app cloner, then it creates a different deviceID and the app is not allowed to work
Is there any way to make our app non clonable
or
Any possible way to have the same deviceId even if the app instance is cloned?
Is there any way to find out whether the app is running in a cloned instance?
Applications like Cloner usually change your application's package name so you can retrieve package name and check if it is changed or not.
if (!context.getPackageName().equals("your.package.name")){
// close the app or do whatever
}
Also they usually sign cloned apk so the signature might be different from yours, you can check if signature is changed or not. I usually use this function:
#SuppressLint("PackageManagerGetSignatures")
public static int getCertificateValue(Context ctx){
try {
Signature[] signatures = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
signatures = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES).signingInfo.getApkContentsSigners();
}catch (Throwable ignored){}
}
if (signatures == null){
signatures = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
}
int value = 1;
for (Signature signature : signatures) {
value *= signature.hashCode();
}
return value;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static boolean checkCertificate(Context ctx, int trustedValue){
return getCertificateValue(ctx) == trustedValue;
}
Before releasing your app call getCertificateValue(context) and write down the value and alongside with package name, check if that value matches the value that you get in runtime.
PS: as #vladyslav-matviienko said hackers will always find a way so try to make cloning harder by running some obfuscations on hardcoded package name and that value. Also try to tangle and spread these kind of logics all around the source code.
I found a story in proandroiddev by Siddhant Panhalkar and with some minor changes it's work perfectly in Mi device I did checked in Mi phones default Dual apps and some third party apps from playstore and it prevents from cloning (means not working properly after clone).
private const val APP_PACKAGE_DOT_COUNT = 3 // number of dots present in package name
private const val DUAL_APP_ID_999 = "999"
private const val DOT = '.'
fun CheckAppCloning(activity: Activity) {
val path: String = activity.filesDir.getPath()
if (path.contains(DUAL_APP_ID_999)) {
killProcess(activity)
} else {
val count: Int = getDotCount(path)
if (count > APP_PACKAGE_DOT_COUNT) {
killProcess(activity)
}
}
}
private fun getDotCount(path: String): Int {
var count = 0
for (element in path) {
if (count > APP_PACKAGE_DOT_COUNT) {
break
}
if (element == DOT) {
count++
}
}
return count
}
private fun killProcess(context: Activity) {
context.finish()
android.os.Process.killProcess( android.os.Process.myPid())
}

How can I attach an image to a "Direct Message" in twitter Android?

I programmed an app that can send a message to twitter with an image attached. It works! I tested it on several devices and asked other people to do the same. It even works for a Direct Message when a twitter friend is selected. However, it does not work when "Direct Message" is selected. This forces the user to select a friend directly instead of selecting him via "Direct Message" (which is really strange) otherwise the picture is not attached. Just have a look at the screenshot:
Here is my Xamarin Android programming code. Let me know how to fix it. Currently, all options work, even selecting my friend but not "Direct Message". I also need to tell that I do not have any issue with the twitter text I expect to see in the tweet.
public bool TweetImage(Bitmap imageToTweet)
{
var messageIntent = context.FindMessageIntent(this.twitterConstants.PackageName);
if (messageIntent == null)
{
return false;
}
string outputFileBMP = SaveBitmap(imageToTweet);
context.Tweet(messageIntent, outputFileBMP, this.twitterConstants.DefaultTwitterText, this.twitterConstants.ChooserMessage);
return true;
}
and
public static Intent FindMessageIntent(this ContextWrapper contextWrapper, params string[] packageNames)
{
Intent wantedIntent = new Intent();
wantedIntent.SetType("text/plain");
var resolveInfos = contextWrapper.PackageManager.QueryIntentActivities(wantedIntent, PackageInfoFlags.MatchDefaultOnly);
var result = (from r in resolveInfos
from p in packageNames
where p == r.ActivityInfo.PackageName
select p).FirstOrDefault();
if (result != null)
{
wantedIntent.SetPackage(result);
return wantedIntent;
}
return null;
}
and
public static void Tweet(this ContextWrapper contextWrapper, Intent messageIntent, string filePath = null, string message = null, string chooserMessage = null)
{
if (filePath != null)
{
using (var file = new Java.IO.File(filePath))
{
messageIntent.PutExtra(Intent.ExtraStream, Android.Net.Uri.FromFile(file));
}
}
if (message != null)
{
messageIntent.PutExtra(Intent.ExtraText, message);
}
if (chooserMessage != null)
{
using (var chooser = Intent.CreateChooser(messageIntent, chooserMessage))
{
contextWrapper.StartActivity(chooser);
}
return;
}
contextWrapper.StartActivity(messageIntent);
}
Please note that I am using Android and need a solution based on Android (intent based).
Sadly, Twitter don't provide API access for uploading images via DM.
If you are able to use Twitter's private API, you should be able to attach a media_id to your DM. But other than that, you're out of luck.
Sorry.

Integrating and working with .pkpass passes in Xamarin Android app

I'm developing a Xamarin Android app and I need the ability to be able to work with Passes (PassKit passes for example (JSON)). I need to be able to list all the passes in a ListVew and be able to open and display the pass. Also be able to save them to a wallet such as PassWallet or Pass2u. I don't need the ability to create them, just view them, and save them to a wallet or discard them.
There seems to be an example Xamarin iOS app which does exactly what i need here but of course I need to be able to do this in Xamarin Android.
I've been researching this for hours but don't know how to achieve what i need. JSON.net seems the way to go to read the passes, but that's as far as I've managed to get. Some examples would be great. Can anybody help?
To add the pass into PassWallet you can use the following:
private static boolean launchPassWallet(Context applicationContext, Uri uri, boolean launchGooglePlay) {
if (null != applicationContext) {
PackageManager packageManager = applicationContext.getPackageManager();
if (null != packageManager) {
final String strPackageName = "com.attidomobile.passwallet";
Intent startIntent = new Intent();
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startIntent.setAction(Intent.ACTION_VIEW);
Intent passWalletLaunchIntent = packageManager
.getLaunchIntentForPackage(strPackageName);
if (null == passWalletLaunchIntent) {
// PassWallet isn't installed, open Google Play:
if (launchGooglePlay) {
String strReferrer = "";
try {
strReferrer = "&referrer=" + URLEncoder.encode(uri.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
strReferrer = "";
}
try {
startIntent.setData(Uri.parse("market://details?id=" + strPackageName + strReferrer));
applicationContext.startActivity(startIntent);
} catch (android.content.ActivityNotFoundException anfe) {
// Google Play not installed, open via website
startIntent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + strPackageName + strReferrer));
applicationContext.startActivity(startIntent);
}
}
} else {
final String strClassName = "com.attidomobile.passwallet.activity.TicketDetailActivity";
startIntent.setClassName(strPackageName, strClassName);
startIntent.addCategory(Intent.CATEGORY_BROWSABLE);
startIntent.setDataAndType(uri, "application/vnd.apple.pkpass");
applicationContext.startActivity(startIntent);
return true;
}
}
}
return false;
}
And an example call is:
launchPassWallet(getApplicationContext(),Uri.parse("http://test.attidomobile.com/PassWallet/Passes/AttidoMobile.pkpass"), true);
You can also use a file:// URL if you have the file locally.
To display them in the list, you'd need to unzip the .pkpass file and then parse the JSON for the relevant fields.

What is android:sharedUserLabel and what added value does it add on top of android:sharedUserID?

The documentation (http://developer.android.com/guide/topics/manifest/manifest-element.html#uid) only states I can't use raw strings and the API level it was added, but doesn't explain why I would want to use it.
If I already set android:sharedUserID to "com.foo.bar" what value should I put in the string referenced by android:sharedUserLabel, and most importantly why!?
Thank you
As far as I understand from the AOSP actually you can use this label just to display a pretty name to a user (if you have several processes in the same uid). For instance, here is a part of code in the RunningState.java file:
// If we couldn't get information about the overall
// process, try to find something about the uid.
String[] pkgs = pm.getPackagesForUid(mUid);
// If there is one package with this uid, that is what we want.
if (pkgs.length == 1) {
try {
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
} catch (PackageManager.NameNotFoundException e) {
}
}
// If there are multiple, see if one gives us the official name
// for this uid.
for (String name : pkgs) {
try {
PackageInfo pi = pm.getPackageInfo(name, 0);
if (pi.sharedUserLabel != 0) {
CharSequence nm = pm.getText(name,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mDisplayLabel = nm;
mLabel = nm.toString();
mPackageInfo = pi.applicationInfo;
return;
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
Basically, it does the following things. At first, it tries to get information about the overall process. If it has not find, it tries to get information using UID of the application as a parameter (this is a part of code that I've given here). If there is only one package with this UID the information about the process is got from this package. But if there are several packages (using shareUserId) then it iterates and tries to find official (pretty) name.
As a confirmation to my words I found the following string in MediaProvider:
<!-- Label to show to user for all apps using this UID. -->
<string name="uid_label">Media</string>
Thus, all process that uses android:sharedUserId="android.media" will have name Media.
I do not think that this feature will be used a lot by ordinary developers and is useful for them.

Categories

Resources