Cross API Notifications - android

So this is more or less a continuation of my last question Link to last post
I have been working on the code and a buddy of mine suggested I use Notifiction.Builder so I have that figured out. I am wondering how I or anyone else would go about making status notifications "Cross API" What I mean by this is that Builder works on API 11+ but anything below is a no go. Are there any known work around's / fixes for this issue or Will I need to code two different versions of this?

There are a number of different ways to achieve multiple api support using deprecated methods, the most frequently mentioned of which is reflection. Personally, I went with exploiting the classloader to solve the notification problem. I made a base abstract class with basic wrapper methods for handling the different data required for creating a notification, which looks like this:
public abstract class NotificationWrapper {
public NotificationWrapper(Context context) {
}
public abstract void createNotification(int resid, CharSequence ticker);
public abstract void setTitle(CharSequence title);
public abstract void setText(CharSequence text);
public abstract void setIntent(PendingIntent intent);
public abstract void setFlags(int... flags);
public abstract void setDefaults(int defaults);
public abstract Notification getNotification();
}
You can of course add and remove methods depending on how much you're customizing your notifications. Then you can just extend this to three separate classes: one for API <11, one for API >=11, and one for Jelly Bean and up. In each class you just relay these method calls to the appropriate available methods for the current API version (i.e. pre-11 relays to Notification methods, post-11 relays to Notification.Builder methods, and Jelly Bean is the same as post-11 except it uses build() as opposed to getNotification()). Then, when it comes time to create your notification you can use a conditional on the SDK version and the beauties of polymorphism to load the right subclass and build your notification using its wrapper methods:
NotificationWrapper wrapper = null;
int ver = VERSION.SDK_INT;
if (ver >= 16) {
wrapper = new NotificationWrapJB(context);
} else if (ver >= 11) {
wrapper = new NotificationWrapHC(context);
} else {
wrapper = new NotificationWrapPreHC(context);
}
wrapper.createNotification(resId, tickerText);
//etc....
Again, this is just one of many ways to deal with this problem, but I like it because it just feels more organized.
If you're unfamiliar with which methods to use for the different SDK versions, read the Android docs on Notification and Notification.Builder.

I think you're looking for NotificationCompat present in the Support Library.
And if you're looking for the brand New Notifications coming with Jelly Bean you can use NotificationCompat2 from Jake Wharton (the one that made ActionBarSherlock).

Related

Checking multi window support

I've a problem with checking is device supports Mutli Window Mode. I'm using this function to check it isInMultiWindowMode() but it've added in API 24, and when i'm runs my app on device with lower api version it cause an exception. There is any replacement for this function for lower api versions?
There is any replacement for this function for lower api versions?
Not in the Android SDK. There is no multi-window mode (from the Android SDK's standpoint) prior to API Level 23. And, for whatever reason, Google elected not to add isInMultiWindowMode() to ActivityCompat, perhaps because they cannot support the corresponding event (onMultiWindowModeChanged()).
So, here's a free replacement method:
public static boolean isInMultiWindowMode(Activity a) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
return false;
}
return a.isInMultiWindowMode();
}
Add that to some utility class somewhere and call it as needed.
Also note that isInMultiWindowMode() suffers from a race condition that makes it unreliable, IMHO.
What #CommonsWare explained is true, it is a race condition. Hence, isInMultiWindowMode() will give actual result if you call it from inside post method:
View yourView = findViewById(R.id.yourViewId);
yourView.post(new Runnable() {
#Override
public void run() {
boolean actualResult = isInMultiWindowMode();
}
});

New MessagingStyle on API < 24

I just stumbled upon the new MessagingStyle described here: https://developer.android.com/guide/topics/ui/notifiers/notifications.html (last paragraph)
I investigated further and found such a class also as a NotificationCompat.Style variant.
This is the code I tried:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
.notify(123, new NotificationCompat.Builder(this)
.setContentTitle("Test")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("4 new messages")
.setStyle(new NotificationCompat.MessagingStyle("Me")
.setConversationTitle("Team lunch")
.addMessage("Hi", 123, null) // Pass in null for user.
.addMessage("What's up?", 234, "Coworker")
.addMessage("Not much", 345, null)
.addMessage("How about lunch?", 456, "Coworker")).build());
}
}
This is basically just the default Activity generated when creating a new project and the sample code taken from the linked website.
Now my problem: The extended style is not shown on APIs below 24. I tested on a device with API 23. When running on an emulator with API 24, it works.
The documentation states:
Helper class for generating large-format notifications that include multiple back-and-forth messages of varying types between any number of people.
If the platform does not provide large-format notifications, this method has no effect. The user will always see the normal notification view. [...]
But versions since KitKat do provide large-format notifications.
Is the documentation not clear enough or am I doing something wrong?
Update: this is fixed as of the 25.0.0 Support Library release and NotificationCompat.MessagingStyle now backports much of the styling to previous versions of Android.
Previous answer:
As per this bug report, MessagingStyle currently does not do any special formatting prior to Android N. The bug report is marked as FutureRelease which means that the work is done and it will support pre-N devices with a more rich formatting in a future version of the Support Library.
If you wish to use it now, you can certainly build your own pre-N version of the notification (using a BigTextStyle if the version of Android is less than N for example).
If I am reading the source code correctly, the fallback for pre-24 devices has not been implemented as of the time of this writing. So, at the moment, it gives you something that compiles, and delegates properly to the native implementation on API Level 24+ devices, but will not show the messages on older devices.
Additional note: check your imports and make sure you use the android.support.v7.app.NotificationCompat class
instead of android.support.v4.app.NotificationCompat, because even in 25.2.0, using MessagingStyle with the "v4" builder does not show anything in devices with API < 24.

Xamarin Forms In app billing for Android

I'm making an app where I have to have in app purchases (buying keys that I can further use in the app).
I have looked at this component http://components.xamarin.com/view/xamarin.inappbilling, but I have no idea how I can implement this in xamarin forms. Is there anyone out there willing to help me with this problem? Is there any open source projects with in app purchase that I can look at?
I know its late, but this might help someone:
The way to do achieve this is to create a service and then surface it to a standard interface (as per your requirement) that will be consumed within the forms project.
You can even use MessagingCenter to communicate between Android and Xamarin.Forms project.
FormsPrject:
MessagingCenter.Send<MainPage, string>(this, "BuyProduct", "buyButtonPressed");
AndroidPoject
MessagingCenter.Subscribe<MainPage, string>(this, "BuyProduct", (sender, arg) =>
{
//logic to buy product
}
Hope that helps!!
The question is very vague so I will offer a general answer and some notes that I found important. I am using Visual Studio 2015 with Xamarin Forms 2.3.0.107.
I would use abstraction for this instead of sending messages directly between the projects.
The basic idea is, you will create a public interface in your Xamarin Forms project. Since your Andriod project has a reference to the Xamarin Forms project, it can use this public interface. Then you will implement this interface in your Android project will all of the billing logic. In the Xamarin Forms project. Using the Dependency service we can get the existing instance of the implementation into the Xamarin Forms project. Then, you can code against the interface. This is especially useful if you ever want to do an iPhone or other implementation, because you would never need to make changes to the Xamarin Forms code; you can just plug in new implementations.
It might be a bit out of scope but, be sure to meet all of the Google requirements as far as setting up your developer account and your merchant account and your API account. It is all very confusing and messy.
The Xamarin component in nuget is currently version 1.5. However, the component has a newer published version. You want to use the newer (2.0 or higher) version.
Use the Android SDK manager to install the Google Play Billing Library.
In your Android project, add a reference to Xamarin.InAppBilling and add a Xamarin.InAppBilling component.
The Google object has to live in an Android activity, because you depend on overriding an activity method to complete purchases (I used the MainActivity here and made the google object a static for easy access)
Testing this with Google Play is a hassle. The documentation is confusing because of differences between versions. You cannot use actual product id's until you publish your app. They provide test id's that can be used during testing but they only offer some functionality.
I have made these code examples as minimal as possible to illustrate the concept. You will obviously want to do much more.
Xamarin Forms project
Create an interface:
public interface IInAppBilling
{
void Pay(string productId);
}
Any time you want to use the billing service, you use IInAppBilling billingService = DependencyService.Get<IInAppBilling>(); to get a reference to the device-specific (Android) implementation.
//call this from a button click or whatever
void BuySomething(string somethingId)
{
//Get any IInAppBilling object that is registered with the DependencyService.
IInAppBilling billingService = DependencyService.Get<IInAppBilling>();
billingService.Pay(somethingId);
}
Android Project
Override an activity's OnCreate method and create an InAppBillingServiceConnection:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
public static InAppBillingServiceConnection google;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
google = new InAppBillingServiceConnection(this, "MII...ApplicationKey");
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
Create a class that implements the Xamarin Forms interface we created earlier. It is important not to ignore the assembly: Dependencyannotation at the top. This is what makes the class available to the Dependency service in the Xamarin Forms object:
[assembly: Dependency(typeof(com.myapp.InAppBilling))]
namespace com.myapp
{
class InAppBilling :IInAppBilling
{
public void Pay(string productId)
{
MainActivity.google.BillingHandler.BuyProduct(productId, ItemType.Product, "MyUniquePayload");
}
}
}
Override the activity's OnActivityResult method to finalize purchases:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
// Ask the open service connection's billing handler to process this request
try
{
google.BillingHandler.HandleActivityResult(requestCode, resultCode, data);
}
catch (Exception ex)
{
//log it or something?
}
}

Android Lollipop - read title of notifications

I'm just testing my app on Android 5.0 and I discover that I am no more able to get the RemoteViews from third party notifications to read its title and ticker text like I did in KitKat. The code I used successfully on KitKat is similar to this:
public static List<String> getText(Notification notification) {
RemoteViews views = notification.contentView;
if (views == null)
return null;
else {
...
}
}
This function return me NULL so it isn't able to grab the entire contentView from the notification. Any help?
Thanks in advance!
I've just figured out that the correct approach is to use AccesssibilityService for Android <= JELLY_BEAN_MR2 and to extend the NotificationListenerService for other newer versions.
In the new class, extension of NotificationListenerService, it is necessary to add the declaration #TargetApi(Build.VERSION_CODES.THE_OS_NAME_YOU_WANT) on the top, to ensure that the following code will be executed just starting by that version of the OS.
It works like it should.
I hope it will help someone else.
Thank you anyway

Android: how to code depending on the version of the API?

In Android I get the version of the SDK easily (Build.VERSION.SDK) but I need to use LabeledIntent only if the platform is newer than 1.6 (>Build.VERSION_CODES.DONUT)
I suppose that Reflection is necessary (I have read this link but it is not clear for a class or to me).
This is the code but it gives me an exception because in my Android 1.6, the compiler verifies if the package exists even if the condition is not applied:
Intent theIntent=....;
if(Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.DONUT)
{
try{
Intent intentChooser = Intent.createChooser(intent,"Choose between these programs");
Parcelable[] parcelable = new Parcelable[1];
parcelable[0] = new android.content.pm.LabeledIntent(theIntent, "", "Texto plano", 0);
intentChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, parcelable);
activity.startActivity(intentChooser);
}
catch(Exception e)
{
activity.startActivity(theIntent);
}
} else
{
activity.startActivity(intentMedicamento);
}
HOW I SOLVED IT, SOME NOTES TO THE RIGHT ANSWER
#Commonsware show me the way to do it. We create a bridge class so that depending on the API LEVEL, you instance one class that uses an API LEVEL or another class that uses another API LEVEL.
The only detail one beginner could forget is that you have to compile your app with the newest SDK you are goint to make reference.
public abstract class LabeledIntentBridge {
public abstract Intent BuildLabeledIntent(String URL, Intent theintent);
public static final LabeledIntentBridge INSTANCE=buildBridge();
private static LabeledIntentBridge buildBridge() {
int sdk=new Integer(Build.VERSION.SDK).intValue();
if (sdk<5) {
return(new LabeledIntentOld());
}
return(new LabeledIntentNew());
}
}
So in the LabeledIntentNew, I included all the code that refers to LabeledIntent only available in API LEVEL 5. In LabeledIntentOld, I can implement another kind of control, in my case I return the intent itself without doing nothing more.
The call to this class is done like this:
LabeledIntentBridge.INSTANCE.BuildLabeledIntent(URLtest,theIntent);
Follow the wrapper class pattern documented in the page you linked to above.
You have to use reflection...
The idea is good, but in your code you refer to LabeledIntent which is not available in 1.6. So when your app runs against 1.6 devices, it cannot find the class and crashes.
So the idea is to write code where you don't refer to LabeledIntent when running in 1.6. To do this, you can write a wrapper class (LabeledIntentWrapper) which extends LabeledIntent and call it in your function. So, in 1.6, the device will see a reference to a known class: LabeledIntentWrapper.

Categories

Resources