In my app, I have a NotificationListenerService that listens to all notifications. I have a StatusBarNotification field that is assigned when some notification is posted and nullified when it is removed. Before nullifying, I have to check whether or not it is the same StatusBarNotification that I have assigned before. However, the check with == operator does not work as expected even though it's exactly the same notification. So how can I compare them?
public class NotificationListener extends NotificationListenerService {
private StatusBarNotification targetNotification;
#Override
public void onNotificationPosted(StatusBarNotification notification) {
if (targetNotification == null && notification.isClearable()) {
targetNotification = notification;
}
}
#Override
public void onNotificationRemoved(StatusBarNotification notification) {
System.out.println("removed noti: " + notification.getPackageName() + ", " + notification.getPostTime()+", "+notification.getId()+", "+notification.getUserId());
System.out.println("target noti: " + targetNotification.getPackageName() + ", " + targetNotification.getPostTime()+", "+targetNotification.getId()+", "+targetNotification.getUserId());
System.out.println(notification == targetNotification);
if (notification == targetNotification) {
targetNotification = null;
}
}
}
Here is the result:
removed noti: com.samepackage, 1412915524994, -99, 0
target noti: com.samepackage, 1412915524994, -99, 0
false
== compare objecs which point to same memory location.(
http://www.programmerinterview.com/index.php/java-questions/java-whats-the-difference-between-equals-and/)
so instead of comparing to objects please compare it's id value. this might be solve your problem.
if (notification.getId() == targetNotification.getId()) {
targetNotification = null;
}
From the documentation of onNotificationRemoved (StatusBarNotification sbn):
Parameters
sbn: A data structure encapsulating at least the original
information (tag and id) and source (package name) used to post the
Notification that was just removed.
So I think to compare two notifications, we need to compare their tag, id, and package name:
if (notification.getTag().equals(targetNotification.getTag()) &&
notification.getId() == targetNotification.getId() &&
notification.getPackageName().equals(targetNotification.getPackageName())) {
targetNotification = null;
}
Edit: Be careful, notification tags can be null! (So, the code above might throw NPE). Make sure to control it.
Related
I'm trying to create two group channels with same name but with different users and custom type.`
But both the group is not created and when I list the groups. I couldn't find what I have done wrong.
createGroupChannelWithData(workOrderIds, false, "Ticket Id:" + jobId, "", Integer.toString(jobId), "Private_Ticket");
private void createGroupChannelWithData(List<String> userIds, boolean distinct, String name, String coverImage, String data, String ticketType) {
GroupChannel.createChannelWithUserIds(userIds, distinct, name, coverImage, data,
ticketType,
new GroupChannel.GroupChannelCreateHandler() {
#Override
public void onResult(GroupChannel groupChannel, SendBirdException e) {
if (e != null) {
// Error!
return;
}
createGroupChannelWithData1(managementIds, false, "Ticket Id:" + jobId, "", Integer.toString(jobId), "Ticket");
}
});
}
` private void createGroupChannelWithData1(List<String> userIds, boolean distinct, String name, String coverImage, String data, String ticketType) {
GroupChannel.createChannelWithUserIds(userIds, distinct, name, coverImage, data,
ticketType,
new GroupChannel.GroupChannelCreateHandler() {
#Override
public void onResult(GroupChannel groupChannel, SendBirdException e) {
if (e != null) {
// Error!
return;
}
getGroupChannelList("end");
}
});
}
Edit:
Channels are created in Sendbird Dashboard.
But while I list the channels, I get only one channel, with custom type "Ticket". I need to get channel, with custom type "Private_Ticket" also.
Everytime, I create two channels, and need the both to list in another app.
My code to get the channel list:
GroupChannelListQuery channelListQuery = GroupChannel.createMyGroupChannelListQuery();
channelListQuery.setIncludeEmpty(true);
channelListQuery.setOrder(GroupChannelListQuery.Order.LATEST_LAST_MESSAGE);
// CHRONOLOGICAL, LATEST_LAST_MESSAGE, CHANNEL_NAME_ALPHABETICAL, and METADATA_VALUE_ALPHABETICAL
channelListQuery.setLimit(15);
channelListQuery.next(new GroupChannelListQuery.GroupChannelListQueryResultHandler() {
#Override
public void onResult(List<GroupChannel> list, SendBirdException e) {
if (e != null) { // Error.
return;
}
for (int i = 0; i < list.size(); i++) {
Log.e(" in first loop name : data : custom type >>",list.get(i).getName()+" : "+list.get(i).getData()+" : "+list.get(i).getCustomType());
}
}
});
Edit:
The issue is fixed. I haven't include the user of the second app, in list. That's the root cause of the issue. thanks.
Great question you have. I hope I can help you with an answer.
The first step is to check if you channels are actually being created. You can do this in Sendbird's Dashboard.
Sendbird Dashboard --> Group Channels --> See channel list
If the channels don't show up in the Sendbird Dashboard then they haven't been created. In this case try logging any errors you see from the SDK when you were creating the channel.
If the channels were created and they are visibile in the Sendbird Dashboard, but are not showing up in your SDK please consider the following.
By default channels without any messages will not be displayed in a channel list query.
Consider these to be "empty channels"
If you need to be able to see empty channels in your channel list query then please include the setIncludeEmpty(true) parameter in your channel list query.
GroupChannelListQuery channelListQuery = GroupChannel.createMyGroupChannelListQuery();
channelListQuery.setIncludeEmpty(true);
channelListQuery.setOrder(GroupChannelListQuery.Order.LATEST_LAST_MESSAGE);
// CHRONOLOGICAL, LATEST_LAST_MESSAGE, CHANNEL_NAME_ALPHABETICAL, and METADATA_VALUE_ALPHABETICAL
channelListQuery.setLimit(15);
channelListQuery.next(new GroupChannelListQuery.GroupChannelListQueryResultHandler() {
#Override
public void onResult(List<GroupChannel> list, SendBirdException e) {
if (e != null) { // Error.
return;
}
}
});
I want to test an application but it's too large to do by static approach.
I started googling. I found a interesting tool called Xposed Framework.
I read a lot of documents / examples. But I cant find the theads about getting method name of application.
My purpose is to log current method name when I press a button in app. Which parameters are sent when the methods are called?
For more information, the application I want to test is a chat application. I want to check that is it secured to use ? Is it true that developers claims the application uses blah blah blah encryption ? Is it real end-to-end encryption?.
According to the large of application, I need some tools to help me analyse this. When I send a message what methods are called ? what values are sent along with ?
If it is opensource you can easily insert a few logs in the source code and recompile. Some code coverage tools allow you to log the executed methods but i am unsure about the parameters (e.g. EMMA coverage).
If it is closed-source then you can do it with Xposed, but it has some challenges. Xposed allows you to hook Java methods, if it is opensource you can lookup the specific methods you want to intercept and print their parameters. If it is closed source, you can always check the method names through decompiling the app with apktool.
Check the Xposed tutorial on how to register hooks. Assuming you created your class that extends XC_MethodHook, the following methods should do the trick for primitive parameters:
#Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String threadname = Thread.currentThread().getName();
String d = _dateFormat.format(new Date());
Class<?> cls = param.this.getClass();
//Extend XC_MethodHook to receive the mthName in the constructor
//and store it as a static field.
Log.v("MethodInvocation", "[A][" + d + "]"
+ "[" + cls.getName() + "."
+ mthName
+ "(" + args(param) + ")" + " = " + result(param)
+ "]" + " [" + threadname + "]");
}
public boolean shouldPrintContent(Object o) {
if (o.getClass().isPrimitive()
|| o.getClass().getName().contains("java.lang"))
return true;
return false;
}
public String args(MethodHookParam param) {
String args = "";
int counter = 0;
if (param != null) {
for (Object o : param.args) {
if (counter > 0)
args += ",";
// argument can be null
if (o == null) {
args += "null";
} else { // if it is an object lets print its type and content.
args += printclean(o.getClass().getName());
if (shouldPrintContent(o)) {
args += ":" + printclean(o.toString());
} else
args += ":nonPrimitiveOrJavaLang";
}
counter++;
}
}
return args;
}
//avoid identation chars in strings
public String printclean(String str) {
char[] res = str.toCharArray();
for (int i = 0; i < str.length(); i++) {
if (res[i] == '\n' || res[i] == '\r' || res[i] == '\t'
|| res[i] == '[' || res[i] == ']') {
res[i] = '*';
}
}
return String.valueOf(res);
}
public String result(MethodHookParam param) {
String res = "";
Object retobj = param.getResult();
if (retobj == null) {
res += "null";
} else {
res += printclean(retobj.getClass().getName());
if (shouldPrintContent(retobj)) {
res += printclean(retobj.toString());
} else
res += "(nonPrimitiveOrJavaLang)";
}
return res;
}
Note that printing any object is not trivial. Here I printed only known types like primitives or other java.lang objects. More complex objects (including collections) you can try using Gson to represent them but this also comes with limitations (e.g. can't often handle reference loops).
Finally, be careful with what method you hook as hooking and logging methods that are called often will impact the performance of the app.
Good luck!
A new Xposed module called Inspeckage does (among many other useful inspections) what you want to do: you can use it to hook any method and view and even change its input and output.
I am working on an ecommerce android app. I am trying to fetch records using dreamfactory API for android, based on multiple filters.
Using an AsyncTask named as GetProductsBySubCatIdTask
public class GetProductsBySubCatIdTask extends BaseAsyncRequest {
Context context;
public Products productsRec;
int subCatId;
String sort_str,fltr_str;
public GetProductsBySubCatIdTask(Context context, int subCataId, String sort_str, String fltr_str){
this.context = context;
this.subCatId = subCataId;
this.sort_str = sort_str;
this.fltr_str = fltr_str;
}
#Override
protected void doSetup() throws ApiException, JSONException {
callerName = "getProductsBySubCatId";
serviceName = AppConstants.DB_SVC;
endPoint = "product";
verb = "GET";
// filter to only select the contacts in this group
if(!TextUtils.isEmpty(fltr_str)){
fltr_str = "&&" + fltr_str;
}
else{
fltr_str = "";
}
queryParams = new HashMap<>();
queryParams.put("filter", "sub_category_id=" + subCatId + fltr_str);
queryParams.put("order", sort_str);
applicationApiKey = AppConstants.API_KEY;
sessionToken = PrefUtil.getString(context, AppConstants.SESSION_TOKEN);
}
#Override
protected void processResponse(String response) throws ApiException, JSONException {
//Log.d("Tang Ho"," >>>>> " + response);
productsRec =
(Products) ApiInvoker.deserialize(response, "", Products.class);
}
#Override
protected void onCompletion(boolean success) {
if(success && productsRec != null && productsRec.products.size() > 0){
Log.d("Tang Ho"," >>>>> Success");
}
}
}
I have used filters which is constructed outside the class and provided as parameter, the possible filters are
unit_offerprice < unit_mrp<br>
unit_offerprice = unit_mrp<br>
(unit_offerprice > 200) && (unit_offerprice > 500)<br>
unit_offerprice > 100<br>
unit_offerprice < 600<br>
All the above filters can be used either individually or in combination of 2 or 3 like
unit_offerprice < unit_mrp && unit_offerprice > 100
After escaping the symbols in the string like
unit_offerprice%3Cunit_mrp
Not able to get desired result,
Searched in documentations but din't found exact thing.
what can be the possible solution for this ?
If your filter has multiple conditions, each condition needs to be placed inside parentheses (). Additionally, the proper syntax for joining filters with and is AND, not &&.
Supported logical operators are AND, OR, and NOT.
In your example, this:
unit_offerprice < unit_mrp && unit_offerprice > 100
should be this:
(unit_offerprice < unit_mrp) AND (unit_offerprice > 100)
See these portions of the documentation:
http://wiki.dreamfactory.com/DreamFactory/Features/Database/Records#Filtering_Records
http://wiki.dreamfactory.com/DreamFactory/Tutorials/Querying_records_with_logical_filters
DreamFactory also offers a number of official support avenues, including user forums. http://www.dreamfactory.com/support
Got it resolved by adding parenthesis for multiple filters.
But there was issue using the special characters (like <,>) and
this needed to be
encoded into %3C (for <) and %3E (for >),
the string used for the filtering is :
"(unit_offerprice>100)AND(unit_offerprice<500)"
the encoded form is :
"(unit_offerprice%3E100)AND(unit_offerprice%3C500)"
I have an app for people with low vision that relies heavily on TTS. However for some reason when I use the speak method TTS randomly skips the first few letters of a sentence or speaks the first few letters in a ver low volume and the rest in a normal volume.
Any idea why this might happen?
This is my current code:
public class SpeechHelper implements TextToSpeech.OnInitListener {
private Context context = null;
private TextToSpeech tts;
public SpeechHelper(Context context)
{
this.context = context;
try {
tts = new TextToSpeech(context, this);
} catch(Exception e) {
Log.e("Phone Features Exception","Couldn't initiate TTS", e);
}
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.getDefault());
}
}
public void speak(String s, int mode, String messageID) {
Log.d("VOLUME", "getStreamVolume " + am.getStreamVolume(AudioManager.STREAM_MUSIC)); // Always 15
Log.d("VOLUME", "isMusicActive " + (am.isMusicActive() ? "true" : "false")); // Always false
Log.d("VOLUME", "isVolumeFixed " + (am.isVolumeFixed() ? "true" : "false")); // Always false
Log.d("VOLUME", "isSpeakerphoneOn: " + (am.isSpeakerphoneOn() ? "true" : "false")); // Always false
Log.d("VOLUME", "getMode: " + am.getMode()); // Always 0
HashMap<String, String> params = new HashMap<String, String>();
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, messageID);
tts.speak(s, mode, params);
}
}
I've noticed the problem always happens on the next 5 seconds after making a call or when unlocking the phone to the app.
Either you are doing something wrong or Android is doing something wrong. If I had to put money on it I would say it is you.
Start by just calling
tts.speak("the quick brown fox jumps over the lazy dog", tts.QUEUE_FLUSH, null);
to see if you can reproduce the problem in a test app (I couldn't). If you can, then I'd be very curious as to what phone/OS you are running on . Your problem probably lies with truncated strings, or something else programmatically messing with volume.
I need to retrieve some text from a RemoteViews object. It is possible for me to get the LayoutId, but I have no idea how to retrieve text from a TextView that is in this RemoteView (namely a notification).
Also the RemoteView only contains setters, but no getters, so I guess I have to use the LayoutId (somehow).
Can you help me with that? Thanks!
/edit: The reason why I am asking this, is because I have an AccessibilityService that retrieves the notification. Therefore this is the only way of retrieving the value.
/edit2: I use this code for receiving the notification:
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
List<CharSequence> notificationList = event.getText();
for (int i = 0; i < notificationList.size(); i++) {
Toast.makeText(this.getApplicationContext(), notificationList.get(i), 1).show();
}
if (!(parcel instanceof Notification)) {
return;
}
final Notification notification = (Notification) parcel;
doMoreStuff();
}
}
With the notification object I have access to a RemoteViews (notification.contentView) and to a PendingIntent (notification.contentIntent).
To get the layoutId, I can call contentView.getLayoutId()
I proposed a similar solution here that also uses reflection to solve the problem, but in a more approachable fashion. This is my solution. In this context, the RemoteViews came from a Notification, so the first three lines can probably be ignored if you already have access to the RemoteViews object. The link on the page provides a much more detailed explanation of what is actually going on. I hope this will help anyone with a similar problem.
public static List<String> getText(Notification notification)
{
// We have to extract the information from the view
RemoteViews views = notification.bigContentView;
if (views == null) views = notification.contentView;
if (views == null) return null;
// Use reflection to examine the m_actions member of the given RemoteViews object.
// It's not pretty, but it works.
List<String> text = new ArrayList<String>();
try
{
Field field = views.getClass().getDeclaredField("mActions");
field.setAccessible(true);
#SuppressWarnings("unchecked")
ArrayList<Parcelable> actions = (ArrayList<Parcelable>) field.get(views);
// Find the setText() and setTime() reflection actions
for (Parcelable p : actions)
{
Parcel parcel = Parcel.obtain();
p.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
// The tag tells which type of action it is (2 is ReflectionAction, from the source)
int tag = parcel.readInt();
if (tag != 2) continue;
// View ID
parcel.readInt();
String methodName = parcel.readString();
if (methodName == null) continue;
// Save strings
else if (methodName.equals("setText"))
{
// Parameter type (10 = Character Sequence)
parcel.readInt();
// Store the actual string
String t = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel).toString().trim();
text.add(t);
}
// Save times. Comment this section out if the notification time isn't important
else if (methodName.equals("setTime"))
{
// Parameter type (5 = Long)
parcel.readInt();
String t = new SimpleDateFormat("h:mm a").format(new Date(parcel.readLong()));
text.add(t);
}
parcel.recycle();
}
}
// It's not usually good style to do this, but then again, neither is the use of reflection...
catch (Exception e)
{
Log.e("NotificationClassifier", e.toString());
}
return text;
}
Taken from Extract notification text from parcelable, contentView or contentIntent :
Notification notification = (Notification) event.getParcelableData();
RemoteViews views = notification.contentView;
Class secretClass = views.getClass();
try {
Map<Integer, String> text = new HashMap<Integer, String>();
Field outerFields[] = secretClass.getDeclaredFields();
for (int i = 0; i < outerFields.length; i++) {
if (!outerFields[i].getName().equals("mActions")) continue;
outerFields[i].setAccessible(true);
ArrayList<Object> actions = (ArrayList<Object>) outerFields[i]
.get(views);
for (Object action : actions) {
Field innerFields[] = action.getClass().getDeclaredFields();
Object value = null;
Integer type = null;
Integer viewId = null;
for (Field field : innerFields) {
field.setAccessible(true);
if (field.getName().equals("value")) {
value = field.get(action);
} else if (field.getName().equals("type")) {
type = field.getInt(action);
} else if (field.getName().equals("viewId")) {
viewId = field.getInt(action);
}
}
if (type == 9 || type == 10) {
text.put(viewId, value.toString());
}
}
System.out.println("title is: " + text.get(16908310));
System.out.println("info is: " + text.get(16909082));
System.out.println("text is: " + text.get(16908358));
}
} catch (Exception e) {
e.printStackTrace();
}
CommonsWare in this question says:
... App widgets are write-only: you can push data to them, but you
cannot read them. Instead, when you update your app widget with
new text, you will need to store that text somewhere, perhaps in a
file.
His answer seems to be logical.
If you are targeting on Android 19+, you can use the following code for getting title/text from a Notification object without using any private APIs.
Notification noty = ...;
Bundle extras = noty.extras;
if (extras != null) {
String title = extras.getString(Notification.EXTRA_TITLE);
String text = extras.getString(Notification.EXTRA_TEXT);
}