I have an activity that loads an external url into a webview within my app. I'd like to use Chrome Custom tabs when it's available but I support devices that might not have a version of Chrome that supports them.
In the case of CustomTabs not being supported I'd like to use my old code but use the CustomTabsIntent.Builder() when they are. The old code loads the content in a WebView contained in an Activity where I can still manage the ActionBar.
I'd like to write a helper method that will tell me if it's supported but I'm not sure how. The info on the developer page is pretty slim:
https://developer.chrome.com/multidevice/android/customtabs
It says if you bind succeeds the custom tabs can be safely used. Is there an easy way to bind to test this?
Like this I assume:
Intent serviceIntent = new Intent("android.support.customtabs.action.CustomTabsService");
serviceIntent.setPackage("com.android.chrome");
boolean customTabsSupported = bindService(serviceIntent, new CustomTabsServiceConnection() {
#Override
public void onCustomTabsServiceConnected(final ComponentName componentName, final CustomTabsClient customTabsClient) {}
#Override
public void onServiceDisconnected(final ComponentName name) {}
},
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
if (customTabsSupported) {
// is supported
}
Instead of binding and unbinding the service, you can use the PackageManager to check if Custom Tabs is supported.
private static final String SERVICE_ACTION = "android.support.customtabs.action.CustomTabsService";
private static final String CHROME_PACKAGE = "com.android.chrome";
private static boolean isChromeCustomTabsSupported(#NonNull final Context context) {
Intent serviceIntent = new Intent(SERVICE_ACTION);
serviceIntent.setPackage(CHROME_PACKAGE);
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentServices(serviceIntent, 0);
return !(resolveInfos == null || resolveInfos.isEmpty());
}
Be aware that other browsers may support Custom Tabs in the future, so you may want to modify that to support this case.
You can try following code to figure out if you have a browser that supports custom tab:
private static final String TAG = "CustomTabLauncher";
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
String mPackageNameToUse;
private String getPackageName(Context context) {
if (mPackageNameToUse != null) {
return mPackageNameToUse;
}
// Get default VIEW intent handler that can view a web url.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.test-url.com"));
// Get all apps that can handle VIEW intents.
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
mPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
mPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
mPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
mPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
mPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
mPackageNameToUse = LOCAL_PACKAGE;
}
return mPackageNameToUse;
}
When calling, you can do something like this:
public void openCustomTab(Uri uri, Activity activity) {
//If we cant find a package name, it means there's no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the default browser
if (getPackageName(activity) == null) {
activity.startActivity(new Intent(Intent.ACTION_VIEW, uri));
} else {
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
intentBuilder.enableUrlBarHiding();
intentBuilder.setToolbarColor(activity.getResources().getColor(R.color.purple_a_01));
CustomTabsIntent customTabsIntent = intentBuilder.build();
customTabsIntent.intent.setPackage(mPackageNameToUse);
customTabsIntent.launchUrl(activity, uri);
}
}
I ended up writing a static method in my Utils class so I can check and handle the case where it isn't supported:
/**
* Check if Chrome CustomTabs are supported.
* Some devices don't have Chrome or it may not be
* updated to a version where custom tabs is supported.
*
* #param context the context
* #return whether custom tabs are supported
*/
public static boolean isChromeCustomTabsSupported(#NonNull final Context context) {
Intent serviceIntent = new Intent("android.support.customtabs.action.CustomTabsService");
serviceIntent.setPackage("com.android.chrome");
CustomTabsServiceConnection serviceConnection = new CustomTabsServiceConnection() {
#Override
public void onCustomTabsServiceConnected(final ComponentName componentName, final CustomTabsClient customTabsClient) { }
#Override
public void onServiceDisconnected(final ComponentName name) { }
};
boolean customTabsSupported =
context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
context.unbindService(serviceConnection);
return customTabsSupported;
}
I solved this problem by handling ActivityNotFound exception in catch block.
The trick is to check if the browser activity of chrome can be started or not, if it can't be started or throws an exception then simply open the link through Intent.ACTION_VIEW.
Here is all the relevant code ....
private void onBtnLinkClicked(View v, int pos) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
openCustomTab(url);
} else {
openBrowserActivity(url);
}
} catch (Exception e) {
e.printStackTrace();
openBrowserActivity(url);
}
}
private void openBrowserActivity(String url) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(browserIntent);
}
What is openCustomTab(url) you say :
Here is the relevant code for it.
private void openCustomTab(String url) {
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
int color = context.getResources().getColor(R.color.colorPrimary);
intentBuilder.setToolbarColor(color);
intentBuilder.setShowTitle(true);
String menuItemTitle = context.getString(R.string.menu_title_share);
PendingIntent menuItemPendingIntent = createPendingShareIntent();
intentBuilder.addMenuItem(menuItemTitle, menuItemPendingIntent);
intentBuilder.setStartAnimations(context,
R.anim.slide_in_right, R.anim.slide_out_left);
intentBuilder.setExitAnimations(context,
android.R.anim.slide_in_left, android.R.anim.slide_out_right);
CustomTabActivityHelper.openCustomTab(
activity, intentBuilder.build(), Uri.parse(url), new WebviewFallback());
}
My style of answer may seem cocky but before clicking downvote let me know if you have run into any unexpected bug or any other problem that this approach may have cause. Do give your feedback, we are a community afterall.
New issues may arise if you are targeting API level 30 and running on an Android 11 device.
You will need to add a section to your manifest, such as:
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
OR
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
Without this, some of the calls to PackageManager mentioned in the above posts will not work as you expect.
https://developer.android.com/training/package-visibility/use-cases
From developer site of Chrome, I found the followings -
As of Chrome 45, Chrome Custom Tabs is now generally available to all
users of Chrome, on all of Chrome's supported Android versions
(Jellybean onwards).
Link: https://developer.chrome.com/multidevice/android/customtabs#whencaniuseit
So, I checked whether Chrome supports Chrome Custom Tab by version.
Check my code:
String chromePackageName = "com.android.chrome";
int chromeTargetVersion = 45;
boolean isSupportCustomTab = false;
try {
String chromeVersion = getApplicationContext().getPackageManager().getPackageInfo(chromePackageName, 0).versionName;
if(chromeVersion.contains(".")) {
chromeVersion = chromeVersion.substring(0, chromeVersion.indexOf('.'));
}
isSupportCustomTab = (Integer.valueOf(chromeVersion) >= chromeTargetVersion);
} catch (PackageManager.NameNotFoundException ex) {
} catch (Exception ex) { }
if (isSupportCustomTab) {
//Use Chrome Custom Tab
} else {
//Use WebView or other Browser
}
I don't know how efficient it is, just want to share.
Related
This question already has answers here:
Is there a way to add a badge to an application icon in Android?
(6 answers)
Closed 5 years ago.
samsung galaxy note 2 android version 4.1.2
I know that this question was asked before and the reply was not possible
How to display balloon counter over application launcher icon on
android
Nevertheless yesterday I updated the facebook app and it started to show a counter of unread messages private messages. How come facebook app can and I cant do so for my app?
samsung galaxy note 2 android version 4.1.2
Android ("vanilla" android without custom launchers and touch interfaces) does not allow changing of the application icon, because it is sealed in the .apk tightly once the program is compiled. There is no way to change it to a 'drawable' programmatically using standard APIs. You may achieve your goal by using a widget instead of an icon. Widgets are customisable. Please read this :http://www.cnet.com/8301-19736_1-10278814-251.html and this http://developer.android.com/guide/topics/appwidgets/index.html.
Also look here: https://github.com/jgilfelt/android-viewbadger. It can help you.
As for badge numbers. As I said before - there is no standard way for doing this. But we all know that Android is an open operating system and we can do everything we want with it, so the only way to add a badge number - is either to use some 3-rd party apps or custom launchers, or front-end touch interfaces: Samsung TouchWiz or Sony Xperia's interface. Other answers use this capabilities and you can search for this on stackoverflow, e.g. here. But I will repeat one more time: there is no standard API for this and I want to say it is a bad practice. App's icon notification badge is an iOS pattern and it should not be used in Android apps anyway. In Andrioid there is a status bar notifications for these purposes:http://developer.android.com/guide/topics/ui/notifiers/notifications.html
So, if Facebook or someone other use this - it is not a common pattern or trend we should consider. But if you insist anyway and don't want to use home screen widgets then look here, please:
How does Facebook add badge numbers on app icon in Android?
As you see this is not an actual Facebook app it's TouchWiz. In vanilla android this can be achieved with Nova Launcher http://forums.androidcentral.com/android-applications/199709-how-guide-global-badge-notifications.html
So if you will see icon badges somewhere, be sure it is either a 3-rd party launcher or touch interface (frontend wrapper). May be sometime Google will add this capability to the standard Android API.
It works in samsung touchwiz launcher
public static void setBadge(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
}
public static String getLauncherClassName(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfos) {
String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
if (pkgName.equalsIgnoreCase(context.getPackageName())) {
String className = resolveInfo.activityInfo.name;
return className;
}
}
return null;
}
ShortcutBadger is a library that adds an abstraction layer over the device brand and current launcher and offers a great result. 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.applyCount(context, badgeCount);
It includes a demo application that allows you to test its behavior.
I have figured out how this is done for Sony devices.
I've blogged about it here. I've also posted a seperate SO question about this here.
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.
This is sample and best way for showing badge on notification launcher icon.
Add This Class in your application
public class BadgeUtils {
public static void setBadge(Context context, int count) {
setBadgeSamsung(context, count);
setBadgeSony(context, count);
}
public static void clearBadge(Context context) {
setBadgeSamsung(context, 0);
clearBadgeSony(context);
}
private static void setBadgeSamsung(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
}
private static void setBadgeSony(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", true);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", String.valueOf(count));
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
context.sendBroadcast(intent);
}
private static void clearBadgeSony(Context context) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", false);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", String.valueOf(0));
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
context.sendBroadcast(intent);
}
private static String getLauncherClassName(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfos) {
String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
if (pkgName.equalsIgnoreCase(context.getPackageName())) {
String className = resolveInfo.activityInfo.name;
return className;
}
}
return null;
}
}
==> MyGcmListenerService.java
Use BadgeUtils class when notification comes.
public class MyGcmListenerService extends GcmListenerService {
private static final String TAG = "MyGcmListenerService";
#Override
public void onMessageReceived(String from, Bundle data) {
String message = data.getString("Msg");
String Type = data.getString("Type");
Intent intent = new Intent(this, SplashActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.BigTextStyle bigTextStyle= new NotificationCompat.BigTextStyle();
bigTextStyle .setBigContentTitle(getString(R.string.app_name))
.bigText(message);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(getNotificationIcon())
.setContentTitle(getString(R.string.app_name))
.setContentText(message)
.setStyle(bigTextStyle)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
int color = getResources().getColor(R.color.appColor);
notificationBuilder.setColor(color);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int unOpenCount=AppUtill.getPreferenceInt("NOTICOUNT",this);
unOpenCount=unOpenCount+1;
AppUtill.savePreferenceLong("NOTICOUNT",unOpenCount,this);
notificationManager.notify(unOpenCount /* ID of notification */, notificationBuilder.build());
// This is for bladge on home icon
BadgeUtils.setBadge(MyGcmListenerService.this,(int)unOpenCount);
}
private int getNotificationIcon() {
boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
return useWhiteIcon ? R.drawable.notification_small_icon : R.drawable.icon_launcher;
}
}
And clear notification from preference and also with badge count
public class SplashActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
AppUtill.savePreferenceLong("NOTICOUNT",0,this);
BadgeUtils.clearBadge(this);
}
}
<uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE" />
In creating a watch face for Android Wear, I would like to have a simple configuration (a toggle switch?) that set which mode the user wanted the watch face to looks like (for example, white or black watch face).
I would prefer the toggle switch to be on the watch itself, prefer not to communicate with the phone for such simple action, and hopefully to avoid all the GoogleApiClient communications between watch and phones. Is there a way to do this easily, similar to doing Settings or SharedPreferences on Android?
I tried using a Broadcast receiver. I can get the changes in Broadcast receiver, but how do I get the CanvasWatchFaceService.Engine to update?
Yes, that's possible.
You have to follow this documentation.
First Create an Activity displaying the settings you want the user to change.
Then in your Manifest file, add this meta data to your Watchface service:
<meta-data
android:name=
"com.google.android.wearable.watchface.wearableConfigurationAction"
android:value=
"com.example.android.wearable.watchface.CONFIG_DIGITAL" />
And this IntentFilter to your Activity:
<intent-filter>
<action android:name=
"com.example.android.wearable.watchface.CONFIG_DIGITAL" />
<category android:name=
"com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Of course, you will have to replace "com.example.android" by your package name.
Then a small setting icon will appear below your watchface preview in the Watchface selector screen.
Do not forget to synchronize the setting between your Activity and Watchface in order to make it appear instantly (with a BroadcastReceiver for example)
I addressed this with a LocalBroadcastManager that registers 3 intents
Get Initial Data, sent from Config Activity, expected by Watch Service
Initial Data, sent by Watch Service in response to the message above
Data Changed, sent by Config Activity when user makes selections.
Everything is wrapped in a single class which exposes two interfaces for interactions (one for Watch Service, the other for Config Activity. Probably not the easiest solution but the best I could come up with after 3 days of digging :(
For the record, here is the class sharing 2 variables (bezelMode and time24).
You will need to instantiate it from your watch service (implementing WatchConfig.Service) and you configuration activity (implementing WatchConfig.Editor)
Communication is based on LocalBroadcastManager
public class WatchConfig {
// private static final String TAG = "Config";
// Used when data has changed
public static final String CONFIG_DATA_CHANGED = "/config/changed";
// Used to provide initial data
public static final String CONFIG_INITIAL_DATA = "/config/inital-data";
// Used to query initial data
public static final String CONFIG_INITIAL_QUERY = "/config/initial-query";
private int m_BezelMode;
private boolean m_Time24;
private LocalBroadcastManager localBroadcastManager;
BroadcastReceiver broadcastReceiverDataChanged;
BroadcastReceiver broadcastReceiverInitialDataRequest;
BroadcastReceiver broadcastReceiverInitialData;
private Service service;
private Editor editor;
WatchConfig(Context context, Service service) {
initialize( context, service, null);
}
WatchConfig(Context context, Editor editor) {
initialize( context, null, editor);
}
void initialize( Context context, Service service, Editor editor) {
this.localBroadcastManager = LocalBroadcastManager.getInstance( context);
this.service = service;
this.editor = editor;
}
interface Service {
void onConfigDataUpdated(boolean time24, int bezelMode);
void onConfigInitialRequest();
}
interface Editor {
void onConfigInitialize(boolean time24, int bezelMode);
}
/**
* Registers all proper receivers
*/
public void connect() {
if( this.service != null) {
IntentFilter intentFilterDataChanged = new IntentFilter(CONFIG_DATA_CHANGED);
this.broadcastReceiverDataChanged = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG,"Data Changed Notification");
service.onConfigDataUpdated(intent.getBooleanExtra("time24", true), intent.getIntExtra("bezel", 24));
}
};
this.localBroadcastManager.registerReceiver(broadcastReceiverDataChanged, intentFilterDataChanged);
IntentFilter intentFilterInitialDataRequesy = new IntentFilter(CONFIG_INITIAL_QUERY);
this.broadcastReceiverInitialDataRequest = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG,"Initial Query Notification");
service.onConfigInitialRequest();
}
};
this.localBroadcastManager.registerReceiver(broadcastReceiverInitialDataRequest, intentFilterInitialDataRequesy);
} else {
this.broadcastReceiverDataChanged = null;
this.broadcastReceiverInitialDataRequest = null;
}
if( this.editor != null) {
IntentFilter intentFilterInitalData = new IntentFilter(CONFIG_INITIAL_DATA);
this.broadcastReceiverInitialData = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG,"Initial Data notification");
editor.onConfigInitialize(intent.getBooleanExtra("time24", true), intent.getIntExtra("bezel", 24));
}
};
this.localBroadcastManager.registerReceiver(broadcastReceiverInitialData, intentFilterInitalData);
// Editors need intial data
Intent intentInitialDataRequest = new Intent( CONFIG_INITIAL_QUERY);
this.localBroadcastManager.sendBroadcast( intentInitialDataRequest);
} else {
this.broadcastReceiverInitialData = null;
}
}
public void disconnect() {
if( this.broadcastReceiverDataChanged != null) {
this.localBroadcastManager.unregisterReceiver(this.broadcastReceiverDataChanged);
}
if( this.broadcastReceiverInitialDataRequest != null) {
this.localBroadcastManager.unregisterReceiver(this.broadcastReceiverInitialDataRequest);
}
if( this.broadcastReceiverInitialData != null) {
this.localBroadcastManager.unregisterReceiver(this.broadcastReceiverInitialData);
}
}
/**
* Used to publish changes in configuration
*/
protected void publishInitialData(boolean time24, int bezel) {
this.m_Time24 = time24;
this.m_BezelMode = bezel;
Intent intent = new Intent( CONFIG_INITIAL_DATA);
intent.putExtra("time24", this.m_Time24);
intent.putExtra("bezel", this.m_BezelMode);
this.localBroadcastManager.sendBroadcast(intent);
}
/**
* Used to publish changes in configuration
*/
protected void publishUpdate() {
Intent intent = new Intent( CONFIG_DATA_CHANGED);
intent.putExtra("time24", this.m_Time24);
intent.putExtra("bezel", this.m_BezelMode);
this.localBroadcastManager.sendBroadcast(intent);
}
public void setTime24(boolean time24) {
this.m_Time24 = time24;
}
public void setBezelMode(int bezelMode) {
this.m_BezelMode = bezelMode;
}
}
This question already has answers here:
Is there a way to add a badge to an application icon in Android?
(6 answers)
Closed 5 years ago.
samsung galaxy note 2 android version 4.1.2
I know that this question was asked before and the reply was not possible
How to display balloon counter over application launcher icon on
android
Nevertheless yesterday I updated the facebook app and it started to show a counter of unread messages private messages. How come facebook app can and I cant do so for my app?
samsung galaxy note 2 android version 4.1.2
Android ("vanilla" android without custom launchers and touch interfaces) does not allow changing of the application icon, because it is sealed in the .apk tightly once the program is compiled. There is no way to change it to a 'drawable' programmatically using standard APIs. You may achieve your goal by using a widget instead of an icon. Widgets are customisable. Please read this :http://www.cnet.com/8301-19736_1-10278814-251.html and this http://developer.android.com/guide/topics/appwidgets/index.html.
Also look here: https://github.com/jgilfelt/android-viewbadger. It can help you.
As for badge numbers. As I said before - there is no standard way for doing this. But we all know that Android is an open operating system and we can do everything we want with it, so the only way to add a badge number - is either to use some 3-rd party apps or custom launchers, or front-end touch interfaces: Samsung TouchWiz or Sony Xperia's interface. Other answers use this capabilities and you can search for this on stackoverflow, e.g. here. But I will repeat one more time: there is no standard API for this and I want to say it is a bad practice. App's icon notification badge is an iOS pattern and it should not be used in Android apps anyway. In Andrioid there is a status bar notifications for these purposes:http://developer.android.com/guide/topics/ui/notifiers/notifications.html
So, if Facebook or someone other use this - it is not a common pattern or trend we should consider. But if you insist anyway and don't want to use home screen widgets then look here, please:
How does Facebook add badge numbers on app icon in Android?
As you see this is not an actual Facebook app it's TouchWiz. In vanilla android this can be achieved with Nova Launcher http://forums.androidcentral.com/android-applications/199709-how-guide-global-badge-notifications.html
So if you will see icon badges somewhere, be sure it is either a 3-rd party launcher or touch interface (frontend wrapper). May be sometime Google will add this capability to the standard Android API.
It works in samsung touchwiz launcher
public static void setBadge(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
}
public static String getLauncherClassName(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfos) {
String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
if (pkgName.equalsIgnoreCase(context.getPackageName())) {
String className = resolveInfo.activityInfo.name;
return className;
}
}
return null;
}
ShortcutBadger is a library that adds an abstraction layer over the device brand and current launcher and offers a great result. 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.applyCount(context, badgeCount);
It includes a demo application that allows you to test its behavior.
I have figured out how this is done for Sony devices.
I've blogged about it here. I've also posted a seperate SO question about this here.
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.
This is sample and best way for showing badge on notification launcher icon.
Add This Class in your application
public class BadgeUtils {
public static void setBadge(Context context, int count) {
setBadgeSamsung(context, count);
setBadgeSony(context, count);
}
public static void clearBadge(Context context) {
setBadgeSamsung(context, 0);
clearBadgeSony(context);
}
private static void setBadgeSamsung(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
}
private static void setBadgeSony(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", true);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", String.valueOf(count));
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
context.sendBroadcast(intent);
}
private static void clearBadgeSony(Context context) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
Intent intent = new Intent();
intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", false);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", String.valueOf(0));
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
context.sendBroadcast(intent);
}
private static String getLauncherClassName(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfos) {
String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
if (pkgName.equalsIgnoreCase(context.getPackageName())) {
String className = resolveInfo.activityInfo.name;
return className;
}
}
return null;
}
}
==> MyGcmListenerService.java
Use BadgeUtils class when notification comes.
public class MyGcmListenerService extends GcmListenerService {
private static final String TAG = "MyGcmListenerService";
#Override
public void onMessageReceived(String from, Bundle data) {
String message = data.getString("Msg");
String Type = data.getString("Type");
Intent intent = new Intent(this, SplashActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.BigTextStyle bigTextStyle= new NotificationCompat.BigTextStyle();
bigTextStyle .setBigContentTitle(getString(R.string.app_name))
.bigText(message);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(getNotificationIcon())
.setContentTitle(getString(R.string.app_name))
.setContentText(message)
.setStyle(bigTextStyle)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
int color = getResources().getColor(R.color.appColor);
notificationBuilder.setColor(color);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int unOpenCount=AppUtill.getPreferenceInt("NOTICOUNT",this);
unOpenCount=unOpenCount+1;
AppUtill.savePreferenceLong("NOTICOUNT",unOpenCount,this);
notificationManager.notify(unOpenCount /* ID of notification */, notificationBuilder.build());
// This is for bladge on home icon
BadgeUtils.setBadge(MyGcmListenerService.this,(int)unOpenCount);
}
private int getNotificationIcon() {
boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
return useWhiteIcon ? R.drawable.notification_small_icon : R.drawable.icon_launcher;
}
}
And clear notification from preference and also with badge count
public class SplashActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
AppUtill.savePreferenceLong("NOTICOUNT",0,this);
BadgeUtils.clearBadge(this);
}
}
<uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE" />
I'm having some issues adding widgets to the homescreen in a custom Launcher I've been working on.
I've been able to generate a list of widgets to add with an AppWidgetManager and I've developed the workflow for adding the widget to my home screen. The code is not quite what is below but looks something like the following:
AppWidgetHost widget_host = new AppWidgetHost(this, 1);
AppWidgetManager widget_manager = AppWidgetManager.getInstance(this);
int widget_id = widget_host.allocateAppWidgetId();
AppWidgetProviderInfo widget_provider = ... //from an array;
Intent bindIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
bindIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget_id);
bindIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, widget_provider.provider);
startActivityForResult(bindIntent, REQUEST_BIND_APPWIDGET);
if (widget_provider.configure != null) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
intent.setComponent(widget_provider.configure);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget_id);
startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
} else {
createWidget(widget_id);
}
I then have an onActivityResult method that leads to the configuration of widgets if necessary, and the createWidget method uses the createView method of the AppWidgetHost.
This workflow works but the ACTION_APPWIDGET_BIND intent asks the user for permission to bind the app, which is kind of annoying. My understanding is that only system apps can request this permission and that I am out of luck in binding widgets without asking for this permission while the app is running. On the other hand, I know there are many other launchers out there and they can all add widgets seamlessly, so there must be another approach to this.
Any advice would be greatly appreciated!
Cheers
Hope that the question is still open...
You do too much things within you method. Under specific circumstances you fire to events shorty one after the other. I am not working too long on android, so I can not tell you, whether this may be ok or not.
And you always fire the intent here:
Intent bindIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
bindIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget_id);
bindIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, widget_provider.provider);
startActivityForResult(bindIntent, REQUEST_BIND_APPWIDGET);
The intent above causes the question most probarbly. You may check beforehand, whether you need to ask for permission or not. You can ask with this routine:
Boolean callProviderIntent = false;
if (checkCallProviderIntent)
{
callProviderIntent = true;
Method m = null;
try
{
m = AppWidgetManager.class
.getMethod("bindAppWidgetIdIfAllowed", new Class[]
{ Integer.TYPE, ComponentName.class });
}
catch (NoSuchMethodException e)
{
}
if (m != null)
{
try
{
callProviderIntent = !(Boolean) m
.invoke(mAppWidgetManager,
appWidgetId,
launcherAppWidgetInfo.provider);
}
catch (Exception e)
{
}
}
}
It is dummy code. And it uses reflection, since I am under Android 2.3.
This is the solution I finally found for my application.
AppWidgetManager manager = m.getAppWidgetManager();
AppWidgetHost host = m.getWidgetHost();
List<AppWidgetProviderInfo> widgetList = manager.getInstalledProviders();
AppWidgetProviderInfo provider = null;
for(AppWidgetProviderInfo info : widgetList){
//To get the google search box
if(info.provider.getClassName().equals("com.google.android.googlequicksearchbox.SearchWidgetProvider")){
provider = info;
break;
}
}
if(provider != null){
int id = host.allocateAppWidgetId();
boolean success = false;
success = manager.bindAppWidgetIdIfAllowed(id, provider.provider);
if (success) {
AppWidgetHostView hostView = host.createView(getActivity(), id, provider);
AppWidgetProviderInfo appWidgetInfo = manager.getAppWidgetInfo(id);
LauncherAppWidgetInfo info = new LauncherAppWidgetInfo(id);
info.hostView = hostView;
info.hostView.setAppWidget(id, appWidgetInfo);
attachWidget(info);
} else {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER,
provider.provider);
// TODO: we need to make sure that this accounts for the options
// bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS,
// options);
m.startActivityForResult(intent, Main.REQUEST_BIND_APPWIDGET);
}
}
}
i'm using this snippet to check if an app/activity is installed:
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
public static boolean isScanAvailable(Context context) {
return isIntentAvailable(context, "com.google.zxing.client.android.SCAN");
}
In the above example it checks if the Barcode Scanner App is installed, which works just fine.
However, if i try to check for the Adobe Flashplayer using com.adobe.flashplayer it doesn't work and always returns false.
Is there a better / more reliable method to check for Flash?
Uhm yeah. My code posted above does Intent checking which isn't working for the flashplayer (no public intents i guess).
The more obvious way would be to just use getPackageInfo() which works just fine:
public static boolean isFlashAvailable(Context context) {
String mVersion;
try {
mVersion = context.getPackageManager().getPackageInfo(
"com.adobe.flashplayer", 0).versionName;
Log.d("Flash", "Installed: " + mVersion);
return true;
} catch (NameNotFoundException e) {
Log.d("Flash", "Not installed");
return false;
}
}
(As an added bonus we get the exact version number too)