How to unregister a client from Push Notifications? - xamarin

I'm working on an Xamarin.Forms app with Azure Notifications Hub and Androud GCM. Now when I user is registered I register him to the notifications:
I have an interface:
public interface IPushNotificationService
{
void Register();
void UnRegister();
}
And in the Android project I implemented the interface as:
public class PushNotificationService : IPushNotificationService
{
public PushNotificationService() { }
public void Register()
{
RegisterWithGCM();
}
public void UnRegister()
{
try
{
GcmClient.UnRegister(MainActivity.instance);
}
catch (Exception e)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "ERROR while UnRegistering - " + e.Message);
}
}
private void RegisterWithGCM()
{
try
{
// Check to ensure everything's setup right
GcmClient.CheckDevice(MainActivity.instance);
GcmClient.CheckManifest(MainActivity.instance);
// Register for push notifications
UnRegister();
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "Registering...");
GcmClient.Register(MainActivity.instance, Constants.SenderID);
}
catch (Java.Net.MalformedURLException)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "ERROR - There was an error creating the client. Verify the URL.");
}
catch (Exception e)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "ERROR - " + e.Message);
CreateAndShowDialog("Cannot register to push notification. Please try to run the app again.", "Error");
}
}
private void CreateAndShowDialog(String message, String title)
{
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.instance);
builder.SetMessage(message);
builder.SetTitle(title);
builder.Create().Show();
}
}
I also has the GcmService as listen in the Azure MSDN sites:
[assembly: Permission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace MyApp.Droid
{
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "#PACKAGE_NAME#" })]
public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
{
public static string[] SENDER_IDS = new string[] { "XXXXX" };
public static string TAG = "PUSH_NOTIFICATIONS";
}
[Service]
public class GcmService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
private NotificationHub Hub { get; set; }
public GcmService()
: base(PushHandlerBroadcastReceiver.SENDER_IDS) { }
protected override void OnRegistered(Context context, string registrationId)
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "GCM Registered: " + registrationId);
RegistrationID = registrationId;
Hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString,
context);
try
{
Hub.UnregisterAll(registrationId);
Hub.Unregister();
}
catch (Exception ex)
{
Log.Error(PushHandlerBroadcastReceiver.TAG, ex.Message);
}
var userId = Settings.UserId;
if (! String.IsNullOrWhiteSpace(userId))
{
Log.Verbose(PushHandlerBroadcastReceiver.TAG, "Registering user_id: " + userId);
var tags = new List<string>() { userId };
try
{
Hub.Register(registrationId, tags.ToArray());
}
catch (Exception ex)
{
Log.Error(PushHandlerBroadcastReceiver.TAG, ex.Message);
}
}
}
protected override void OnMessage(Context context, Intent intent)
{
Log.Info(PushHandlerBroadcastReceiver.TAG, "GCM Message Received!");
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
string message = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(message))
{
createNotification("New Message", message);
return;
}
Log.Error(PushHandlerBroadcastReceiver.TAG, "Unknown message details: " + msg);
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show ui
var uiIntent = new Intent(this, typeof(MainActivity));
//Use Notification Builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
//Create the notification
//we use the pending intent, passing our ui intent over which will get called
//when the notification is tapped.
var notification = builder.SetContentIntent(PendingIntent.GetActivity(this, 0, uiIntent, 0))
.SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
.SetTicker(title)
.SetContentTitle(title)
.SetContentText(desc)
//.AddAction(new NotificationCompat.Action())
//Set the notification sound
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
//Auto cancel will remove the notification once the user touches it
.SetAutoCancel(true).Build();
//Show the notification
notificationManager.Notify(1, notification);
}
protected override void OnUnRegistered(Context context, string registrationId)
{
Log.Info(PushHandlerBroadcastReceiver.TAG, "Unregistered RegisterationId : " + registrationId);
try
{
Hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString,
context);
Hub.Unregister();
if (!String.IsNullOrWhiteSpace(registrationId))
Hub.UnregisterAll(registrationId);
}
catch (Exception e)
{
Log.Error(PushHandlerBroadcastReceiver.TAG, "Error while unregistering: " + e.Message);
}
}
protected override void OnError(Context context, string errorId)
{
Log.Error(PushHandlerBroadcastReceiver.TAG, "GCM Error: " + errorId);
}
}
}
Now when a user logged in to the app I call:
DependencyService.Get<IPushNotificationService>().Register();
And when he logged out I call:
Now when a user logged in to the app I call:
DependencyService.Get<IPushNotificationService>().UnRegister();
What I'm doing wrong here? When a user logged out I see in the debug that all the Unregister methods are called but the user still getting new messages.
Thanks,
Seif.

Maybe the problem is in the line:
Hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString,
context);
You are creating a new object of NotificationHub that might not be referencing the initial connection.
You could try creating a field NotificationHub _hub; set its value in the registering step; and use this field on the unregistering step:
_hub.Unregister();
if (!string.IsNullOrWhiteSpace(registrationId))
_hub.UnregisterAll(registrationId);
disclaimer: did not try this solution. Hope it helps.

Related

Xamarin Forms consuming Web Service

I'm new in mobile apps and now developing an app with xamarin forms. There is a website which i developed with django (sqlite3 db), and now i'am trying to consume data from it and display in my mobile app in listvew. Any thoughts how to achieve it. I've tried this but it doesn't work. Should i use rest api?
public class LandingViewModel : INotifyPropertyChanged
{
private List<Dishes> _menuList { set; get; }
public List<Dishes> MenuList
{
get
{
return _menuList;
}
set
{
if(value != _menuList)
{
_menuList = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public LandingViewModel()
{
GetDataAsync();
}
private async void GetDataAsync()
{
HttpClient client = new HttpClient();
var response = await client.GetAsync("https://mysite.ru/project/");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var menu = JsonConvert.DeserializeObject<List<Dishes>>(content);
MenuList = new List<Dishes>(menu);
}
}
models:
public class Dishes
{
public int id { get; set; }
public string description { get; set; }
public string image { get; set; }
public DateTime published { get; set; }
}
my database in django:
operations = [
migrations.CreateModel(
name='Post',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True)),
('image', models.ImageField(blank=True, upload_to='pictures/')),
('published', models.DateTimeField(verbose_name='publishing date')),
],
),
]
i solved the problem
in postgresql database allowed remote connection: in postgresql.conf replaced line listen_addresses = 'localhost' with listen_addresses = '*'
allowed tcp\ip connection on port 5432
in my web api established connection width db
NpgsqlConnection connection;
public string _name;
public string _description;
public string _image;
private readonly MenuContext _context;
public MenuController(MenuContext context)
{
_context = context;
string connectionString = "Server=server; Port=5432; User Id=user;
Password=password; Database=database";
try
{
connection = new NpgsqlConnection(connectionString);
connection.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM table";
try
{
NpgsqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
_name = reader[1].ToString();
_image = reader[2].ToString();
_description = reader[3].ToString();
_context.Menus.Add(new Menu { name = _name, description =
_description, image = "https://mysite.ru/media/" + _image });
_context.SaveChanges();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
// GET: api/Menu
[HttpGet]
public async Task<ActionResult<IEnumerable<Menu>>> GetMenus()
{
return await _context.Menus.ToListAsync();
}
code in my app:
private async void GetDataAsync()
{
HttpClient httpClient = new HttpClient();
var content = await httpClient.GetStringAsync("https://locahost/api/Menu");
var menu = JsonConvert.DeserializeObject<List<Dishes>>(content);
MenuList = new ObservableCollection<Dishes>(menu);
}
and then displayed it in listview

JSON Array With Volley Showing Only One data

I am trying to display array data using JSON Volley in PostsBasedOnCategory.java. Array data that I want to display are, Title post, and excerpt based on specific category from https://www.kisahmuslim.com/wp-json/wp/v2/categories. But unfortunately, it only showing one result.
Below you can check my code
DaftarCategories.java
public class CallingPage extends AsyncTask<String, String, String> {
HttpURLConnection conn;
java.net.URL url = null;
private int page = 1;
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method will be running on UI thread
showNoFav(false);
pb.setVisibility(View.VISIBLE);
}
#Override
protected String doInBackground(String... params) {
try {
url = new URL("https://www.kisahmuslim.com/wp-json/wp/v2/categories");
}
catch(MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return e.toString();
}
try {
// Setup HttpURLConnection class to send and receive data from php and mysql
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(READ_TIMEOUT);
conn.setConnectTimeout(CONNECTION_TIMEOUT);
conn.setRequestMethod("GET");
// setDoOutput to true as we recieve data from json file
conn.setDoOutput(true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return e1.toString();
}
try {
int response_code = conn.getResponseCode();
// Check if successful connection made
if (response_code == HttpURLConnection.HTTP_OK) {
// Read data sent from server
InputStream input = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
// Pass data to onPostExecute method
return (result.toString());
} else {
return("koneksi gagal");
}
} catch (IOException e) {
e.printStackTrace();
return e.toString();
} finally {
conn.disconnect();
}
}
protected void onPostExecute(String result)
{
JsonArrayRequest stringRequest = new JsonArrayRequest(Request.Method.GET, SumberPosts.HOST_URL+"wp/v2/categories/", null, new Response.Listener<JSONArray>()
{
#Override
public void onResponse(JSONArray response) {
// display response
Log.d(TAG, response.toString() + "Size: "+response.length());
// agar setiap kali direfresh data tidak redundant
typeForPosts.clear();
for(int i=0; i<response.length(); i++) { //ambil semua objek yang ada
final CategoriesModel post = new CategoriesModel();
try {
Log.d(TAG, "Object at " + i + response.get(i));
JSONObject obj = response.getJSONObject(i);
post.setId(obj.getInt("id"));
post.setPostURL(obj.getString("link"));
//Get category name
post.setCategory(obj.getString("name"));
//////////////////////////////////////////////////////////////////////////////////////
// getting article konten
JSONObject postCategoryParent = obj.getJSONObject("_links");
JSONArray postCategoryObj = postCategoryParent.getJSONArray("wp:post_type");
JSONObject postCategoryIndex = postCategoryObj.getJSONObject(0); //ambil satu objek saja
String postCategoryUrl = postCategoryIndex.getString("href"); //satu objek yang dimaksud adalah href
if(postCategoryUrl != null) {
Log.d(TAG, postCategoryIndex.getString("href"));
JsonArrayRequest getKonten = new JsonArrayRequest(Request.Method.GET, postCategoryUrl, null, new Response.Listener<JSONArray>()
{
#Override
public void onResponse(JSONArray respon) {
Log.d(TAG, respon.toString() + "Size: "+respon.length());
for(int d=0; d<respon.length(); d++) {
try {
//Log.d(TAG, "Object at " + i + respon.get());
JSONObject objek = respon.getJSONObject(d); //ambil semua artikel yang tersedia di postCategoryUrl
post.setId(objek.getInt("id"));
post.setCreatedAt(objek.getString("date"));
post.setPostURL(objek.getString("link"));
//Get category title
JSONObject titleObj = objek.getJSONObject("title");
post.setJudul(titleObj.getString("rendered"));
//Get excerpt
JSONObject exerptObj = objek.getJSONObject("excerpt");
post.setExcerpt(exerptObj.getString("rendered"));
// Get content
JSONObject contentObj = objek.getJSONObject("content");
post.setContent(contentObj.getString("rendered"));
}
catch (JSONException e) {
e.printStackTrace();
}
}// for category
} //onResponse2
}, new Response.ErrorListener() { //getKonten
#Override
public void onErrorResponse(VolleyError error) {
pb.setVisibility(View.GONE);
Log.d(TAG, error.toString());
}
});
queue.add(getKonten);
} //if postCategoryUrl
//////////////////////////////////////////////////////////////////////////////////////
typeForPosts.add(post);
} //try 1
catch (JSONException e) {
e.printStackTrace();
}
} //for 1
pb.setVisibility(View.GONE);
recycleViewWordPress.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
} //onResponse1
}, new Response.ErrorListener() { //stringRequest
#Override
public void onErrorResponse(VolleyError error) {
showNoFav(true);
pb.setVisibility(View.GONE);
Log.e(TAG, "Error: " + error.getMessage());
Toast.makeText(getContext(), "Tidak bisa menampilkan data. Periksa kembali sambungan internet Anda", Toast.LENGTH_LONG).show();
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
} //onPostExecute
} //CallingPage
#Override
public void onPostingSelected(int pos) {
CategoriesModel click = typeForPosts.get(pos);
excerpt = click.getExcerpt();
//gambar = click.getPostImg();
judul = click.getJudul();
//url = click.getPostURL();
content = click.getContent();
Bundle bundle = new Bundle();
bundle.putString("judul", judul);
//bundle.putString("gambar", gambar);
//bundle.putString("url", url);
bundle.putString("content", content);
bundle.putString("excerpt", excerpt);
PostsBasedOnCategory bookFragment = new PostsBasedOnCategory();
bookFragment.setArguments(bundle);
AppCompatActivity activity = (AppCompatActivity) getContext();
activity.getSupportFragmentManager().beginTransaction().replace(R.id.flContainerFragment, bookFragment).addToBackStack(null).commit();
}
PostsBasedOnCategory.java
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_wordpress, container,false);
ButterKnife.bind(this,view);
setHasOptionsMenu(true);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
swipeRefreshLayout =(SwipeRefreshLayout) getActivity().findViewById(R.id.swipeRefreshLayout);
recycleViewWordPress =(RecyclerView) getActivity().findViewById(R.id.recycleViewWordPress);
pb = (ProgressBar) getActivity().findViewById(R.id.loadingPanel);
noFavtsTV = getActivity().findViewById(R.id.no_favt_text);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(false);
pb.setVisibility(View.VISIBLE);
recycleViewWordPress.setAdapter(mAdapter);
}
});
typeForPosts = new ArrayList<CategoriesModel>();
recycleViewWordPress.setHasFixedSize(true);
recycleViewWordPress.setLayoutManager(new LinearLayoutManager(getContext()));
recycleViewWordPress.setNestedScrollingEnabled(false);
mAdapter = new AdapterCategoryPosts(getContext(), typeForPosts, this);
recycleViewWordPress.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
loadData();
}
public void loadData() {
Bundle bundel = this.getArguments();
long id = bundel.getLong("id");
String kategori = bundel.getString("kategori");
String title = bundel.getString("judul");
String excerpt = bundel.getString("excerpt");
String pict = bundel.getString("gambar");
String url = bundel.getString("url");
String konten = bundel.getString("konten");
CategoriesModel list = new CategoriesModel();
list.setId(id);
list.setCategory(kategori);
list.setJudul(title);
list.setExcerpt(excerpt);
list.setPostImg(pict);
list.setPostURL(url);
list.setContent(konten);
typeForPosts.add(list);
}
Logcat
2019-09-07 12:01:27.941 11246-11246/com.kursusarabic.arabicforfun D/postFrag: [{"id":12,"count":53,"description":"","link":"https:\/\/kisahmuslim.com\/category\/kisah-nyata\/biografi-ulama","name":"Biografi Ulama","slug":"biografi-ulama","taxonomy":"category","parent":29,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/12"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"up":[{"embeddable":true,"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/29"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=12"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":26,"count":8,"description":"","link":"https:\/\/kisahmuslim.com\/category\/download","name":"Download","slug":"download","taxonomy":"category","parent":0,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/26"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=26"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":1812,"count":9,"description":"","link":"https:\/\/kisahmuslim.com\/category\/info","name":"Info","slug":"info","taxonomy":"category","parent":0,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/1812"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=1812"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":17,"count":3,"description":"","link":"https:\/\/kisahmuslim.com\/category\/kisah-nyata\/kisah-birrul-walidain","name":"Kisah Birrul Walidain","slug":"kisah-birrul-walidain","taxonomy":"category","parent":29,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/17"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"up":[{"embeddable":true,"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/29"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=17"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":25,"count":31,"description":"","link":"https:\/\/kisahmuslim.com\/category\/kisah-nyata\/kisah-hidayah-islam","name":"Kisah Hidayah Islam","slug":"kisah-hidayah-islam","taxonomy":"category","parent":29,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/25"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"up":[{"embeddable":true,"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/29"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=25"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":16,"count":49,"description":"","link":"https:\/\/kisahmuslim.com\/category\/kisah-nyata\/kisah-kaum-durhaka","name":"Kisah Kaum Durhaka","slug":"kisah-kaum-durhaka","taxonomy":"category","parent":29,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/16"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"up":[{"embeddable":true,"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/29"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=1
2019-09-07 12:01:27.942 11246-11246/com.kursusarabic.arabicforfun D/postFrag: Object at 0{"id":12,"count":53,"description":"","link":"https:\/\/kisahmuslim.com\/category\/kisah-nyata\/biografi-ulama","name":"Biografi Ulama","slug":"biografi-ulama","taxonomy":"category","parent":29,"meta":[],"_links":{"self":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/12"}],"collection":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories"}],"about":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/taxonomies\/category"}],"up":[{"embeddable":true,"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/categories\/29"}],"wp:post_type":[{"href":"https:\/\/kisahmuslim.com\/wp-json\/wp\/v2\/posts?categories=12"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}
2019-09-07 12:01:27.942 11246-11246/com.kursusarabic.arabicforfun D/postFrag: https://kisahmuslim.com/wp-json/wp/v2/posts?categories=12
You cannot call network requests in loop, Use async processing or
Retrofit & RxJava multiple requests complete
and after updating typeForPosts, there is no need to setAdapter on recyclerview just call adapter.notifyDataSetChanged()

How to use wikitude plugin in xamarin forms?

I am using Xamarin to develop cross platform AR application. I am using Wikitude instant tracking.
I am able to start the Wikitude activity and able to run the Instant tracking...Now I want capture the high resolution image while tracking...I am trying to build the plugin to get the frame and then convert it to image stream
Her is my Wikitude activity
namespace XamarinExample.Droid
{
[Activity(Label = "WikitudeActivity")]
public class WikitudeActivity : Activity, ArchitectView.IArchitectUrlListener
{
ArchitectView architectView;
string worldUrl;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.sample_cam);
Title = Intent.GetStringExtra("id");
worldUrl = "Wikitude" + File.Separator + Intent.GetStringExtra("id") + File.Separator + "index.html";
architectView = FindViewById<ArchitectView>(Resource.Id.architectView);
ArchitectStartupConfiguration startupConfiguration = new ArchitectStartupConfiguration();
startupConfiguration.setLicenseKey(Constants.WIKITUDE_SDK_KEY);
startupConfiguration.setFeatures(ArchitectStartupConfiguration.Features.ImageTracking);
startupConfiguration.setCameraResolution(CameraSettings.CameraResolution.Auto);
/////////////////////////////// Register Plugin////////////////////////////////////
var plugins = new Plugin01("test");
architectView.RegisterPlugin(plugins);
architectView.OnCreate(startupConfiguration);
architectView.RegisterUrlListener(this);
}
}
My Plugin code taken from
public class Plugin01 : Com.Wikitude.Common.Plugins.Plugin
{
public Plugin01(string p0) : base(p0)
{
}
Frame currentFrame = null;
public override void CameraFrameAvailable(Frame p0)
{
System.Diagnostics.Debug.WriteLine("AVAILABLE FRAME");
try
{
var data = p0.GetData();
currentFrame = p0;
}
catch (System.Exception ex) { }
}
public override void Update(RecognizedTarget[] p0)
{
System.Diagnostics.Debug.WriteLine("AVAILABLE FRAME");
if (p0 != null)
{
if (currentFrame != null)
{
// ConvertYuvToJpeg(currentFrame, p0[0]);
}
}
}
}
I have registered the plugins but it is not calling
public override void Update(RecognizedTarget[] p0) Method....What am I doing wrong here ?
I think the problem is calling "RegisterPlugin" in the wrong method, as you know the cycle of calling activity methods are different.you should call it in "OnPostCreate" method of activity.
try below code and let me know the result:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
try
{
SetContentView(Resource.Layout.Main);
architectView = FindViewById<ArchitectView>(Resource.Id.architectView);
var config = new ArchitectStartupConfiguration();
config.setLicenseKey(WIKITUDE_SDK_KEY);
architectView.OnCreate(config);
}
catch (Exception ex) { Toast.MakeText(this, ex.ToString(), ToastLength.Long); }
}
protected override void OnPostCreate(Bundle savedInstanceState)
{
base.OnPostCreate(savedInstanceState);
if (architectView != null)
architectView.OnPostCreate();
try
{
try
{
string url = string.Format(#"file:///android_asset/01_ImageRecognition_1_ImageOnTarget/index.html");
architectView.Load(url);
Plugin01 cardPlugin = new Plugin01("com.plugin.dpiar");
architectView.RegisterPlugin(cardPlugin);
}
catch (Exception ex) { }
}
catch (Exception ex) { Toast.MakeText(this, ex.ToString(), ToastLength.Long); }
}
consider changing variables name.

DisplayAlert With changing Text xamarin forms

I have a requirement where i have to show the status of the download on a DisplayAlert. But with changing text on it asynchronously.
How to achieve this?
DisplayAlert("Download Info", "Downloading.....", "Ok");
I want to show status like...
Connected to server
Downloading
Download Complete
Here is a simple "Dynamic Alert" for Forms and iOS using UIAlertController and Android using a DialogFragment and a Xamarin.Forms dependency service:
Dependency Interface:
public interface IDynamicAlert
{
void Show(string title, string message);
void Update(string message);
void Dismiss();
}
iOS IDynamicAlert Dependency Implementation:
public class DynamicAlert : IDynamicAlert
{
UIAlertController alert;
public void Show(string title, string message)
{
if (alert != null) throw new Exception("DynamicAlert already showing");
alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
var rootVC = UIApplication.SharedApplication.Windows[0].RootViewController;
rootVC.PresentViewController(alert, true, () =>
{
});
}
public void Update(string message)
{
if (alert == null) throw new Exception("DynamicAlert is not showing, call Show first");
alert.Message = message;
}
public void Dismiss()
{
if (alert == null) throw new Exception("DynamicAlert is not showing, call Show first");
alert.DismissViewController(true, () =>
{
alert.Dispose();
alert = null;
});
}
}
Example Usage:
var alert = DependencyService.Get<IDynamicAlert>();
if (alert != null)
{
alert.Show("StackOverflow", "Starting your request...");
await Task.Delay(2000); // Do some work...
alert.Update("Your request is processing...");
await Task.Delay(2000); // Do some work...
alert.Update("Your request is complete...");
await Task.Delay(750);
alert.Dismiss();
}
else
{
throw new Exception("IDynamicAlert Dependency not found");
}
Output:
Android Version:
The android version consists of a couple of parts, a DialogFragment subclass and the IDynamicAlert implementation that uses the custom DialogFragment.
Android DialogFragment Subclass:
public class DynamicAlertDialogFragment : DialogFragment
{
AlertDialog alertDialog;
readonly Context context;
public static DynamicAlertDialogFragment Instance(Context context, string title, string message)
{
var fragment = new DynamicAlertDialogFragment(context);
Bundle bundle = new Bundle();
bundle.PutString("title", title);
bundle.PutString("message", message);
fragment.Arguments = bundle;
return fragment;
}
public DynamicAlertDialogFragment(Context context)
{
this.context = context;
}
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
var title = Arguments.GetString("title");
var message = Arguments.GetString("message");
alertDialog = new AlertDialog.Builder(context)
.SetIcon(Android.Resource.Drawable.IcDialogInfo)
.SetTitle(title)
.SetMessage(message)
.Create();
return alertDialog;
}
public void SetMessage(string message)
{
(context as Activity).RunOnUiThread(() => { alertDialog.SetMessage(message);});
}
}
Android IDynamicAlert Dependency Implementation:
public class DynamicAlert : IDynamicAlert
{
const string FRAGMENT_TAG = "DynamicAlert_Fragment";
DynamicAlertDialogFragment fragment;
static FormsAppCompatActivity currentActivity;
public static FormsAppCompatActivity CurrentActivity { set { currentActivity = value; } }
public void Show(string title, string message)
{
if (currentActivity == null) throw new Exception("DynamicAlert.CurrentActivity needs assigned");
var fragMgr = currentActivity.FragmentManager;
var fragTransaction = fragMgr.BeginTransaction();
var previous = fragMgr.FindFragmentByTag(FRAGMENT_TAG);
if (previous != null)
{
fragTransaction.Remove(previous);
}
fragTransaction.DisallowAddToBackStack();
fragment = DynamicAlertDialogFragment.Instance(currentActivity, title, message);
fragment.Show(fragMgr, FRAGMENT_TAG);
}
public void Update(string message)
{
if (fragment == null) throw new Exception("DynamicAlert is not showing, call Show first");
fragment.SetMessage(message);
}
public void Dismiss()
{
if (fragment == null) throw new Exception("DynamicAlert is not showing, call Show first");
fragment.Dismiss();
fragment.Dispose();
fragment = null;
}
}
Android Init / Usage:
When creating the AlertDialog in the DialogFragment we need access to the current Activity and when using Xamarin.Forms, that is normally the MainActivity that is a FormsAppCompatActivity subclass. Thus you will need to initialize the DynamicAlert.CurrentActivity static property with this Activity in your MainActivity.OnCreate subclass:
Example:
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
////////////
DynamicAlert.CurrentActivity = this;
////////////
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
Android Output:

Android - Xamarin - Fused Location

I am trying to implement a custom activity that initialises the Fused Location services in Xamarin c# so I can reuse this activity whenever the Fused Location is needed. The problem that I am having is that the map is being loaded before the location services. This way, I cannot animate the camera to zoom in at the user's location since the location is still null.
Here is the custom activity:
using System;
using Android.App;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.Gms.Maps.Model;
using Android.Locations;
using Android.OS;
using Android.Gms.Location;
using Android.Widget;
namespace Maps.Droid.LocationService {
public class LocationTrackerActivity : Activity, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener, Android.Gms.Location.ILocationListener {
// Static Fields
public static long MIN_DISTANCE_CHANGE_FOR_UPDATES = 5; // 5 meters
public static long MIN_TIME_BW_UPDATES = 1000 * 10; // 10 seconds
private Location currentLocation;
private Activity activity;
private bool hasGooglePlayServices;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private bool locationAvailable = false;
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
this.activity = this;
hasGooglePlayServices = checkPlayServices();
if (hasGooglePlayServices) {
initFusedLocation();
} else {
initAndroidLocation();
}
}
private void initFusedLocation() {
mLocationRequest = new LocationRequest();
mLocationRequest.SetInterval(LocationTrackerActivity.MIN_TIME_BW_UPDATES);
mLocationRequest.SetFastestInterval(LocationTrackerActivity.MIN_TIME_BW_UPDATES / 2);
mLocationRequest.SetSmallestDisplacement(LocationTrackerActivity.MIN_DISTANCE_CHANGE_FOR_UPDATES);
mLocationRequest.SetPriority(LocationRequest.PriorityHighAccuracy);
mGoogleApiClient = new GoogleApiClient.Builder(Application.Context)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.AddApi(LocationServices.API)
.Build();
}
protected override void OnResume() {
base.OnResume();
if (mGoogleApiClient.IsConnected) {
LocationServices.FusedLocationApi.RequestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
protected override void OnPause() {
base.OnPause();
// Stop location updates to save battery, but don't disconnect the GoogleApiClient object.
if (mGoogleApiClient.IsConnected) {
LocationServices.FusedLocationApi.RemoveLocationUpdates(mGoogleApiClient, this);
}
}
protected override void OnStart() {
base.OnStart();
mGoogleApiClient.Connect();
}
protected override void OnStop() {
base.OnStop();
// only stop if it's connected, otherwise we crash
if (mGoogleApiClient != null) {
mGoogleApiClient.Disconnect();
}
}
private void initAndroidLocation() {
}
private bool checkPlayServices() {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.Instance;
int resultCode = apiAvailability.IsGooglePlayServicesAvailable(activity);
if (resultCode != ConnectionResult.Success) {
// In case we want to tell the user to install or update Google Play Services
//if (apiAvailability.IsUserResolvableError(resultCode)) {
// apiAvailability.GetErrorDialog(activity, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST).Show();
//} else {
// Toast.MakeText(activity, "This device is not supported", ToastLength.Long).Show();
//}
return false;
}
return true; // has google play services installed
}
public double getLatitude() {
return currentLocation == null ? 0.0 : currentLocation.Latitude;
}
public double getLongitude() {
return currentLocation == null ? 0.0 : currentLocation.Longitude;
}
public bool canGetLocation() {
return locationAvailable;
}
public LatLng getLatLng() {
return new LatLng(currentLocation.Latitude, currentLocation.Longitude);
}
public void OnConnected(Bundle connectionHint) {
// Get last known recent location. If the user launches the activity,
// moves to a new location, and then changes the device orientation, the original location
// is displayed as the activity is re-created.
if (currentLocation == null && mGoogleApiClient.IsConnected) {
currentLocation = LocationServices.FusedLocationApi.GetLastLocation(mGoogleApiClient);
}
Console.WriteLine("location is about to be set to true");
locationAvailable = true;
LocationServices.FusedLocationApi.RequestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
public void OnConnectionSuspended(int cause) {
// GoogleApiClient will automatically attempt to restore the connection.
// Applications should disable UI components that require the service, and wait for a call to onConnected(Bundle) to re-enable them
if (cause == GoogleApiClient.ConnectionCallbacks.CauseServiceDisconnected) {
Toast.MakeText(activity, "Location Services disconnected. Please re-connect.", ToastLength.Long).Show();
} else if (cause == GoogleApiClient.ConnectionCallbacks.CauseNetworkLost) {
Toast.MakeText(activity, "Network lost. Please re-connect.", ToastLength.Long).Show();
}
}
public void OnConnectionFailed(ConnectionResult result) {
Console.WriteLine("Connection failed: " + result.ToString());
}
public void OnLocationChanged(Location location) {
currentLocation = location;
}
}
}
Here is the class that inherit the custom class:
using Android.App;
using Android.OS;
using Maps.Droid.LocationService;
namespace Maps.Droid {
[Activity(Label = "Map Activity")]
public class MapActivity : LocationTrackerActivity {
// Properties
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.map_activity);
var mapFrag = new MapViewFragment();
var ft = FragmentManager.BeginTransaction();
ft.Add(Resource.Id.map_container, mapFrag);
ft.Commit();
}
}
}
Here is the fragment in the inherited activity:
using System;
using Android.App;
using Android.OS;
using Android.Views;
using Android.Gms.Maps;
namespace Maps.Droid {
public class MapViewFragment : Fragment, IOnMapReadyCallback {
// private Activity activity;
private GoogleMap map;
private MapActivity parent;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.Inflate(Resource.Layout.map_fragment, null);
parent = ((MapActivity)Activity);
MapFragment frag = this.FragmentManager.FindFragmentById<MapFragment>(Resource.Id.map);
frag.GetMapAsync(this);
return view;
}
public void OnMapReady(GoogleMap googleMap) {
if (googleMap != null) {
map = googleMap;
var zoomVariance = 0.2;
var defaultZoom = 16f;
var currentZoomLevel = map.CameraPosition.Zoom;
map.MapType = GoogleMap.MapTypeNormal;
map.MyLocationEnabled = true;
map.CameraChange += delegate (object sender, GoogleMap.CameraChangeEventArgs e) {
if (Math.Abs(e.Position.Zoom - currentZoomLevel) < zoomVariance) {
return;
}
currentZoomLevel = e.Position.Zoom;
Console.WriteLine("Zooming " + currentZoomLevel);
};
map.UiSettings.ZoomControlsEnabled = true;
map.UiSettings.CompassEnabled = true;
map.UiSettings.SetAllGesturesEnabled(true); // Zoom, Tilt, Scroll, Rotate
if (parent.canGetLocation()) {
// ***** PROBLEM HERE ******* canGetLocation is set to true just afterwards.
map.AnimateCamera(CameraUpdateFactory.NewLatLngZoom(parent.getLatLng(), defaultZoom)); // Mosaic coordinates
}
}
}
}
}
I was thinking of implementing a call back to LocationTrackerActivity. So when the location services become available the class MapActivity then would be able to load the MapViewFragment within that custom callback. This way the location services would be loaded before the map. Therefore, this part of the code would always execute :
if (parent.canGetLocation()) {
// ***** PROBLEM HERE ******* canGetLocation is set to true just afterwards.
map.AnimateCamera(CameraUpdateFactory.NewLatLngZoom(parent.getLatLng(), defaultZoom)); // Mosaic coordinates
}
However I have no idea how to customise a callback. Maybe there are better solutions to this problem?
The problem can be because of OnConnected method. That's my OnConnected method for FusedLocationProvider in Xamarin. You can take a look on it.
public async void OnConnected(Bundle bundle)
{
Location location = LocationServices.FusedLocationApi.GetLastLocation(apiClient);
if (location != null)
{
latitude = location.Latitude;
longitude = location.Longitude;
locRequest.SetPriority(100);
locRequest.SetFastestInterval(500);
locRequest.SetInterval(1000);
await LocationServices.FusedLocationApi.RequestLocationUpdates(apiClient, locRequest, this);
}
}
When it is connected, i set the latitude and longitude values. Then in my onMapReady method, i create a new LatLng variable with these latitude and longitude variables.
void IOnMapReadyCallback.OnMapReady(GoogleMap myMap)
{
this.myMap = myMap;
myMarker = new MarkerOptions();
locCoordinates = new LatLng(latitude, longitude);
myMap.MapType = GoogleMap.MapTypeNormal;
myMap.UiSettings.ZoomControlsEnabled = true;
myMap.AnimateCamera(CameraUpdateFactory.NewLatLngZoom(locCoordinates, 10));
}
UPDATE:
I have managed to find a solution. If the user has google play services then use FusedLocation Services, if the user does not then we use Android Location Services. Then we only have to interact with one object of the type LocationTracker and everything is done by this interface:
namespace Maps.Droid.LocationService {
public interface LocationInterface {
void startLocationServices();
void stopLocationServices();
void pauseLocationServices();
void resumeLocationServices();
double getLatitude();
double getLongitude();
bool canGetLocation();
}
}
using System;
using Android.App;
using Android.Gms.Location;
using Android.Gms.Common.Apis;
using Android.OS;
using Android.Gms.Maps.Model;
using Android.Widget;
using Android.Locations;
using Android.Gms.Common;
using Android.Gms.Maps;
namespace Maps.Droid.LocationService {
public class FusedLocation : Java.Lang.Object, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener, Android.Gms.Location.ILocationListener {
private Activity activity;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private Location currentLocation;
private bool locationAvailable = false;
private GoogleMap map;
public FusedLocation(Activity activity) {
this.activity = activity;
mLocationRequest = new LocationRequest();
mLocationRequest.SetInterval(LocationTracker.MIN_TIME_BW_UPDATES);
mLocationRequest.SetFastestInterval(LocationTracker.MIN_TIME_BW_UPDATES / 2);
mLocationRequest.SetSmallestDisplacement(LocationTracker.MIN_DISTANCE_CHANGE_FOR_UPDATES);
mLocationRequest.SetPriority(LocationRequest.PriorityHighAccuracy);
mGoogleApiClient = new GoogleApiClient.Builder(Application.Context)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.AddApi(LocationServices.API)
.Build();
}
public Location getCurrentLocation() {
return currentLocation;
}
public void setMap(GoogleMap map) {
this.map = map;
}
public double getLatitude() {
return currentLocation.Latitude;
}
public double getLongitude() {
return currentLocation.Longitude;
}
public void OnResume() {
if (mGoogleApiClient.IsConnected) {
LocationServices.FusedLocationApi.RequestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
public void OnPause() {
// Stop location updates to save battery, but don't disconnect the GoogleApiClient object.
if (mGoogleApiClient.IsConnected) {
LocationServices.FusedLocationApi.RemoveLocationUpdates(mGoogleApiClient, this);
}
}
public void OnStart() {
mGoogleApiClient?.Connect();
}
public void OnStop() {
// only stop if it's connected, otherwise we crash
if (mGoogleApiClient.IsConnected) {
mGoogleApiClient?.Disconnect();
}
}
public LatLng getLatLng() {
return new LatLng(currentLocation.Latitude, currentLocation.Longitude);
}
public bool canGetLocation() {
return locationAvailable && currentLocation != null;
}
public void OnConnected(Bundle connectionHint) {
// Get last known recent location. If the user launches the activity,
// moves to a new location, and then changes the device orientation, the original location
// is displayed as the activity is re-created.
currentLocation = LocationServices.FusedLocationApi.GetLastLocation(mGoogleApiClient);
if (currentLocation != null) {
locationAvailable = true;
LocationServices.FusedLocationApi.RequestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
if (map != null) {
map.AnimateCamera(CameraUpdateFactory.NewLatLngZoom(getLatLng(), LocationTracker.DEFAULT_ZOOM));
}
}
}
public void OnConnectionSuspended(int cause) {
// GoogleApiClient will automatically attempt to restore the connection.
// Applications should disable UI components that require the service, and wait for a call to onConnected(Bundle) to re-enable them
if (cause == GoogleApiClient.ConnectionCallbacks.CauseServiceDisconnected) {
Toast.MakeText(activity, "Location Services disconnected. Please re-connect.", ToastLength.Long).Show();
} else if (cause == GoogleApiClient.ConnectionCallbacks.CauseNetworkLost) {
Toast.MakeText(activity, "Network lost. Please re-connect.", ToastLength.Long).Show();
}
}
public void OnLocationChanged(Location location) {
currentLocation = location;
}
public void OnConnectionFailed(ConnectionResult result) {
Console.WriteLine("Connection failed: " + result.ToString());
}
}
}
using System;
using Android.OS;
using Android.Locations;
using Android.Runtime;
using Android.App;
using Android.Content;
using Android.Widget;
using Android.Gms.Maps.Model;
using Java.Util.Concurrent;
namespace Maps.Droid.LocationService {
public class AndroidLocation : Java.Lang.Object, ILocationListener {
// Properties
private LocationManager locMgr;
private Activity activity;
private Location locationGPS, locationNetwork/*, locationPassive*/, currentLocation;
private bool locationAvailable = false;
private Android.Gms.Maps.GoogleMap map;
// UNCOMMNET
// private bool isPassiveEnabled = false; // Gets location from other apps that uses Location Services
// Initializer method (Constructor). Call this method onCreate
public AndroidLocation(Activity activity) {
this.activity = activity;
getLocation();
}
public Location getCurrentLocation() {
return currentLocation;
}
public void setMap(Android.Gms.Maps.GoogleMap map) {
this.map = map;
}
private Location getLocation() {
// Use Standard Android Location Service Provider
try {
locMgr = activity.GetSystemService(Context.LocationService) as LocationManager;
bool isGPSEnabled = locMgr.IsProviderEnabled(LocationManager.GpsProvider);
// Varying precision, Less power consuming. Combination of WiFi and Cellular data
bool isNetworkEnabled = locMgr.IsProviderEnabled(LocationManager.NetworkProvider);
// UNCOMMENT
// bool isPassiveEnabled = locMgr.IsProviderEnabled(LocationManager.GpsProvider);
// UNCOMMNET
//if (isPassiveEnabled) {
// locMgr.RequestLocationUpdates(LocationManager.PassiveProvider, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
// locationPassive = locMgr.GetLastKnownLocation(LocationManager.PassiveProvider);
//}
if (isGPSEnabled) {
locMgr.RequestLocationUpdates(LocationManager.GpsProvider, LocationTracker.MIN_TIME_BW_UPDATES, LocationTracker.MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
locationGPS = locMgr?.GetLastKnownLocation(LocationManager.GpsProvider);
}
if (isNetworkEnabled) {
locMgr.RequestLocationUpdates(LocationManager.NetworkProvider, LocationTracker.MIN_TIME_BW_UPDATES, LocationTracker.MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
locationNetwork = locMgr?.GetLastKnownLocation(LocationManager.NetworkProvider);
}
// UNCOMMENT - Method must be implement if PassiveLocation is to be used
// currentLocation = getBestLocation(locationGPS, locationNetwork, locationPassive);
currentLocation = getBestLocation(locationNetwork, locationGPS);
if (currentLocation != null) {
locationAvailable = true;
if (map != null) {
map.AnimateCamera(Android.Gms.Maps.CameraUpdateFactory.NewLatLngZoom(getLatLng(), LocationTracker.DEFAULT_ZOOM));
}
}
} catch (Exception e) {
Console.WriteLine("ERROR: getLocation() " + e.ToString());
}
return currentLocation;
}
// Determines the most recent and/or most accurate location
private Location getBestLocation(Location loc1, Location loc2) {
if (loc1 == null || loc2 == null) {
return loc1 ?? loc2; // If either location is null then return the not null location
}
long time1 = TimeUnit.Milliseconds.ToSeconds(loc1.Time);
long time2 = TimeUnit.Milliseconds.ToSeconds(loc2.Time);
long twiceUpdate = (LocationTracker.MIN_TIME_BW_UPDATES / 1000) * 2;
if (Math.Abs(time1 - time2) > twiceUpdate) { // If location times are more than twiceUpdate apart
if (time1 > time2) { // More time value, most current time
return loc1;
} else {
return loc2;
}
} else {
float accuracy1 = loc1.Accuracy;
float accuracy2 = loc2.Accuracy;
// Smaller the value (meters), the greater the accuracy
if (accuracy1 < accuracy2) {
return loc1;
} else {
return loc2;
}
}
}
public void OnStop() {
locMgr = null;
}
public void OnPause() {
locMgr?.RemoveUpdates(this);
}
public void OnStart() {
}
public void OnResume() {
if (locMgr == null || currentLocation == null) {
getLocation();
}
}
public bool canGetLocation() {
return locationAvailable;
}
public LatLng getLatLng() {
return new LatLng(currentLocation.Latitude, currentLocation.Longitude);
}
public double getLatitude() {
return currentLocation.Latitude;
}
public double getLongitude() {
return currentLocation.Longitude;
}
public void OnLocationChanged(Location location) {
currentLocation = getBestLocation(currentLocation, location);
}
// User disabled a provider
public void OnProviderDisabled(string provider) {
getLocation(); // Check if all providers are disabled and pop up alertDialog if they are so
}
// User enabled a provider
public void OnProviderEnabled(string provider) {
getLocation(); // Update all available providers for getting the best provider available
}
public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras) {
}
}
}
using Android.App;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.Gms.Maps.Model;
using Android.Gms.Maps;
using Android.Locations;
using Android.Content;
using Android.Widget;
namespace Maps.Droid.LocationService {
public class LocationTracker {
public static long MIN_DISTANCE_CHANGE_FOR_UPDATES = 5; // 5 meters
public static long MIN_TIME_BW_UPDATES = 1000 * 15; // 15 seconds ok, 5 seconds really fast, 30s slow
public static float DEFAULT_ZOOM = 16f;
private bool hasGooglePlayServices;
public GoogleApiClient mGoogleApiClient;
private FusedLocation fusedLocation;
private AndroidLocation androidLocation;
private bool locationIsDisabled;
public LocationTracker(Activity activity) {
if (locationIsDisabled = isLocationDisabled(activity)) {
showSettingsAlert(activity);
} else {
hasGooglePlayServices = checkPlayServices(activity);
if (hasGooglePlayServices) {
fusedLocation = new FusedLocation(activity);
} else {
androidLocation = new AndroidLocation(activity);
}
}
}
private void showSettingsAlert(Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetTitle("Location Services Not Active");
builder.SetMessage("Please enable Location Services and GPS");
builder.SetPositiveButton("OK", delegate {
// Show location settings when the user acknowledges the alert dialog
var intent = new Intent(Android.Provider.Settings.ActionLocationSourceSettings);
activity.StartActivity(intent);
});
builder.SetNegativeButton("Cancel", delegate {
Toast.MakeText(activity, "Location disabled by user", ToastLength.Short).Show();
});
AlertDialog alertDialog = builder.Create();
alertDialog.SetCanceledOnTouchOutside(false);
alertDialog.Show();
}
private bool isLocationDisabled(Activity activity) {
LocationManager locMgr = activity.GetSystemService(Context.LocationService) as LocationManager;
// More precise, More power consuming
bool isGPSEnabled = locMgr.IsProviderEnabled(LocationManager.GpsProvider);
// Varying precision, Less power consuming. Combination of WiFi and Cellular data
bool isNetworkEnabled = locMgr.IsProviderEnabled(LocationManager.NetworkProvider);
// UNCOMMENT
// bool isPassiveEnabled = locMgr.IsProviderEnabled(LocationManager.PassiveProvider);
// UNCOMMENT
// return !isGPSEnabled && !isNetworkEnabled && !isPassiveEnabled; // True only when the 3 location services are disabled
return !isGPSEnabled && !isNetworkEnabled; // True only when both location services are disabled
}
// Call this method at OnMapReady callback if initial zooming/animation on user's location is desired
public void setMap(GoogleMap map) {
if (locationIsDisabled) {
return;
}
if (hasGooglePlayServices) {
fusedLocation.setMap(map);
} else {
androidLocation.setMap(map);
}
}
public void OnResume() {
if (locationIsDisabled) {
return;
}
if (hasGooglePlayServices) {
fusedLocation.OnResume();
} else {
androidLocation.OnResume();
}
}
public void OnPause() {
if (locationIsDisabled) {
return;
}
if (hasGooglePlayServices) {
fusedLocation.OnPause();
} else {
androidLocation.OnPause();
}
}
public void OnStart() {
if (locationIsDisabled) {
return;
}
if (hasGooglePlayServices) {
fusedLocation.OnStart();
} else {
androidLocation.OnStart();
}
}
public void OnStop() {
if (locationIsDisabled) {
return;
}
if (hasGooglePlayServices) {
fusedLocation.OnStop();
} else {
androidLocation.OnStop();
}
}
private bool checkPlayServices(Activity activity) {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.Instance;
int resultCode = apiAvailability.IsGooglePlayServicesAvailable(activity);
if (resultCode == ConnectionResult.Success) {
return true;
}
return false;
}
public double getLatitude() {
if (locationIsDisabled) {
return 0;
}
if (hasGooglePlayServices) {
return fusedLocation.getCurrentLocation() == null ? 0.0 : fusedLocation.getLatitude();
} else {
return androidLocation.getCurrentLocation() == null ? 0.0 : androidLocation.getLatitude();
}
}
public double getLongitude() {
if (locationIsDisabled) {
return 0;
}
if (hasGooglePlayServices) {
return fusedLocation.getCurrentLocation() == null ? 0.0 : fusedLocation.getLongitude();
} else {
return androidLocation.getCurrentLocation() == null ? 0.0 : androidLocation.getLongitude();
}
}
public bool canGetLocation() {
if (locationIsDisabled) {
return false;
}
if (hasGooglePlayServices) {
return fusedLocation.canGetLocation();
} else {
return androidLocation.canGetLocation();
}
}
public LatLng getLatLng() {
if (locationIsDisabled) {
return null;
}
LatLng latlng;
if (hasGooglePlayServices) {
latlng = fusedLocation.getLatLng();
} else {
latlng = androidLocation.getLatLng();
}
return latlng;
}
public Location getCurrentLocation() {
if (hasGooglePlayServices) {
return fusedLocation.getCurrentLocation();
} else {
return androidLocation.getCurrentLocation();
}
}
}
}
Then to use it on your fragment or activity:
Initialise it at OnCreate:
Location tracker = new LocationTracker(this.Activity);
make a referent for the life cycles:
public override void OnResume() {
base.OnResume();
tracker.OnResume();
}
public override void OnPause() {
base.OnPause();
tracker.OnPause();
}
public override void OnStart() {
base.OnStart();
tracker.OnStart();
}
public override void OnStop() {
base.OnStop();
tracker.OnStop();
}
if you want the animation to zoom at the users location in the beginning, then you have to add this line of code when you have the googlemap:
tracker.setMap(googleMap); // Invoke this method if zooming/animating to the user's location is desired
Spent many days on this solution. Hope it can help somebody!

Resources