How to track Omniture Custom Traffic Variable in Android? - android

In Site Catalyst we have defined Custom Traffic Variable "Item Bought". Now how do i send request to update this custom variable from Android library? i don't find much help here
Please find my code below,
public class TrackingHelper {
private static final String TRACKING_RSID = "MY_TRACKING_RSID";
private static final String TRACKING_SERVER = "MY_TRACKING_SERVER";
static ADMS_Measurement measurement = null;
public static void startActivity(Activity activity) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.startActivity(activity);
}
public static void stopActivity() {
measurement = ADMS_Measurement.sharedInstance();
measurement.stopActivity();
}
public static void configureAppMeasurement(Activity activity) {
if (measurement == null) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.configureMeasurement(TRACKING_RSID, TRACKING_SERVER);
measurement.setOfflineTrackingEnabled(true);
measurement.setDebugLogging(true);
}
}
public static void trackItemBought() {
if (measurement != null) {
measurement = ADMS_Measurement.sharedInstance();
Hashtable<String, Object> contextData = new Hashtable<String, Object>();
contextData.put("Item Bought", "Item Bought");
measurement.setEvents("Item Bought");
measurement.track(contextData);
}
}
}

I can't vouch for the android code, but here are some general thoughts:
When you send a context data variable, you send a name-value pair, so I suspect this line:
contextData.put("Item Bought", "Item Bought");
Should be something more like this (I'm going to say the item being bought is a basketball):
contextData.put("Item Bought", "Basketball");
Then someone with access to processing rules in SiteCatalyst will need to configure a rule to say that that variable ("Item Bought") belongs to its assigned custom traffic variable (prop14, for instance). http://www.jasonegan.net/2011/04/07/omniture-sitecatalyst-15-context-variables-processing-rules/ talks about how to do that a bit, but the article generally assumes you have a certain level of knowledge of SiteCatalyst.

Just make the following change in your code.
public class TrackingHelper {
private static final String TRACKING_RSID = "MY_TRACKING_RSID";
private static final String TRACKING_SERVER = "MY_TRACKING_SERVER";
static ADMS_Measurement measurement = null;
public static void startActivity(Activity activity) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.startActivity(activity);
}
public static void stopActivity() {
measurement = ADMS_Measurement.sharedInstance();
measurement.stopActivity();
}
public static void configureAppMeasurement(Activity activity) {
if (measurement == null) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.configureMeasurement(TRACKING_RSID, TRACKING_SERVER);
measurement.setOfflineTrackingEnabled(true);
measurement.setDebugLogging(true);
}
}
public static void trackItemBought() {
if (measurement != null) {
measurement = ADMS_Measurement.sharedInstance();
Hashtable<String, Object> contextData = new Hashtable<String, Object>();
contextData.put("Item Bought", "Item Bought");
measurement.setEvents("Item Bought");
measurement.contextData(contextData);
measurement.track();
}
}

Related

convert protobuff class as model and store its value using realm

I am generating protobuf class using Squareup Wire protobuf libary
here is my proto file
syntax = "proto2";
package squareup.dinosaurs;
option java_package = "com.squareup.dinosaurs";
message Dinosaur {
// Common name of this dinosaur, like "Stegosaurus".
optional string name = 1;
// URLs with images of this dinosaur.
repeated string picture_urls = 2;
}
and here is my auto generated code
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: dinosaur/dinosaur.proto at 8:1
package com.squareup.dinosaurs;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import java.io.IOException;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.util.List;
import okio.ByteString;
public final class Dinosaur extends Message<Dinosaur, Dinosaur.Builder> {
public static final ProtoAdapter<Dinosaur> ADAPTER = new ProtoAdapter<Dinosaur>(FieldEncoding.LENGTH_DELIMITED, Dinosaur.class) {
#Override
public int encodedSize(Dinosaur value) {
return (value.name != null ? ProtoAdapter.STRING.encodedSizeWithTag(1, value.name) : 0)
+ ProtoAdapter.STRING.asRepeated().encodedSizeWithTag(2, value.picture_urls)
+ value.unknownFields().size();
}
#Override
public void encode(ProtoWriter writer, Dinosaur value) throws IOException {
if (value.name != null) ProtoAdapter.STRING.encodeWithTag(writer, 1, value.name);
if (value.picture_urls != null) ProtoAdapter.STRING.asRepeated().encodeWithTag(writer, 2, value.picture_urls);
writer.writeBytes(value.unknownFields());
}
#Override
public Dinosaur decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.name(ProtoAdapter.STRING.decode(reader)); break;
case 2: builder.picture_urls.add(ProtoAdapter.STRING.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
#Override
public Dinosaur redact(Dinosaur value) {
Builder builder = value.newBuilder();
builder.clearUnknownFields();
return builder.build();
}
};
private static final long serialVersionUID = 0L;
public static final String DEFAULT_NAME = "";
/**
* Common name of this dinosaur, like "Stegosaurus".
*/
public final String name;
/**
* URLs with images of this dinosaur.
*/
public final List<String> picture_urls;
public Dinosaur(String name, List<String> picture_urls) {
this(name, picture_urls, ByteString.EMPTY);
}
public Dinosaur(String name, List<String> picture_urls, ByteString unknownFields) {
super(unknownFields);
this.name = name;
this.picture_urls = immutableCopyOf("picture_urls", picture_urls);
}
#Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.name = name;
builder.picture_urls = copyOf("picture_urls", picture_urls);
builder.addUnknownFields(unknownFields());
return builder;
}
#Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Dinosaur)) return false;
Dinosaur o = (Dinosaur) other;
return equals(unknownFields(), o.unknownFields())
&& equals(name, o.name)
&& equals(picture_urls, o.picture_urls);
}
#Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (name != null ? name.hashCode() : 0);
result = result * 37 + (picture_urls != null ? picture_urls.hashCode() : 1);
super.hashCode = result;
}
return result;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (name != null) builder.append(", name=").append(name);
if (picture_urls != null) builder.append(", picture_urls=").append(picture_urls);
return builder.replace(0, 2, "Dinosaur{").append('}').toString();
}
public static final class Builder extends com.squareup.wire.Message.Builder<Dinosaur, Builder> {
public String name;
public List<String> picture_urls;
public Builder() {
picture_urls = newMutableList();
}
/**
* Common name of this dinosaur, like "Stegosaurus".
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* URLs with images of this dinosaur.
*/
public Builder picture_urls(List<String> picture_urls) {
checkElementsNotNull(picture_urls);
this.picture_urls = picture_urls;
return this;
}
#Override
public Dinosaur build() {
return new Dinosaur(name, picture_urls, buildUnknownFields());
}
}
}
now the issue is i want to directly store the value of Dinosaur into the database using Realm in android. i want Dinosaur class to act as a model.
but the problem is Dinosaur class is declared as final so i cant even derive it.
So is there any design pattern or way that exists to reuse or convert Dinosaur class into model?
You cannot use the Wire Dinosaur with Realm as Wire also require you to extend the Message class, while Realm require you to extend RealmObject.
If you want to combine the two you can create a RealmDinosaur class that accept the wire Dinosaur. Something like this:
public class RealmDinosaur extends RealmObject {
private String name;
private RealmList<RealmString> pictureUrls;
public RealmDinosaur(Dinosaur dino) {
// Fill Realm fields. Note that Realm doesn't support Lists
// with primitive strings yet.
// See https://realm.io/docs/java/latest/#primitive-lists
}
// getter and setters
}
realm.beginTransaction();
realm.copyToRealm(new RealmDinosaur(wireDinosaur));
realm.commitTransaction();
Short answer: no.
For me, this is one of several show-stoppers for wide adoption of Realm.
The developers of Realm don't seem to have considered real-world use-cases such as yours, where your data objects already inherit from something.
They also seem don't seem to get Android's threading requirements.
If you really want to use Realm, I think that you'll have to create another set of objects, likely in another package, that you only use with Realm. Then, you'd have to copy your data from your 'real' objects into the Realm objects.
Personally, for anything non-trivial, I'd either use the built-in SQLite, or find another database that better meets your needs.

Need help on simplify a list of strings

Can someone help me on simplfy this lists of strings:
public class MyList {
// New customers
public static final String mNew1 = "email1";
public static final String mNew2 = "email2";
public static final String mNew3 = "email3";
public static final String mNew4 = "email4";
// Old customers
public static final String mOld1 = "email1";
public static final String mOld2 = "email2";
public static final String mOld3 = "email3";
}
public class App extends Application {
public static boolean mIsNew = false;
public static boolean mIsOld = false;
Pattern emailPattern = Patterns.EMAIL_ADDRESS;
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
String possibleEmail = account.name;
if (MyList.mNew1.matches(possibleEmail) || MyList.mNew2.matches(possibleEmail) ||
MyList.mNew3.matches(possibleEmail) || MyList.mNew4.matches(possibleEmail)) {
mIsNew = true;
}
if (MyList.mOld1.matches(possibleEmail) || MyList.mOld2.matches(possibleEmail) || MyList.mOld3.matches(possibleEmail)) {
mIsOld = true;
}
}
}
Since the old customers email will be over 10.000 in less then a week can you suggest me an easy way to pick the strings from the MyList class and enable the right boolean? I.E if oneOfTheStringInThisList.matches(possibleEmail) mIsOld = true.
I'm not really familiar with lists of strings, sorry for my noob question! Thankyou!
You can get the values by reflection:
private void Something()
{
MyList list = new MyList();
HashSet<String> oldMails = new HashSet<String>();
HashSet<String> newMails = new HashSet<String>();
try {
GetAllMails(list, oldMails, newMails);
} catch (IllegalAccessException e) {
// TODO
}
boolean mIsOld = oldMails.contains("email4");
boolean mIsNew = newMails.contains("email4");
}
private void GetAllMails(MyList list, HashSet<String> oldMails, HashSet<String> newMails) throws IllegalAccessException
{
Field[] allFields = MyList.class.getDeclaredFields();
for(Field f : allFields)
{
if (f.getName().startsWith("mNew"))
{
newMails.add(f.get(list).toString());
}
else if (f.getName().startsWith("mOld"))
{
oldMails.add(f.get(list).toString());
}
}
}
You should store the HashSets in memory because reflection is not very performant.

When passing 2 identical Parcable Classes, 1 is null

i have a weird issue.
I'm trying to pass 2 parcable classesfrom one activity to another.
I define both of them the exact same way, but of them is null.
The parcable class :
class Friends implements Parcelable {
private ArrayList<Integer> ids = new ArrayList<Integer>();
private ArrayList<String> names = new ArrayList<String>();
private ArrayList<Bitmap> images = new ArrayList<Bitmap>();
public void addId(Integer id)
{
ids.add(id);
}
public void addName(String name){
names.add(name);
}
public void addImage(Bitmap img){
images.add(img);
}
public ArrayList<Integer> getIds() {
return ids;
}
public ArrayList<String> getNames() {
return names;
}
public ArrayList<Bitmap> getImages() {
return images;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(ids);
dest.writeList(names);
dest.writeList(images);
}
public static final Parcelable.Creator<Friends> CREATOR = new Parcelable.Creator<Friends>() {
public Friends createFromParcel(Parcel in) {
return new Friends(in);
}
public Friends[] newArray(int size) {
return new Friends[size];
}
};
public Friends(Parcel in){
in.readList(ids, null);
in.readList(names, null);
in.readList(images, null);
}
public Friends(Integer id, String name, Bitmap img) {
ids.add(id);
names.add(name);
images.add(img);
}
public Friends(){
}
The Sending part :
for(Integer position : selectedIds)
{
String name = a.getItem(position).getFriendName();
int id = a.getItem(position).getFriendId();
Bitmap img = a.getItem(position).getFriendImage();
Log.e("ID",String.valueOf(id));
selectedFriends.addId(new Integer(id));
selectedFriends.addName(name);
selectedFriends.addImage(img);
}
for(int position=0;position<list.getCount(); position++)
{
String name = a.getItem(position).getFriendName();
int id = a.getItem(position).getFriendId();
Bitmap img = a.getItem(position).getFriendImage();
Log.e("All IDs",String.valueOf(id));
allFriends.addId(new Integer(id));
allFriends.addName(name);
allFriends.addImage(img);
}
b.putParcelable("selecet_friends", selectedFriends);
b.putParcelable("all_friends", allFriends);
data.putExtras(b);
Both of the loops are being runned ( i can see the logs), all variables you don't see are being initialized correctly, everything is fine.
The Reciving part :
i define both as null :
private Friends selectedFriends = null;
private Friends allFriends = null;
And handle the onResult like this :
Log.e("Result","yessss");
Friends all_friends = (Friends)data.getParcelableExtra("all_friends");
Friends selected_friends = (Friends)data.getParcelableExtra("selected_friends");
allFriends = all_friends;
selectedFriends = selected_friends;
if(selectedFriends != null){
Log.e("is null","No");
}
if(allFriends != null){
Log.e("is all null","No");
}
Does anyone know how come the selectedFriends is null when allFriends is not?
EDIT:
Just a thought, but maybe it's because i put 2 parcables on a Bundle?
just i just add 2 bundles?
In the sending method you have a typo in this line:
b.putParcelable("selecet_friends", selectedFriends);
try this instead:
b.putParcelable("selected_friends", selectedFriends);
Also, you should use more specific names for the keys. The documentation for putExtras() says:
Add a set of extended data to the intent. The keys must include a
package prefix, for example the app com.android.contacts would use
names like "com.android.contacts.ShowAll

How to define prod/test launch scenarios for Android app?

I'm developing an Android app and I have different environments for testing and production releases (backend web server URL, debugging on/off, c2dm notifications e-mail account and a few others). I'd like to have those project configs to be stored in app structure in a convenient way (like xml file, not hardcoded in Java) and be able to switch between them with one switch (something like target="prod|test" in a place like AndroidManifest.xml. Any ideas?
You can add a xml file to the res folder and parse it when running.
<data target="prod">
<target id="prod">
<debug>off</debug>
</target>
<target id="test">
<debug>on</debug>
</target>
</data>
So you will only have to change the target attribute. However you would also have your debug settings in the final app which could be a security risk.
Better way would probably be a xml file that you exchange depending whether you have prod or test.
Thanks, Xeno, for your suggestion, actually I took a similar track, decided to keep the config settings in one XML file, and if there's any security concern about leaving it in the public release, I can remove unwanted entries as one of the release preparation steps (replacing XML files was exactly what didn't want to do).
Here are the details in case anyone needs a ready-to-implement solution in the future:
Usage — if using helper methods from Config.java, whenever you need a config setting value, you just write a line of code like Config.getInstance().getXxx(), e.g. Config.getInstance().getApiServer() to get the API server URL. Config XML file is parsed only once, on the first Config.getInstance() call.
I created config.xml file with current-env pointing to the currently active environment, devel-env and prod-env containing the actual config settings, and default-env containing common values for all environments (they can be overwritten in the particular environment sections:
<current-env>prod-env</current-env>
<devel-env>
<label>devel</label>
<api-server>http://monitor.test.xxx.com/</api-server>
<notification-email>testnotifications.xxx#gmail.com</notification-email>
</devel-env>
<prod-env>
<label></label>
<api-server>http://monitor.xxx.com/</api-server>
<notification-email>notifications.xxx#gmail.com</notification-email>
</prod-env>
<default-env>
<http-connection-timeout-millis>5000</http-connection-timeout-millis>
<http-so-timeout-millis>10000</http-so-timeout-millis>
<api-root>api15/</api-root>
<shared-data-key>com.xxx.monitor</shared-data-key>
</default-env>
I wrote Config.java class that parses the config.xml and returns config values for the current environment:
public class Config {
public static final String TAG_CONFIG_ROOT = "config";
public static final String TAG_CONFIG_PROD = "prod-env";
public static final String TAG_CONFIG_DEVEL = "devel-env";
public static final String TAG_CONFIG_DEFAULT = "default-env";
public static final String TAG_CURRENT_ENV = "current-env";
public static final String TAG_API_SERVER = "api-server";
public static final String TAG_API_ROOT = "api-root";
public static final String TAG_SHARED_DATA_KEY = "shared-data-key";
public static final String TAG_NOTIFICATION_EMAIL = "notification-email";
public static final String TAG_ENVIRONMENT_LABEL = "label";
public static final String TAG_HTTP_CONNECTION_TIMEOUT_MILLIS = "http-connection-timeout-millis";
public static final String TAG_HTTP_SO_TIMEOUT_MILLIS = "http-so-timeout-millis";
private static Config instance = null;
private XmlNode configRootNode = null;
public static Config getInstance(Context context) {
if (instance == null) {
try {
instance = new Config(context);
} catch (Exception e) {
Log.err("Error creating Config instance", e);
}
}
return instance;
}
private Config(Context context) throws XmlPullParserException, IOException {
readConfig(context);
}
private void readConfig(Context context) throws XmlPullParserException, IOException {
Resources res = context.getResources();
XmlResourceParser xpp = res.getXml(R.xml.config);
SimpleXmlDocumentModel xmlModel = new SimpleXmlDocumentModel();
configRootNode = xmlModel.parseXml(xpp);
return;
}
private String getCurrentEnv() {
return configRootNode.getChildValue(TAG_CURRENT_ENV);
}
private String getConfigValue(String key) {
String value;
XmlNode envNode = configRootNode.getChild(getCurrentEnv());
value = envNode.getChildValue(key);
if (value == null) {
value = getDefaultValue(key);
}
return value;
}
private String getDefaultValue(String key) {
String value = null;
XmlNode defaultNode = configRootNode.getChild(TAG_CONFIG_DEFAULT);
if (defaultNode != null) {
value = defaultNode.getChildValue(key);
}
return value;
}
public Integer getHttpConnectionTimeoutMillis() {
return StringUtil.parseInteger(getConfigValue(Config.TAG_HTTP_CONNECTION_TIMEOUT_MILLIS));
}
public Integer getHttpSoTimeoutMillis() {
return StringUtil.parseInteger(getConfigValue(Config.TAG_HTTP_SO_TIMEOUT_MILLIS));
}
public String getApiServer() {
return getConfigValue(Config.TAG_API_SERVER);
}
public String getApiRoot() {
return getConfigValue(Config.TAG_API_ROOT);
}
public String getSharedDataKey() {
return getConfigValue(Config.TAG_SHARED_DATA_KEY);
}
public String getNotificationEmail() {
return getConfigValue(Config.TAG_NOTIFICATION_EMAIL);
}
public String getEnvironmentLabel() {
return getConfigValue(Config.TAG_ENVIRONMENT_LABEL);
}
}
It uses 2 helper classes for xml parsing. Since they're pretty generic, I isolated them to a separate package so that they're reusable.
SimpleXmlDocumentModel.java:
public class SimpleXmlDocumentModel {
private XmlNode rootNode = null;
public XmlNode parseXml(XmlResourceParser xpp) throws XmlPullParserException, IOException {
XmlNode currentNode = null;
xpp.next();
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
String tagName = xpp.getName();
currentNode = addNode(currentNode, tagName);
} else if (eventType == XmlPullParser.END_TAG) {
currentNode = currentNode.parent;
} else if (eventType == XmlPullParser.TEXT) {
currentNode.text = xpp.getText();
}
eventType = xpp.next();
}
return rootNode;
}
private XmlNode addNode(XmlNode currentNode, String name) {
XmlNode newNode = new XmlNode();
newNode.parent = currentNode;
if (currentNode != null) {
if (currentNode.nodes == null) {
currentNode.nodes = new HashMap<String, XmlNode>();
}
currentNode.nodes.put(name, newNode);
} else {
rootNode = newNode;
}
currentNode = newNode;
return currentNode;
}
}
XmlNode.java:
public class XmlNode {
String text = null;
Map<String, XmlNode> nodes = null;
XmlNode parent = null;
public XmlNode getChild(String key) {
return (nodes != null) ? nodes.get(key) : null;
}
public String getChildValue(String key) {
String value = null;
if (nodes != null) {
XmlNode valueNode = nodes.get(key);
if (valueNode != null) {
value= valueNode.text;
}
}
return value;
}
}
The simple XML document model above doesn't support more complex entities or entity lists, but serves my purposes and is stupid simple.

Is there a way to add a badge to an application icon in Android?

On the iPhone, you can add a numbered badge to the application icon. On BlackBerry, I've successfully painted an image onto the application's icon while in the program. I want to do this for Android as well. I don't want to use the notification bar, as it's not something that needs to be notified instantly. Instead, I just want the user to be able to see how many new messages are in the application just by looking at the application icon.
Unfortunately, Android does not allow changing of the application icon because it's sealed in the APK once the program is compiled. There is no way to programmatically change it to a 'drawable'.
You may achieve your goal by using a widget instead of an icon. Widgets are highly customisable and can do what you want.
There's a short discussion about the difference between iPhone icon notification and using widgets here:
http://www.cnet.com/8301-19736_1-10278814-251.html
As you'll notice, there is virtually no difference between using a widget or an icon, since they can be the same size and look the same.
This can also be done for Sony's Xperia Home. I've blogged about it here, but the important parts are below. Sony devices use a class named BadgeReciever.
Declare the com.sonyericsson.home.permission.BROADCAST_BADGE permission in your manifest file:
Broadcast an Intent to the BadgeReceiver:
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", "com.yourdomain.yourapp.MainActivity");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", true);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", "99");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", "com.yourdomain.yourapp");
sendBroadcast(intent);
Done. Once this Intent is broadcast the launcher should show a badge on your application icon.
To remove the badge again, simply send a new broadcast, this time with SHOW_MESSAGE set to false:
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", false);
I've excluded details on how I found this to keep the answer short, but it's all available in the blog. Might be an interesting read for someone.
I've also posted a seperate SO question about this here and will add the full answer there once I'm allowed to (need 10 reputation to answer my own question within 8 hours).
ShortcutBadger library makes it possible and works with LG, Sony, Samsung, HTC and other custom Launchers.
It even has a way to display Badge Count in Pure Android devices desktop.
Updating the Badge Count in the application icon is as easy as calling:
int badgeCount = 1;
ShortcutBadger.setBadge(getApplicationContext(), badgeCount);
It includes a demo application that allows you to test its behaviour.
OR
you can also try activity-alias to do so, but in this you need to create different icons with badge values ,it will work great in case- you need to switch between 2 different App icons (need to create different activity-alias for displaying different icon i.e more icons = more activity-alias).
Asus devices:
public static class AsusHomeBadger implements Badger {
private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";
#Override
public void executeBadge(int badgeCount) {
final Intent intent = new Intent(INTENT_ACTION);
intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
intent.putExtra("badge_vip_count", 0);
if (canResolveBroadcast(intent)) {
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(intent);
}
});
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.asus.launcher");
}
}
Huawei devices:
public static class HuaweiHomeBadger implements Badger {
#Override
public void executeBadge(int badgeCount) {
final Bundle localBundle = new Bundle();
localBundle.putString("package", ApplicationLoader.applicationContext.getPackageName());
localBundle.putString("class", componentName.getClassName());
localBundle.putInt("badgenumber", badgeCount);
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
try {
ApplicationLoader.applicationContext.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, localBundle);
} catch (Exception e) {
FileLog.e(e);
}
}
});
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList(
"com.huawei.android.launcher"
);
}
}
HTC devices:
public static class NewHtcHomeBadger implements Badger {
public static final String INTENT_UPDATE_SHORTCUT = "com.htc.launcher.action.UPDATE_SHORTCUT";
public static final String INTENT_SET_NOTIFICATION = "com.htc.launcher.action.SET_NOTIFICATION";
public static final String PACKAGENAME = "packagename";
public static final String COUNT = "count";
public static final String EXTRA_COMPONENT = "com.htc.launcher.extra.COMPONENT";
public static final String EXTRA_COUNT = "com.htc.launcher.extra.COUNT";
#Override
public void executeBadge(int badgeCount) {
final Intent intent1 = new Intent(INTENT_SET_NOTIFICATION);
intent1.putExtra(EXTRA_COMPONENT, componentName.flattenToShortString());
intent1.putExtra(EXTRA_COUNT, badgeCount);
final Intent intent = new Intent(INTENT_UPDATE_SHORTCUT);
intent.putExtra(PACKAGENAME, componentName.getPackageName());
intent.putExtra(COUNT, badgeCount);
if (canResolveBroadcast(intent1) || canResolveBroadcast(intent)) {
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(intent1);
ApplicationLoader.applicationContext.sendBroadcast(intent);
}
});
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.htc.launcher");
}
}
Samsung devices:
public static class SamsungHomeBadger implements Badger {
private static final String CONTENT_URI = "content://com.sec.badge/apps?notify=true";
private static final String[] CONTENT_PROJECTION = new String[]{"_id","class"};
private static DefaultBadger defaultBadger;
#Override
public void executeBadge(int badgeCount) {
try {
if (defaultBadger == null) {
defaultBadger = new DefaultBadger();
}
defaultBadger.executeBadge(badgeCount);
} catch (Exception ignore) {
}
Uri mUri = Uri.parse(CONTENT_URI);
ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver();
Cursor cursor = null;
try {
cursor = contentResolver.query(mUri, CONTENT_PROJECTION, "package=?", new String[]{componentName.getPackageName()}, null);
if (cursor != null) {
String entryActivityName = componentName.getClassName();
boolean entryActivityExist = false;
while (cursor.moveToNext()) {
int id = cursor.getInt(0);
ContentValues contentValues = getContentValues(componentName, badgeCount, false);
contentResolver.update(mUri, contentValues, "_id=?", new String[]{String.valueOf(id)});
if (entryActivityName.equals(cursor.getString(cursor.getColumnIndex("class")))) {
entryActivityExist = true;
}
}
if (!entryActivityExist) {
ContentValues contentValues = getContentValues(componentName, badgeCount, true);
contentResolver.insert(mUri, contentValues);
}
}
} finally {
close(cursor);
}
}
private ContentValues getContentValues(ComponentName componentName, int badgeCount, boolean isInsert) {
ContentValues contentValues = new ContentValues();
if (isInsert) {
contentValues.put("package", componentName.getPackageName());
contentValues.put("class", componentName.getClassName());
}
contentValues.put("badgecount", badgeCount);
return contentValues;
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList(
"com.sec.android.app.launcher",
"com.sec.android.app.twlauncher"
);
}
}
Sony devices:
public static class SonyHomeBadger implements Badger {
private static final String INTENT_ACTION = "com.sonyericsson.home.action.UPDATE_BADGE";
private static final String INTENT_EXTRA_PACKAGE_NAME = "com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME";
private static final String INTENT_EXTRA_MESSAGE = "com.sonyericsson.home.intent.extra.badge.MESSAGE";
private static final String INTENT_EXTRA_SHOW_MESSAGE = "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE";
private static final String PROVIDER_CONTENT_URI = "content://com.sonymobile.home.resourceprovider/badge";
private static final String PROVIDER_COLUMNS_BADGE_COUNT = "badge_count";
private static final String PROVIDER_COLUMNS_PACKAGE_NAME = "package_name";
private static final String PROVIDER_COLUMNS_ACTIVITY_NAME = "activity_name";
private static final String SONY_HOME_PROVIDER_NAME = "com.sonymobile.home.resourceprovider";
private final Uri BADGE_CONTENT_URI = Uri.parse(PROVIDER_CONTENT_URI);
private static AsyncQueryHandler mQueryHandler;
#Override
public void executeBadge(int badgeCount) {
if (sonyBadgeContentProviderExists()) {
executeBadgeByContentProvider(badgeCount);
} else {
executeBadgeByBroadcast(badgeCount);
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.sonyericsson.home", "com.sonymobile.home");
}
private static void executeBadgeByBroadcast(int badgeCount) {
final Intent intent = new Intent(INTENT_ACTION);
intent.putExtra(INTENT_EXTRA_PACKAGE_NAME, componentName.getPackageName());
intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
intent.putExtra(INTENT_EXTRA_MESSAGE, String.valueOf(badgeCount));
intent.putExtra(INTENT_EXTRA_SHOW_MESSAGE, badgeCount > 0);
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(intent);
}
});
}
private void executeBadgeByContentProvider(int badgeCount) {
if (badgeCount < 0) {
return;
}
if (mQueryHandler == null) {
mQueryHandler = new AsyncQueryHandler(ApplicationLoader.applicationContext.getApplicationContext().getContentResolver()) {
#Override
public void handleMessage(Message msg) {
try {
super.handleMessage(msg);
} catch (Throwable ignore) {
}
}
};
}
insertBadgeAsync(badgeCount, componentName.getPackageName(), componentName.getClassName());
}
private void insertBadgeAsync(int badgeCount, String packageName, String activityName) {
final ContentValues contentValues = new ContentValues();
contentValues.put(PROVIDER_COLUMNS_BADGE_COUNT, badgeCount);
contentValues.put(PROVIDER_COLUMNS_PACKAGE_NAME, packageName);
contentValues.put(PROVIDER_COLUMNS_ACTIVITY_NAME, activityName);
mQueryHandler.startInsert(0, null, BADGE_CONTENT_URI, contentValues);
}
private static boolean sonyBadgeContentProviderExists() {
boolean exists = false;
ProviderInfo info = ApplicationLoader.applicationContext.getPackageManager().resolveContentProvider(SONY_HOME_PROVIDER_NAME, 0);
if (info != null) {
exists = true;
}
return exists;
}
}
Xiaomi devices:
public static class XiaomiHomeBadger implements Badger {
public static final String INTENT_ACTION = "android.intent.action.APPLICATION_MESSAGE_UPDATE";
public static final String EXTRA_UPDATE_APP_COMPONENT_NAME = "android.intent.extra.update_application_component_name";
public static final String EXTRA_UPDATE_APP_MSG_TEXT = "android.intent.extra.update_application_message_text";
#Override
public void executeBadge(int badgeCount) {
try {
Class miuiNotificationClass = Class.forName("android.app.MiuiNotification");
Object miuiNotification = miuiNotificationClass.newInstance();
Field field = miuiNotification.getClass().getDeclaredField("messageCount");
field.setAccessible(true);
field.set(miuiNotification, String.valueOf(badgeCount == 0 ? "" : badgeCount));
} catch (Throwable e) {
final Intent localIntent = new Intent(INTENT_ACTION);
localIntent.putExtra(EXTRA_UPDATE_APP_COMPONENT_NAME, componentName.getPackageName() + "/" + componentName.getClassName());
localIntent.putExtra(EXTRA_UPDATE_APP_MSG_TEXT, String.valueOf(badgeCount == 0 ? "" : badgeCount));
if (canResolveBroadcast(localIntent)) {
AndroidUtilities.runOnUIThread(new Runnable() {
#Override
public void run() {
ApplicationLoader.applicationContext.sendBroadcast(localIntent);
}
});
}
}
}
#Override
public List<String> getSupportLaunchers() {
return Arrays.asList(
"com.miui.miuilite",
"com.miui.home",
"com.miui.miuihome",
"com.miui.miuihome2",
"com.miui.mihome",
"com.miui.mihome2"
);
}
}
As of API 26, this is now officially supported:
Starting with 8.0 (API level 26), notification badges (also known as notification dots) appear on a launcher icon when the associated app has an active notification. Users can long-press on the app icon to reveal the notifications (alongside any app shortcuts), as shown in figure 1.
These dots appear by default in launcher apps that support them and there's nothing your app needs to do. However, there might be situations in which you don't want the to notification dot to appear or you want to control exactly which notifications to appear there.
To set a custom number, call setNumber() on the notification:
mNotification.setNumber(messageCount)
Here's how to do it for:
Apex Launcher
Nova Launcer
I think there's also a way to do it on the LG launcher, but haven't found out how yet.

Categories

Resources