Author: Evan Parsons

Xamarin – Did not find reference matching RestoreAssemblyResources AssemblyName metadata

Did you recently remove a package, but Visual Studio insists it’s there? Or did you readd it and VS seems to disagree? People on the Xamarin Forums tend to get good luck cleaning/rebuilding, and it miraculously starts working again for them. I wasn’t so fortunate and wasted a lot of time taking their word for it.

 

My exact error message

Error Did not find reference matching RestoreAssemblyResources AssemblyName metadata 'Google.MobileAds, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' iosTests

If you’re like me, you’ve tried readding the package, modifying your packages.config file and still nothing is working

Here’s what you need to do:

  1. Locate your  .csproj file and open it in a text editor (Double clicking opens it in VS)
  2. Press CNTRL + F and search for the offending library
  3. Comment out the entry or delete it
  4. Build the project again (Since it’s Xamarin, it will take a lifetime to build) and it will hopefully work again!
  5. Success! 

I spent days trying to figure this out. When we as Xamarin developers run into issues like this, we need to share our solutions since there is little to no support from Xamarin. I hope all my fellow Xamarin developers will find this useful!

Displaying AdMob ads on iOS with Xamarin Forms!

All the tutorials on displaying ads on iOS are currently broken, as the package used to display ads has been removed.

There is a new package, however the usage example it gives is for an iOS dependent project. It will not work/compile for use in Xamarin Forms

The approach is the same however, You are rendering a View you declare in your portable project

Install the correct ad package, it should look like this in Nuget Package Manager

xamarin

Here is the view I created in my Xamarin Forms project.

namespace CanadianClassifiedsAlerter
{
    public class AdMobView : View
    {
        public AdMobView() { }
    }
}

In my iOS project, I created a ViewRenderer.

[assembly: ExportRenderer(typeof(AdMobView), typeof(AdMobRenderer))]
namespace CanadianClassifiedsAlerter.iOS
{
    public class AdMobRenderer : Xamarin.Forms.Platform.iOS.ViewRenderer
    {
        const string bannerId = "THISISWHEREMY_AD_IDGOES";

        BannerView adView;
        bool viewOnScreen = false;

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
        {
            base.OnElementChanged(e);
            // Setup your BannerView, review AdSizeCons class for more Ad sizes. 
            adView = new BannerView(size: AdSizeCons.Banner, origin: new CGPoint(-10, 0))
            {
                AdUnitID = bannerId,
                RootViewController = UIApplication.SharedApplication.Windows[0].RootViewController
            };

            // Wire AdReceived event to know when the Ad is ready to be displayed
            adView.AdReceived += (object sender, EventArgs ea) => {
               
                viewOnScreen = true;
            };

            adView.LoadRequest(Request.GetDefaultRequest());
            base.SetNativeControl(adView);
        }
    }


}

Now, after all is said and done to add it to my page, it’s this simple!

 

 mainLayout.Children.Add(new AdMobView
            {
                WidthRequest = 320,
                HeightRequest = 50
            });

Your end result should look like this

iosAdmobFinalResult

EDIT: Sample project files are here

Unused Canadian Classifieds Portable Project Files (Xamarin Forms).

I decided to use Xamarin.iOS instead for this project, so feel free to do whatever with those files.

 

EDIT: A user followed the advice from this article and got the following error…

“Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[AppDelegate window]: unrecognized selector sent to instance 0x79aa9b10”

He override the Window with the following code and away it worked!

public override UIWindow Window {
get
{
return UIApplication.SharedApplication.KeyWindow;
}
}

No valid iOS code signing keys found in keychain. You need to request a codesigning certificate from https://developer.apple.com

If you receive the following error message after trying to run an Xamarin application in Visual Studio

"No valid iOS code signing keys found in keychain. You need to request a codesigning certificate from https://developer.apple.com. <YourAppName>"

What this basically means is that you need to create or log into your Apple ID in XCode itself and get a certification straight from Apple.

There is a great article on the Xamarin website on how to do this, check it out here

https://developer.xamarin.com/guides/ios/getting_started/installation/device_provisioning/#Using_Xcode

If you still get the error message, make sure you have the iPhone simulator selected

 

Untitled

 

Untitled2

Changing SharedPreferences of a Context instance for testing with Robolectric

The following code will get it done in a snap

Context context = RuntimeEnvironment.application.getApplicationContext();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putBoolean("useOnlyOnWifi", false).commit();

Explanation

 

You use “RuntimeEnvironment.application.getApplicationContext();” to create an instance of a Context object.

You return the preferences, and modify the needed value and then commit it.

The next time this instance of Context returns SharedPreferences, it will return the version that you modified.

Change as many settings as you need to, then pass the context to the method you are trying to test!

 

Android Studio – How to make an instance of Context during unit testing

One of the great new features of Android Studio 2.0 is the automatic implementation of unit testing when you create a new project. However, in it’s base form, it’s only good for testing Java code (nothing Android specific). Mocking an instance of Context won’t do any good if you need it to execute your code.

The solution is…

 Robolectric

The only drawback to this method, is that it doesn’t currently support Api level 22 and above. You will need to adjust the following in your build.gradle…

compileSdkVersion 21
buildToolsVersion "21.1.2"

You will also need to change the version of appcompat you’re using

compile 'com.android.support:appcompat-v7:21.0.0'

Add the following line into the dependencies section

testCompile "org.robolectric:robolectric:3.0"

So all said and done, your build.gradle file should look similar to this

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "net.evanp.umactuallynerdtriviagame"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    testCompile "org.robolectric:robolectric:3.0"
    compile 'com.android.support:appcompat-v7:21.0.0'
}

 

Editing your test classes

Before your class is declared, add the following

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)

On top of the test method add @test just like usual, but notice how the instance of Context is created

@Test
public void testAbilityToAccessXML() throws Exception {
    Context context = RuntimeEnvironment.application.getApplicationContext();
    ProgramWideFunctions.randomCategory(context);
}

It’s that easy!

Current status of Canadian Classifieds Alerter Android App

UPDATED Nov 2019:

Future of Canadian Classifieds Alerter:

In case you haven’t noticed, this app hasn’t been updated in over a year. I can assure you it’s not because I’m lazy, it’s that the app will not work on newer versions of Android.

What? Why? The app works on my phone now and I have the newest version of Android!

That’s because I have the target API level set to 23 (Android 6.0). In order to submit an update to google play, I’d need to target at least API Level 28 (Android 9.0)

The app runs in compatibility mode so that’s why it still works.

Some newer phones are already having problems! Some phones will only allow background services to run if the app is in the foreground!

Why don’t you just target Android 9.0 then?

Oh man, I wish I could. The problem is that newer versions of Android are incredibly picky about background services. See this blog post for more information:

https://blog.klinkerapps.com/android-o-background-services/

So where does this leave us? Is the app done for?

Ha. Well. Funny story. I’ve been in this same situation for the iOS version and found a workaround!

Okay… what’s the workaround?

I purchased a server to do the crunching for the app, then deliver the results to the app through a push notification! It’s working right now as we speak in the current beta and has been working for the iOS version for about two years.

I don’t know what that means. Can you just give me the pros and cons?

This new app will have the following pros and cons.

Pros:

  • Better battery life
  • Less slowdown on phones with limited memory and CPU
  • Less data usage (aka, lower monthly bill)
  • Ability to do complex things that a phone would choke on or be inefficient at. For example, I can ignore posts by certain sellers (people who repost the same crap for way too much). It’s made me WAY more efficient on Kijiji.
  • The app will be updated regularly again.
  • The bug where you have to reopen the app after opening a notification has been fixed

Cons:

  • Relies on a server. Servers go down occasionally
  • Will have a recurring monthly subscription cost if you want premium features (instantaneous checking). Free checking will get slower as the app gains more users.

What will happen to the old app? It still works fine for me for now

I won’t be updating it anymore. However the play store listing will still be around and I won’t be getting rid of the listing. You’re free to keep using it, just know that at any given time, you could update your version of Android and it just straight up won’t work.

What new features are in the works for the new version of the app?

  • Facebook marketplace
  • Better french translations.
  • Ability to open Kijiji or Preview mode directly from a notification

Summary:

Install the open beta from the link below. If there’s any issues, I need to know before I release it to the public. The old version of the app will always be available to you, but could stop working at any time.

Open beta link:

https://play.google.com/store/apps/details?id=net.evanp.canadianclassifiedsalerterpush

Please report any issues!

This page is the system status for the Android port of Canadian Classifieds Alerter. To see the iPhone status page, please visit this page

Current Issues:

– None!

Implementing a hierarchy (tree like) structure with Java / Android and navigating them with Buttons

For my Canadian Classifieds Alerter app, I needed to implement a hierarchy like structure to represent locations and categories. This is what I used in my solution.

For example,

Buy N’ Sell -> Computers -> Laptops

Canada -> New Brunswick -> Moncton

Laptops belongs to Computers, which a sub category of Buy N’ Sell’

Moncton belongs to New Brunswick, which belongs to Canada

Solution

Here it is in it’s simplest form. We’ll actually make it do something further down.

public enum KijijiCategory {
    //root category
    CATEGORY(null),
    //buy n sell, pass in the root category as the parent
    BUY_N_SELL(CATEGORY),
    //Pass in Buy N Sell as the parent category
    COMPUTERS(BUY_N_SELL),
    //Pass in Computers as the parent category
    LAPTOPS(COMPUTERS),
   

    private KijijiCategory parent = null;
   

	//Constructor, you pass in the parent
    KijijiCategory(KijijiCategory parent) {
        this.parent = parent;
    }

}

General explanation of what’s going on:

For CATEGORY, there is no parent, so I pass in null.

For BUY_N_SELL, the parent is CATEGORY, so I pass it in

For COMPUTERS, the parent is BUY_N_SELL, so I pass it in.

For LAPTOPS, the parent is COMPUTERS, so I pass it in.

Are you starting to see where this is going?

What can we use it for?

Check out this video.

Notice how easy it is to traverse through menu options? To do this we’re going to have to add more to the code I posted above

public enum KijijiCategory {
    //root category
    CATEGORY(null),
    //buy n sell, pass in the root category as the parent
    BUY_N_SELL(CATEGORY, 10, "Buy n sell"),
    //Pass in Buy N Sell as the parent category
    COMPUTERS(BUY_N_SELL, 16, "Computers"),
    //Pass in Computers as the parent category
    LAPTOPS(COMPUTERS,773, "Laptops"),
   
	
    private KijijiCategory parent = null;
    //unique identifier used internally by my app, you might not
	//need this or you may need to add your own
	private int id;
	//title will give us a nicer looking 
    private String title = "";


	//In this example
    KijijiCategory(KijijiCategory parent) {
        this.parent = parent;
    }

	//This is the constructor used by almost all of the categories above
    KijijiCategory(KijijiCategory parent, int id, String title) {
        this.parent = parent;
        this.id = id;
        this.title = title;
    }

    /*
     * Getters
      * */
    public String getTitle() {
        return this.title;

    }

    public String getParentName() {

        if (parent == null)
        {
            return "none";
        } else {
            return parent.getTitle();
        }
    }

    public KijijiCategory getParentCategory() {
        return parent;
    }

    public int getId() {
        return this.id;
    }

    @Override
    public String toString() {
        return "Kijiji Category name of: " + this.getTitle() + " and id of " + this.getId() + " and a parent of " + getParentName();
    }
}

You’ll notice I added a new constructor. I’m passing in an internally used identifier, and a nicer to look at string called title we’ll use to set the text for the boxes. I also added a few getters and overrode the toString() method

This is how I generated the LinearLayout

First step is to call the new method in onCreate (I prefer to keep this stuff out of onCreate as it makes future modifications easier)

generateKijijiCategories(KijijiCategory.CATEGORY, categoryLayout);

Here is where all the buttons are generated.

private LinearLayout generateKijijiCategories(final KijijiCategory categoryParent, final LinearLayout categoryLayout) {

        //Shows all of the buttons if we're on the main category 

        if (categoryParent == KijijiCategory.CATEGORY) {
            final Button button = new Button(this);
            button.setText("All Categories");
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    button.setTextColor(Color.GREEN);
                    categoryToQuery = KijijiCategory.CATEGORY;
                }
            });
            categoryLayout.addView(button);

        }
        //iterate through all potential values
        for (final KijijiCategory kijijiCategory : KijijiCategory.values()) {
            //abandon the current iteration if it's not a child of the parent we passed in
	    if (kijijiCategory.getParentCategory() != categoryParent) {
                continue;
            }
	    //Instantiate a button
            final Button button = new Button(this);
	//Give it a nice looking text
            button.setText(kijijiCategory.getTitle());
			//Used internally in my app
            button.setId(kijijiCategory.getId());
			button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
		    //turns focus to the top
                    txtCategories.requestFocus();
		    //empty out all buttons
                    categoryLayout.removeAllViews();
                    button.setTextColor(Color.GREEN);
                    categoryLayout.addView(button);
                    categoryToQuery = kijijiCategory;
		    //This isn't true recursion, it's debateable though
                    generateKijijiCategories(kijijiCategory, categoryLayout);

                }
            });
			//add the button to the layout
            categoryLayout.addView(button);
        }
        //add back button!
        if (categoryParent != KijijiCategory.CATEGORY)
        {

            Button backButton = new Button(this);
            final Button parentButton = new Button(this);
            parentButton.setText(categoryParent.getParentCategory().getTitle());
            parentButton.setTextColor(Color.GREEN);
            backButton.setText("Back");
            backButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    txtCategories.requestFocus();
                    categoryLayout.removeAllViews();

                    if(categoryParent.getParentCategory() != KijijiCategory.CATEGORY) {
                        categoryLayout.addView(parentButton);
                    }
                    categoryToQuery = categoryParent.getParentCategory();
                    generateKijijiCategories(categoryToQuery, categoryLayout);
                }
            });
            categoryLayout.addView(backButton);
        }

        return categoryLayout;
    }

See not so bad!

Canadian Classifieds Alerter – Frequently Asked Questions (FAQ)

Where can I download Canadian Classifieds Alerter?

Google Play or Amazon.

How much data will it use?

Here’s a simple formula you can use to get a ballpark

(X) = minutes you’re on data and not WiFi a day (2 hours = 120)
(Y) = Refresh frequency (Check settings to see potential values)
(Z) = Number of alerts

((X) \ (Y)) x 60 x (Z) = Amount of kilobytes used in a run of the day.

Someone who has 6 alerts, who is not on WiFi for 2 hours a day (120 / 15) x 60 x 6 = 2.8 MB’s a day… or 0.0028 GB’s a day.

If data usage is a huge concern, you can turn off checking when not on WiFi in the settings

How much battery will it drain?

Barely any really. Might equate to losing 2 minutes of charge time a day, tops.

Will it work on my Android phone?

Unless you have an Android phone older than 2011, it will work! I tested Canadian Classifieds Alerter on a Galaxy Ace 2 which is running Android 4.0.3 (Api level 15) and it works just fine. The only issue I’ve found is that it ignores ringtone/vibrate settings. This will be fixed in a future update.

Will it work on BlackBerry 10 devices?

According to the Amazon compatibility checker, it does. I no longer own my Q10, so I can’t test it. I will update the FAQ when I find out for sure.

How can I get it to open the Kijiji app instead of the browser?

You can’t. Unless Kijiji modifies their app to open all links associated with Kijiji. This is out of my control unfortunately 🙁

Canadian Classifieds Alerter – How to customize and configure to your liking – Rundown of the app settings

The Canadian Classifieds Alerter app is ready to go out of the box, however you may want to fine-tune the settings or temporarily disable the service.

Settings are available on the main page, click the top right button

Settings location

There is two different sections, “Notifications” and “Data and Sync”

2

 

Let’s go through each section and I’ll explain the settings to you

Notifications

Notifications

#1 – Service Enabled: Want to disable the service for a while? Turn this switch off. When you’re ready to re-enable it, flick the switch back on.

 

#2 – Ringtone: Change the ringtone to something specific so you know it’s a Classifieds Alert if you want.
#3 – Vibrate: Turn Vibration on or off

 

Data & Sync

Data & Sync

#1 – Sync Frequency on WiFi: How often do you want to perform checks while on WiFi? For battery considerations, you may want to set this higher. You can set it as frequently as one minute, but I highly recommend that you don’t for two reasons:

  1. Kijiji’s servers aren’t going to like being bothered that much.
  2. If you have more than 10 alerts, your phone won’t like it. As soon as it’s done doing one round of checks, it will be time to do another.

#2 – Sync Frequency on mobile: How often do you want to perform checks while on your mobile data? Each alert uses 60 KB (0.06 MB) of data everytime you do a check. If you check every 15 minutes, have 5 alerts and you’re on your mobile data two hours a day, you would use 480 KBs of data (0.5 MB or 0.0005 gigabytes) every single day. If you have a great data plan, you can definitely get away with setting this more frequently.

#3 – Check for new ads only on wifi: If you have a terrible data plan (I do too, nothing to ashamed of) and just don’t want to use your data at all, you can turn off checking while on data. Once you connect to WiFi again, the service will turn back on.

 

These are all the settings! Now download the app if you haven’t already!

 

How to get instant alerts from Kijiji – Canadian Classifieds Alerter

You’ve tried using Kijiji’s daily alerts. The issue with them… is that they come daily. As in once a day, when everyone else gets them, meaning you have no more advantage than anyone else. The worst part is… sometimes they don’t even get sent at all.

The solution

If you have an Android device, you can download “Canadian Classifieds Alerter” from the google play store.

Or you can download the iPhone/iOS version on the Apple Store

What does it do?

Canadian Classifieds Alerter checks Kijiji for you! Let’s say you are looking for an iPhone and you’re on a budget of $200. Simply set an alert to check for “iPhone” and a price limit of $200 and you will be notified the next time someone posts it on Kijiji… before anyone else!

Where can I get it and does it cost money?

It is available on Google play and the Apple app store for the low cost of FREE!

How do I setup an alert?

(These steps are for an outdated Android version, iOS/iPhone version and newer Android versions will be very similar!)

Glad you asked and that you’re on board. Let’s walk through the steps… I am going to assume you know to download an application from Google Play or Amazon. Because of how their search is setup, you may have to search for “Canadian Classifieds Alerter” all in quotes. If not, just click this link for Google Play or this link for Amazon on your Android smartphone/tablet.

(These screenshots are from an earlier version of the app. The steps are still the same)

Step 1. Click “New Alert”

The New Alert screen will then be displayed

Click on "New Alert"

Click on “New Alert”

 

Step 2. Select a Category and Location

Clicking through the buttons will traverse you through categories, and categories within categories. The same goes for location. For this example, I am picking “Cellphones” in the “Phones” category and I will be searching in Moncton, New Brunswick. If you make a mistake, don’t sweat it, just press the back button.

step2

 

Step 3. (Optional) Enter a search query and a maximum price

While this part is optional, I highly recommend it just so you aren’t bombarded with notifications with things you don’t care about, or things out of your price range. In the maximum  price field, enter the most you’re willing to pay (ie, 200) and enter a search query in the “Search for” box (In this case, “iphone”).

step3

Step 4. Press “ADD ALERT”

Self-explanatory.

step4

And just like that you’re brought to the main screen and your notification is now there

step5

Step 5. Close the application and wait.

There is a background service running that does all the checking for you… sit tight. The more specific your search is, the less results you get. It’s worth mentioning that by default, it will not check unless you’re on wifi. Go into the settings to change this. Want a rundown of all the settings? Check out my blog post on how to do that!

Step 6. You got an alert! Hurry and jump on it!

step6

Wow, that was fast! Touch the notification and the Notifications screen will appear… click “GO TO AD” to open the ad in your browser

 

step7

Check the ad out, if you like it, then contact the seller!

step8

 

Want more information? Check out these posts:

Canadian Classifieds Alerter – How to customize and configure to your liking – Rundown of the app settings
Canadian Classifieds Alerter –  Frequently Asked Questions (FAQ)