I have a Tab Activity and with in Tab, im using Activity Group. and using LocalActivityManger,
im trying to destroy an Activty using the following function call provided in LocalActivityManger class
manager.destroyActivity(mIdList.get(index), true);
in the code. but later i found that there is a bug in Android impl for this
The exact source of the problem is in the following chunk of code in LocalActivityManager.java:
public Window destroyActivity(String id, boolean finish) {
LocalActivityRecord r = mActivities.get(id);
Window win = null;
if (r != null) {
win = performDestroy(r, finish);
if (finish) {
mActivities.remove(r);
}
}
return win;
}
The variable mActivities is the hashmap containing the activity records and it uses the id passed into startActivity() as the key. In this method, the object passed in for the key is a LocalActivityRecord instead of the id string. This results in the hashmap not finding the entry and thus not removing it.
More info refer this link. http://code.google.com/p/android/issues/detail?id=879More
and i found a work around for this issue and im using following function to fix the problem.
public boolean destroy(String id) {
if(manager != null){
manager.destroyActivity(id, false);
try {
final Field mActivitiesField = LocalActivityManager.class.getDeclaredField("mActivities");
if(mActivitiesField != null){
mActivitiesField.setAccessible(true);
#SuppressWarnings("unchecked")
final Map<String, Object> mActivities = (Map<String, Object>)mActivitiesField.get(manager);
if(mActivities != null){
mActivities.remove(id);
}
final Field mActivityArrayField = LocalActivityManager.class.getDeclaredField("mActivityArray");
if(mActivityArrayField != null){
mActivityArrayField.setAccessible(true);
#SuppressWarnings("unchecked")
final ArrayList<Object> mActivityArray = (ArrayList<Object>)mActivityArrayField.get(manager);
if(mActivityArray != null){
for(Object record : mActivityArray){
final Field idField = record.getClass().getDeclaredField("id");
if(idField != null){
idField.setAccessible(true);
final String _id = (String)idField.get(record);
if(id.equals(_id)){
mActivityArray.remove(record);
break;
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
return false;
}
now the problem is, this fix is working fine in Android API versions 2.1,2.2 and 2.3 but i tested in 3.0 . but it is failing there. no exceptions.
I want to know in which API version this bug has been fixed.
And also what fix can i make for this so that it will work fine in all the API versions after 2.1 .
Thank u
Related
I am trying to update an element in a dynamodb table but it never updates I've done some digging and found that in the updateItem function it doesn't think that the anything in the document has changed any adivice on how fix this (also I am able to get element no problem)
Document builder = table.getMemoById(Room);
builder.commit();
builder.put("room_status","clean");
Document tmp = table.curr_table.updateItem(builder,
new Primitive(Room),
new UpdateItemOperationConfig().withReturnValues(ReturnValue.UPDATED_NEW));
Log.d(TAG, tmp.toString());
after using the debugger I found that the issue is in Primitive.java where the last line and this.value is compared to this.value but needs to be other.value the file is read only any suggestion on how to fix this
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final Primitive other = (Primitive) obj;
return Objects.equal(this.type, other.type) && Objects.equal(this.value, this.value);
}
turns out if you use the current version of the library 2.9.2 this is fixed I was using a much older version
please help me I am trying to get text from stack of notifications. I am getting following error.
java.lang.NoSuchFieldException: No field mActions in class
Landroid/app/Notification$BuilderRemoteViews; (declaration of
'android.app.Notification$BuilderRemoteViews' appears in
/system/framework/framework.jar)
I am using NotificationListenerService and
private List<String> getMessages(Notification notification) {
RemoteViews views = notification.bigContentView;
if (views == null) views = notification.contentView;
if (views == null) return null;
List<String> text = new ArrayList<String>();
try {
Field field = views.getClass().getDeclaredField(Constants.ACTIONS);
field.setAccessible(true);
#SuppressWarnings("unchecked")
ArrayList<Parcelable> actions = (ArrayList<Parcelable>) field.get(views);
for (Parcelable p : actions) {
Parcel parcel = Parcel.obtain();
p.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
int tag = parcel.readInt();
if (tag != 2) continue;
parcel.readInt();
String methodName = parcel.readString();
if (methodName == null) {
continue;
} else if (methodName.equals(Constants.SET_TEXT)) {
parcel.readInt();
methodName.getBytes();
String gettingText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel).toString().trim();
text.add(gettingText);
} else if (methodName.equals(Constants.SET_TIME)) {
parcel.readInt();
String gettingText = new SimpleDateFormat("h:mm a").format(new Date(parcel.readLong()));
text.add(gettingText);
}
parcel.recycle();
}
} catch (Exception e) {
Log.e("NotificationClassifier", e.toString());
}
return text;
}
This is working fine in android 4,5 but's its getting crash on Android 7, Getting error like mAction field not available.
Please any one help me to solve this, I am stuck from two days.
Thank you
You are using reflection to access method names and you are not switching the code for multiple platforms. The fields/methods that you are trying to access, change over time on multiple versions of the OS, so if you really want to have the reflection, you have to study what changes they were.
Generally, reflection should really be last resort, since on Android, the base code varies from version to version, and even from manufacturer to manufacturer.
In my app I need to monitorize recently added or updated packages, but since Oreo this is a hard task.
To do it I have a service that runs every X time to detect the new installed/updated apps.
The main core of this service is to call the getChangedPackages function from the PackageManager, but this function always returns null, even if I install or update any app from or not from the Play Store in the interval between two consequtive calls to getChangedPackages.
https://developer.android.com/reference/android/content/pm/PackageManager.html#getChangedPackages(int)
I need to request any permission to call this function? Is the getChangedPackages buggy?
private void _doProcess()
{
try
{
PackageManager package_manager = getPackageManager();
int sequence_number = ApplicationPreferences.getInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, 0);
ChangedPackages changed_packages = package_manager.getChangedPackages(sequence_number);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages == null) changed_packages = package_manager.getChangedPackages(0);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages != null)
{
List<String> packages_names = changed_packages.getPackageNames();
LogUtilities.show(this, String.format("%d recently installed/updated apps", packages_names == null ? 0 : packages_names.size()));
if (packages_names != null) for (String package_name : packages_names) PackagesUpdatedReceiver.doProcessPackageUpdate(this, new Intent(isNewInstall(package_manager, package_name) ? Intent.ACTION_PACKAGE_ADDED : Intent.ACTION_PACKAGE_REPLACED).setData(Uri.parse(String.format("package:%s", package_name))));
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", changed_packages.getSequenceNumber()));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, changed_packages.getSequenceNumber());
}
else
{
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", sequence_number + 1));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, sequence_number + 1);
}
}
catch (Exception e)
{
LogUtilities.show(this, e);
}
}
My experimental results so far have shown that this PackageManager API method getChangedPackages() is not reliable: quite often the returned ChangedPackages value contains many unchanged packages. So I’ve decided to implement a similar feature in a class called PackageUtils, as shown below. The idea is to poll for all the installed packages, as shown in method getInstalledPackageNames() below, and compare the string list with a previously saved one. This comparison boils down to comparing 2 string lists, as shown in method operate2StringLists() below. To get a set of removed packages, use GET_1_MINUS_2_OR_REMOVED as operation. To get a set of added packages, use GET_2_MINUS_1_OR_ADDED as operation.
public class PackageUtils {
public static final int GET_1_MINUS_2_OR_REMOVED = 0;
public static final int GET_2_MINUS_1_OR_ADDED = 1;
// Get all the installed package names
public static List<String> getInstalledPackageNames(Context context) {
List<String> installedPackageNames = new ArrayList<>();
try {
PackageManager packageManager = context.getPackageManager();
List<ApplicationInfo> appInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo appInfo : appInfoList) {
installedPackageNames.add(appInfo.packageName);
}
} catch (Exception e) {
e.printStackTrace();
}
return installedPackageNames;
}
// Compare 2 string lists and return differences.
public static Set<String> operate2StringLists(List<String> pkgList1, List<String> pkgList2, int operation) {
Set<String> result = null;
Set<String> pkgSet1 = new HashSet<String>(pkgList1);
Set<String> pkgSet2 = new HashSet<String>(pkgList2);
switch (operation) {
case GET_1_MINUS_2_OR_REMOVED:
pkgSet1.removeAll(pkgSet2);
result = pkgSet1;
break;
case GET_2_MINUS_1_OR_ADDED:
pkgSet2.removeAll(pkgSet1);
result = pkgSet2;
break;
default:
break;
}
return result;
}
}
The code has been tested on an Android Oreo device. It can reliably detect all added and removed packages between 2 time instances. However, it can’t detect updated packages in-between.
Finally got it. You have to create a variable called sequenceNumber, and update it every time you query changed packages.
private static int sequenceNumber = 0;
...
PackageManager pm = getContext().getPackageManager();
ChangedPackages changedPackages = pm.getChangedPackages(sequenceNumber);
if(changedPackages != null)
sequenceNumber = changedPackages.getSequenceNumber();
I'm currently studying ways to sync data between an Android device to Google Drive and stumbled upon the sample code at https://github.com/googledrive/android-quickeditor , but I'm encountering an issue when opening an existing file and then tapping on 'Save Changes'. Basically the problematic code is here:
#Override
protected com.google.android.gms.common.api.Status doInBackground(DriveId... params) {
R await;
DriveFile file = params[0].asDriveFile();
PendingResult<DriveContentsResult> openDriveContentsResult = file.open(mClient, DriveFile.MODE_WRITE_ONLY, null);
if (!openDriveContentsResult.await().getStatus().isSuccess()) {
return openDriveContentsResult.await().getStatus();
}
Changes changes = edit(openDriveContentsResult.await().getDriveContents());
PendingResult<MetadataResult> metadataResult = null;
PendingResult<com.google.android.gms.common.api.Status> closeContentsResult = null;
if (changes.getMetadataChangeSet() != null) {
metadataResult = file.updateMetadata(mClient, changes.getMetadataChangeSet());
if (!metadataResult.await().getStatus().isSuccess()) {
return metadataResult.await().getStatus();
}
}
if (changes.getDriveContents() != null) {
closeContentsResult = changes.getDriveContents().commit(mClient, null);
closeContentsResult.await();
}
return closeContentsResult.await().getStatus();
}
The exact line throwing the error is:
Changes changes = edit(openDriveContentsResult.await().getDriveContents());
The Error is java.lang.IllegalStateException: Result has already been consumed. I referred to the PendingResult class reference and it clearly states that "After the result has been retrieved using await() or delivered to the result callback, it is an error to attempt to retrieve the result again." Fair enough I guess.
The problem is, how am I supposed to fix this properly? I have commented out
if (!metadataResult.await().getStatus().isSuccess()) {...}
for now to avoid calling await() twice (for sake of debugging), BUT (as expected) then results into the same error at
return closeContentsResult.await().getStatus();
So...how can I fix this? Pretty please?
Solved. For anyone that's getting frustrated over this:
#Override
protected com.google.android.gms.common.api.Status doInBackground(DriveId... params)
{
R await;
DriveFile file;
Changes changes;
PendingResult<DriveContentsResult> openDriveContentsResult;
DriveContentsResult driveContentsResult;
PendingResult<MetadataResult> updateMetadataResult;
MetadataResult metaDataResult;
PendingResult<com.google.android.gms.common.api.Status> commitResults;
com.google.android.gms.common.api.Status closeContentResults = null;
file = params[0].asDriveFile();
openDriveContentsResult = file.open(mClient, DriveFile.MODE_WRITE_ONLY, null);
driveContentsResult = openDriveContentsResult.await();
if (!driveContentsResult.getStatus().isSuccess())
{
return driveContentsResult.getStatus();
}
changes = edit(driveContentsResult.getDriveContents());
updateMetadataResult = null;
commitResults = null;
if (changes.getMetadataChangeSet() != null)
{
updateMetadataResult = file.updateMetadata(mClient, changes.getMetadataChangeSet());
metaDataResult = updateMetadataResult.await();
if (!metaDataResult.getStatus().isSuccess())
{
return metaDataResult.getStatus();
}
}
if (changes.getDriveContents() != null)
{
commitResults = changes.getDriveContents().commit(mClient, null);
closeContentResults = commitResults.await();
}
if (null == commitResults)
{
return commitResults.await().getStatus();
}
else
{
return closeContentResults.getStatus();
}
}
im using the Google Drive API to save(use as backup) a database there, its working nice, but just if i use the ROOT
the Api Call:
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
......build();
Drive.DriveApi.getRootFolder(mGoogleApiClient)
.createFile(mGoogleApiClient, metadataChangeSet, result.getDriveContents())
.setResultCallback(fileCallback);
CallBack to Save the file:
final public ResultCallback < DriveFolder.DriveFileResult > fileCallback = new
ResultCallback < DriveFolder.DriveFileResult > () {
#Override
public void onResult(DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
return;
}
Log.i(TAG, "Successfull !");
}
};
i know that i must get the Folder, but if i do this, i need to do a CallBack to call another callback and then save?
isnt any way to directly do .createNewFile inside the FOLDER? without doing another Query for folder, check if the folder exist than create the folder, than use the DriveID, than create the file?
Remember, that in the GooDrive universe, the tree structure (folder, subfolder, ...) is a mirage. The Drive is a flat system of objects (files, folders) where one of the metadata fields is a 'set of parent IDs', that actually forms the notion of parentobject - childobject structure. Actually the classic tree (one parent many children) is not even enforced, so a child object can 'appear' in more that one parent.
This fact explains that you CAN NOT create an OS type of path in one shot. The objects (parents) must be created before their IDs can be plugged into child objects' metadata.
So the only way to do it, is to do what you say:
if folder exists
return it's ID
else
return ID of newly created one
create a child object with parent's ID
... and here is an example how I create a structure of type:
/ MYROOT / 2015 / 2015-12
(where MYROOT, 2015 , 2015-12 are subfloders the Drive root)
new Thread(new Runnable() {
#Override
public void run() {
DriveId Id = getFolder( getFolder( getFolder(
Drive.DriveApi.getRootFolder(mGAC).getDriveId(), "MYROOT"),
"2015",
"2015-12"
);
}
}).start();
GoogleApiClient mGAC;
DriveId getFolder(DriveId parentId, String titl) {
DriveId dId = null;
if (parentId != null && titl != null) try {
ArrayList<Filter> fltrs = new ArrayList<>();
fltrs.add(Filters.in(SearchableField.PARENTS, parentId));
fltrs.add(Filters.eq(SearchableField.TITLE, titl));
fltrs.add(Filters.eq(SearchableField.MIME_TYPE, "application/vnd.google-apps.folder"));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
MetadataBuffer mdb = null;
DriveApi.MetadataBufferResult rslt = Drive.DriveApi.query(mGAC, qry).await();
if (rslt.getStatus().isSuccess()) try {
mdb = rslt.getMetadataBuffer();
if (mdb.getCount() > 0)
dId = mdb.get(0).getDriveId();
} catch (Exception ignore) {}
finally { if (mdb != null) mdb.close(); }
if (dId == null) {
MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(UT.MIME_FLDR).build();
DriveFolderResult r1 = parentId.asDriveFolder().createFolder(mGAC, meta).await();
DriveFolder dFld = (r1 != null) && r1.getStatus().isSuccess() ? r1.getDriveFolder() : null;
if (dFld != null) {
MetadataResult r2 = dFld.getMetadata(mGAC).await();
if ((r2 != null) && r2.getStatus().isSuccess()) {
dId = r2.getMetadata().getDriveId();
}
}
}
} catch (Exception e) { e.printStackTrace(); }
return dId;
}
In the 'mdb.get(0).getDriveId()' area, you can see how hacky it gets when you try to impose a classic tree structure on the Drive. The search here can return multiple objects with the same name, so I use the first one. There should be some kind of error reporting here.
As you can see it is possible to replace callbacks with the 'await()' method, flattening the code into a classic DOS style spaghetti code as long as you place the whole sequence off-UI thread (asynctask, thread, ....)
Still, more elegant (IMO) option to accomplish this is to use recursive call from the result callback.
fromPath(Drive.DriveApi.getRootFolder(mGAC).getDriveId(), "MYROOT/2015/2015-12/file.jpg");
....
void fromPath(final DriveId parentId, final String path) {
if (parentId != null && path != null) {
final int idx = path.indexOf('/');
if (idx < 0) {
// reached last path item - probably file name
// CREATE FILE WITH patentID AND QUIT
return; //--- DONE -------------------->>>
}
final String titl = path.substring(0, idx);
ArrayList<Filter> fltrs = new ArrayList<>();
fltrs.add(Filters.in(SearchableField.PARENTS, parentId));
fltrs.add(Filters.eq(SearchableField.TITLE, titl));
fltrs.add(Filters.eq(SearchableField.MIME_TYPE, UT.MIME_FLDR));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
Drive.DriveApi.query(mGAC, qry).setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult rslt) {
MetadataBuffer mdb = null;
if (rslt != null && rslt.getStatus().isSuccess()) {
try {
mdb = rslt.getMetadataBuffer();
for (Metadata md : mdb) {
if (md.isTrashed()) continue;
fromPath(md.getDriveId(), path.substring(idx + 1));
return; //+++ first found, NEXT +++++++>>>
}
} finally { if (mdb != null) mdb.close(); }
}
MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(UT.MIME_FLDR).build();
parentId.asDriveFolder().createFolder(mGAC, meta)
.setResultCallback(new ResultCallback<DriveFolderResult>() {
#Override
public void onResult(DriveFolderResult rslt) {
DriveFolder dFld = rslt != null && rslt.getStatus().isSuccess() ? rslt.getDriveFolder() : null;
if (dFld != null) {
dFld.getMetadata(mGAC).setResultCallback(new ResultCallback<MetadataResult>() {
#Override
public void onResult(MetadataResult rslt) {
if (rslt != null && rslt.getStatus().isSuccess()) {
fromPath(rslt.getMetadata().getDriveId(), path.substring(idx + 1));
return; //+++ created, NEXT +++++++>>>
}
}
});
}
}
});
}
});
}
}
A WORD OF CAUTION:
As I called this sequence repeatedly, using the last DriveId (like 2015-12) as a parent of a JPEG image file, I have experienced weird behavior, like suddenly getting a 'null' result from 'Drive.DriveApi.getRootFolder(mGAC).getDriveId()'. It shouldn't happen and I assume it is a bug in GDAA. I contribute this to the fact that the DriveId used inside GDAA is 'invalid' until the folder gets committed and the ResourceId is resolved in underlying REST Api. Unfortunately, there is no completion event available for folder creation, so I resolved this by calling this sequence only once in onConnected() and caching the '2015-12's DriveId for later use as a parent of the image JPEG files.
Actually you can see it here (createTree() method) with text file on the tail, but the moment I switched the TEXT to JPEG, all hell broke lose.
Good Luck