If you call AbsListView.setItemChecked() directly, it works well, and the ActionMode will activate and create.
mGridView.setItemChecked(pPosition, true);
But when you call View.startActionMode() first, then call AbsListView.setItemChecked(), the ActionMode create by startActionMode() will destroy, and recreate a new one by setItemChecked().
My question is: How to avoid this issue when call View.startActionMode() first?
Looking forward to your reply! Thanks!
Why recreate a new one? See the source code of AbsListView.setItemChecked(int position, boolean value) method, you can see following codeļ¼
// Start selection mode if needed. We don't need to if we're unchecking something.
if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
if (mMultiChoiceModeCallback == null ||
!mMultiChoiceModeCallback.hasWrappedCallback()) {
throw new IllegalStateException("AbsListView: attempted to start selection mode " +
"for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
"supplied. Call setMultiChoiceModeListener to set a callback.");
}
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
That means if mChoiceActionMode == null, it will call startActionMode(mMultiChoiceModeCallback), so will recreate a new ActionMode.
And how to fix?
Here is a simple way: use reflect to assign a ActionMode create by startActionMode() to the private field mChoiceActionMode in AbsListView.
private void startActionMode() {
// Get the field "mMultiChoiceModeCallback" instance by reflect
AbsListView.MultiChoiceModeListener wrapperIns = null;
try {
Field wrapper = null;
wrapper = AbsListView.class.getDeclaredField("mMultiChoiceModeCallback");
wrapper.setAccessible(true);
wrapperIns = (AbsListView.MultiChoiceModeListener) wrapper.get(mMessageGridView);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// Start the ActionMode, but not select any item.
ActionMode actionMode = mMessageGridView.startActionMode(wrapperIns);
// Assign actionMode to field "mChoiceActionMode" by reflect
try {
Field mChoiceActionMode = null;
mChoiceActionMode = AbsListView.class.getDeclaredField("mChoiceActionMode");
mChoiceActionMode.setAccessible(true);
mChoiceActionMode.set(mMessageGridView, actionMode);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
Why here we use wrapper? Because AbsListView.setMultiChoiceModeListener(MultiChoiceModeListener listener) will wrap our mMultiChoiceModeListener, so we can't not use directly.
Related
I am trying billow Code from this answer to check if the permission is enabled. but it is returning false even when the permission is enabled from the settings.
public static boolean canDrawOverlayViews(Context con){
if(Build.VERSION.SDK_INT< Build.VERSION_CODES.LOLLIPOP){return true;}
try {
return Settings.canDrawOverlays(con);
}
catch(NoSuchMethodError e){
return canDrawOverlaysUsingReflection(con);
}
}
public static boolean canDrawOverlaysUsingReflection(Context context) {
try {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
Class clazz = AppOpsManager.class;
Method dispatchMethod = clazz.getMethod("checkOp", new Class[] { int.class, int.class, String.class });
//AppOpsManager.OP_SYSTEM_ALERT_WINDOW = 24
int mode = (Integer) dispatchMethod.invoke(manager, new Object[] { 24, Binder.getCallingUid(), context.getApplicationContext().getPackageName() });
return AppOpsManager.MODE_ALLOWED == mode;
} catch (Exception e) { return false; }
}
Recently I've also faced the same issue and got the following workaround .
Referenced from
https://code.google.com/p/android/issues/detail?id=198671#c7
public boolean getWindoOverLayAddedOrNot2() {
String sClassName = "android.provider.Settings";
try {
Class classToInvestigate = Class.forName(sClassName);
if (context == null)
context = activity;
Method method = classToInvestigate.getDeclaredMethod("isCallingPackageAllowedToDrawOverlays", Context.class, int.class, String.class, boolean.class);
Object value = method.invoke(null, context, Process.myUid(), context.getPackageName(), false);
Log.i("Tag", value.toString());
// Dynamically do stuff with this class
// List constructors, fields, methods, etc.
} catch (ClassNotFoundException e) {
// Class not found!
} catch (Exception e) {
// Unknown exception
e.printStackTrace();
}
return false;
}
does the check involves the device admin?
I have encountered this problem when disabling device admin, I have checked this permission in the DeviceAdminReceiver->onDisabled() and on some devices, and canDrawOverlays returned false, despite the fact i had the permission.
The above answer helped sometimes but not all the time. the thing that did work is Thread.sleep before the check.
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// some exception here
}
The minimal time that worked for me was 20 millis. than canDrawOverlays returned true
Note: this is not a good practice however this is the only thing that worked for me
Based on BennyP's answer, I've made a Runnable run the required code after 500ms and that worked very well. The feedback is a bit delayed, but the user won't even notice the delay.
This is the code I've added to my onResume()
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if(!Settings.canDrawOverlays(ControllerActivity.this)){
//Handle overlay permission not given here
}
else{
//Handle overlay permission given here
}
}
}, 500);
Hope it helps!
I tried restarting the activity after the user accessed the setting . This is code :
public static void restartActivity(Activity act){
Intent intent = getIntent();
finish();
startActivity(intent);
}
First of all, I am really very surprised with this strange behaviour of
Settings.canDrawOverlays(this);
I also faced the same issue with its usage, it was returning false even if the permission is already assigned.
What I noticed that, I was using this check in my onStart() method, where it was creating this wired behavior. To resolve this, I searched over internet and no result was there that can satisfy me and the one I can use.
Solution
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.e("Overlay Permission", "" + Settings.canDrawOverlays(this));
if (!Settings.canDrawOverlays(this)) {
MyPreferences.saveBoolean(HomeScreen.this, "showOverlayPermissionDialog", true);
} else {
MyPreferences.saveBoolean(HomeScreen.this, "showOverlayPermissionDialog", false);
}
}
I did something lake this, in my onCreate(). Here I saved the values accordingly in my SharedPreferences, and according to these Shared Preference values, I created a check for showing an overlay dialog in my onStart(). This worked fine!
You can try this solution, and mark this answer useful if your problem is solved.
Thanks
i'm trying to make an app by using calimero library for KNX. In my app, i use some buttons, switches, togglebuttons, etc to switch on/off the lights.
With 'Button', everything works well. I can switch on/off a light or open/close curtains.
private class button9OnClickListener implements View.OnClickListener {
public void onClick(View view){
try {
falseTask dt = new falseTask();
String adr = "5/1/0";
dt.execute(adr);
} catch (Exception e) {
}
}
}
But when i use 'Switch' with either OnClickListener or OnCheckedChangeListenser, it crash.
private class switch1OnCheckedChangeListener implements CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if(compoundButton.isChecked()) {
try {
trueTask dt = new trueTask();
String adr = "5/4/6";
dt.execute(adr);
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
falseTask dt = new falseTask();
String adr = "5/4/6";
dt.execute(adr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
the problem is below:
uncaughtException java.lang.RuntimeException: An error occured while executing doInBackground()
and here's my doInBackground
protected String doInBackground(String...strings){
String adr = strings[0];
try {
final InetSocketAddress localEP = new InetSocketAddress(InetAddress.getByName(hostip), 0);
final InetSocketAddress remoteEP = new InetSocketAddress(remoteip, knxServerPort);
knxLink = new KNXNetworkLinkIP(KNXNetworkLinkIP.TUNNEL, localEP, remoteEP, true, TPSettings.TP1);
pc = new ProcessCommunicatorImpl(knxLink);
pc.write(new GroupAddress(adr), true);
knxLink.close();
}
catch (final KNXException e) {
}
catch (final UnknownHostException e) {
}
return null;
}
i'm new to java and android programming. Can you help with this problem? i can't understand why this work for 'button' but not for 'switch'.
Thank you in advance
UPDATE
I copy my code and try with eclipse. Everything work fine. I can switch on/off the light i want and there is no error.
With android studio, it did not work. Someone know the reason?
I tried with ToggleButton and Switch in Eclipse. All work good. I put exactly what I used in android studio. I copy all the code without any change. Can't understand the problem.
Finally I find the problem. I use 2 Bundle in the MainActivity and I use these 2 Bundle in doInBackground for my AsyncTask in the SecondActivity. But I just pass on the first Bundle to the SecondActivity and I forget to pass on the second Bundle. So I got error while executing doInBackground() What foolish mistake I have made...
I've seen this question:
Changing the ActionBar hide animation?
But it doesn't say whether it's possible to disable animation altogether.
You can now do this,
getSupportActionBar().setShowHideAnimationEnabled(false);
I fixed using the below method:
public static void disableShowHideAnimation(ActionBar actionBar) {
try
{
actionBar.getClass().getDeclaredMethod("setShowHideAnimationEnabled", boolean.class).invoke(actionBar, false);
}
catch (Exception exception)
{
try {
Field mActionBarField = actionBar.getClass().getSuperclass().getDeclaredField("mActionBar");
mActionBarField.setAccessible(true);
Object icsActionBar = mActionBarField.get(actionBar);
Field mShowHideAnimationEnabledField = icsActionBar.getClass().getDeclaredField("mShowHideAnimationEnabled");
mShowHideAnimationEnabledField.setAccessible(true);
mShowHideAnimationEnabledField.set(icsActionBar,false);
Field mCurrentShowAnimField = icsActionBar.getClass().getDeclaredField("mCurrentShowAnim");
mCurrentShowAnimField.setAccessible(true);
mCurrentShowAnimField.set(icsActionBar,null);
}catch (Exception e){
//....
}
}
}
If you use ActionBarSherlock then you can do it. See ActionBarImpl class, it has setShowHideAnimationEnabled(boolean enabled) method.
I use GestureDetector to implement scrolling inside a custom View. My implementation is based on this: Smooth scrolling with inertia and edge resistance/snapback
I noticed a short pause before the scrolling starts: I examined the onScroll messages and noticed that the first one arrives only after a larger movement of a finger, which causes noticable lag at the beginning of the scrolling. After that the scrolling is smooth.
It seems GestureDetector starts sending onScroll messages only after a minimal distance between the motionevents to make sure the gesture is not a longtap or tap (btw I set setIsLongpressEnabled(false)).
Is there any way to change this behaviour and create a smooth scroll without implementing a custom scroll gesture using low level touch events?
The answer is no, you have to create your own GestureDetector. If you look at the Android source code (GestureDetector.java) lines 524 to 540 are use to detect the "touch slop" for a single tap. Specifically line 528 prevents the onScroll event from being called until the movement is outside the touch slop (which is pulled from the view configuration). You cannot change the view configuration and the slop is hard coded at 16 pixels. This is the radius that causes the lag that you are seeing.
You can use reflection to change mTouchSlopSquare from GestureDetector.java
public static void setGestureDetectorTouchSlop(GestureDetector gestureDetector, int value) {
try {
Field f_mTouchSlopSquare = GestureDetector.class.getDeclaredField("mTouchSlopSquare");
f_mTouchSlopSquare.setAccessible(true);
f_mTouchSlopSquare.setInt(gestureDetector, value * value);
} catch (NoSuchFieldException | IllegalAccessException | NullPointerException e) {
Log.w(TAG, gestureDetector.toString(), e);
}
}
Also, here is the method to change the slop for the GestureDetectorCompat.java
public static void setGestureDetectorTouchSlop(GestureDetectorCompat gestureDetector, int value) {
try {
Field f_mImpl = GestureDetectorCompat.class.getDeclaredField("mImpl");
f_mImpl.setAccessible(true);
Object mImpl = f_mImpl.get(gestureDetector);
if (mImpl == null) {
Log.w(TAG, f_mImpl + " is null");
return;
}
Class<?> c_GDCIJellybeanMr2 = null;
Class<?> c_GDCIBase = null;
try {
c_GDCIJellybeanMr2 = Class.forName(GestureDetectorCompat.class.getName() + "$GestureDetectorCompatImplJellybeanMr2");
c_GDCIBase = Class.forName(GestureDetectorCompat.class.getName() + "$GestureDetectorCompatImplBase");
} catch (ClassNotFoundException ignored) {
}
if (c_GDCIJellybeanMr2 != null && c_GDCIJellybeanMr2.isInstance(mImpl)) {
Field f_mDetector = c_GDCIJellybeanMr2.getDeclaredField("mDetector");
f_mDetector.setAccessible(true);
Object mDetector = f_mDetector.get(mImpl);
if (mDetector instanceof GestureDetector)
setGestureDetectorTouchSlop((GestureDetector) mDetector, value);
} else if (c_GDCIBase != null) {
Field f_mTouchSlopSquare = c_GDCIBase.getDeclaredField("mTouchSlopSquare");
f_mTouchSlopSquare.setAccessible(true);
f_mTouchSlopSquare.setInt(mImpl, value * value);
} else {
Log.w(TAG, "not handled: " + mImpl.getClass().toString());
}
} catch (NoSuchFieldException | IllegalAccessException | NullPointerException e) {
Log.w(TAG, gestureDetector.getClass().toString(), e);
}
}
There is any way I can enable Android NFC reader using API?
So apparently there is no way to enable the NFC from the API, even though Google does so within their source code (see below).
If you look at a line from the API for NfcAdapter.isEnabled():
Return true if this NFC Adapter has
any features enabled.
Application may use this as a helper
to suggest that the user should turn
on NFC in Settings.
If this method returns false, the NFC
hardware is guaranteed not to generate
or respond to any NFC transactions.
It looks like there is no way to do it within the API. Bummer. Your best bet is a dialog to inform the user they need to enable it in the settings, and perhaps launch a settings intent.
EDIT: The following is from the source, but it looks like they didn't allow the user to implement the methods in the API (I'm confused about this).
I found this from the android source code to help enable and disable the adapter.
Relevant source:
public boolean onPreferenceChange(Preference preference,
Object value) {
// Turn NFC on/off
final boolean desiredState = (Boolean) value;
mCheckbox.setEnabled(false);
// Start async update of the NFC adapter state, as the API is
// unfortunately blocking...
new Thread("toggleNFC") {
public void run() {
Log.d(TAG, "Setting NFC enabled state to: "
+ desiredState);
boolean success = false;
if (desiredState) {
success = mNfcAdapter.enable();
} else {
success = mNfcAdapter.disable();
}
if (success) {
Log.d(TAG,
"Successfully changed NFC enabled state to "
+ desiredState);
mHandler.post(new Runnable() {
public void run() {
handleNfcStateChanged(desiredState);
}
});
} else {
Log.w(TAG, "Error setting NFC enabled state to "
+ desiredState);
mHandler.post(new Runnable() {
public void run() {
mCheckbox.setEnabled(true);
mCheckbox
.setSummary(R.string.nfc_toggle_error);
}
});
}
}
}.start();
return false;
}
I got it working through reflection
This code works on API 15, haven't checked it against other verions yet
public boolean changeNfcEnabled(Context context, boolean enabled) {
// Turn NFC on/off
final boolean desiredState = enabled;
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
if (mNfcAdapter == null) {
// NFC is not supported
return false;
}
new Thread("toggleNFC") {
public void run() {
Log.d(TAG, "Setting NFC enabled state to: " + desiredState);
boolean success = false;
Class<?> NfcManagerClass;
Method setNfcEnabled, setNfcDisabled;
boolean Nfc;
if (desiredState) {
try {
NfcManagerClass = Class.forName(mNfcAdapter.getClass().getName());
setNfcEnabled = NfcManagerClass.getDeclaredMethod("enable");
setNfcEnabled.setAccessible(true);
Nfc = (Boolean) setNfcEnabled.invoke(mNfcAdapter);
success = Nfc;
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
} else {
try {
NfcManagerClass = Class.forName(mNfcAdapter.getClass().getName());
setNfcDisabled = NfcManagerClass.getDeclaredMethod("disable");
setNfcDisabled.setAccessible(true);
Nfc = (Boolean) setNfcDisabled.invoke(mNfcAdapter);
success = Nfc;
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
if (success) {
Log.d(TAG, "Successfully changed NFC enabled state to "+ desiredState);
} else {
Log.w(TAG, "Error setting NFC enabled state to "+ desiredState);
}
}
}.start();
return false;
}//end method
This requires 2 permissions though, put them in the manifest:
<!-- change NFC status toggle -->
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
The NFC button's state switches accordingly when the code is used, so there are no issues when doing it manually in the seetings menu.
If you can see the NfcService Application Source Code, there is a Interface file INfcAdapter.aidl. In the file two API's are there namely "boolean enable()" and "boolean disable()". You can directly use this API's to enable and disable NfcService through an android application. But the trick over here is that you can not compile the code using SDK provided by the Android. You have to compile the application using the a makefile. I have successfully build a application.
I hope this forum would be help you to resolve this issue as well to get the clear understanding on the NFC power on/off API barries.
http://ranjithdroid.blogspot.com/2015/11/turn-onoff-android-nfc-by.html