Categories
discuss

How to check if an element has been loaded on a page before running a script?

So I am creating my page in my companies template, which only allow us access to the body of the page. We don’t have access to the head tag and there are scripts loaded on a lower part of the page that we don’t have access to. One of these scripts dynamically loads an element onto the page. I need to run another script on that element, but because the element isn’t loaded on the page until after my script has already ran I can’t access that element. Is there a way I can check to see if that element has already been loaded on the page before running my script?

Let me know if I need to explain better.

<head>Don't have access</head>


<body>
   <!--Needs to manipulate the element created by the companyScript.js--> 
   <script src="myScript.js"></script>

   <!--Script that runs after mine, which I don't have access too-->
   <script src="companyScript.js">
       /*Dynamically adds <div class="element"></div> to page*/
   </script>
</body>

Answer

Sounds like a job for MutationObserver!

A MutationObserver is like an event listener: you can attach it to any DOM element to listen for changes:

var observer = new MutationObserver(function (mutationRecords) {
    console.log("change detected");
});

The callback is passed an array of MutationRecords, which hold different lists of added/deleted/modified nodes.

Then, we attach the observer to any node:

observer.observe(document.body, {childList: true});
// second argument is a config: in this case, only fire the callback when nodes are added or removed

Note: IE support is not amazing. (surprise, surprise) Only works on IE 11. (Edge supports it, though.)

Here’s another SO question about detecting changes to the DOM: Detect changes in the DOM

Snippet:

document.querySelector("button").addEventListener("click", function () {
  document.body.appendChild(document.createElement("span"));
});

var observer = new MutationObserver(function (m) {
  if (m[0].addedNodes[0].nodeName === "SPAN")
    document.querySelector("div").innerHTML += "Change Detected<br>";
});

observer.observe(document.body, {childList: true});
<button>Click</button>
<div></div>
Categories
discuss

Showing a Snackbar from inside a Service

When showing a SnackBar from inside an Activity, I have the rootView available. But I need to show a SnackBar from inside a Service, where I do not have a View available. How might I accomplish this?

As backstory: an activity starts a service to do a task. The Service needs to show a SnackBar depending on situations. I don’t want to Bind to the Service just for that. So how might I accomplish this? Normally I could show a Toast, but I need the user to be able to read the message and confirm so.

Answer

The Snackbar needs a View to be displayed, so if you want to show snackbars on your app depending on the state of your Service you’ll have to either bind it to your Activity or broadcast a message through the LocalBroadcastManager and show a message on your View.

I don’t think there’s any other way around it, you’ll have to communicate with your Activity or Fragment somehow.

Snackbars are not like Toasts that only need a context, so if you want to display it out of your app, I believe you can’t with the class provided by Android.

As from the design guidelines:

Placement

Snackbars appear above most elements on screen, and they are equal in elevation to the floating action button. However, they are lower in elevation than dialogs, bottom sheets, and navigation drawers.

It’s not explicit, but you can reach the conclusion that it’ll only display inside your app views. So, again, you’ll have to communicate with your visible view somehow.


Snippets on broadcasting a message:

Sender (on your Service)

private void doSendBroadcast(String message) {
    Intent it = new Intent("EVENT_SNACKBAR");

    if (!TextUtils.isEmpty(message))
        it.putExtra(EXTRA_RETURN_MESSAGE,message);

    LocalBroadcastManager.getInstance(mContext).sendBroadcast(it);
}

Receiver (on your Activity)

private BroadcastReceiver mMessageReceiver = null;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Other stuff.

    mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Do something
        }
    };
}

@Override
public void onResume() {
    super.onResume();

    LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("EVENT_SNACKBAR"));
}

More on bound services here.

And about LocalBroadcastManager here on this question.

Update: You could also make use of an EventBus to communicate with your visible view, as it works on a Publisher/Subscriber fashion. You could even make use of the concept of Sticky events to make sure the Snackbar will be displayed once the app is visible again.

Take a look at this answer of mine on how to use the Event Bus.

Categories
discuss

How can i set a tag for viewpager fragments?

I,m using slidingTabLayout with 4 tabs and a viewpager and 4 fragment class for every tab.

i need to reload one of my fragments (first tab) in my project when user click some button, and for this i need to have a tag for that fragment

my question is : how can i set a tag for one of viewpagers fragment???

My MainActivity tab and viewpager codes:

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener {



Toolbar toolbar;
ViewPager pager;
ViewPagerAdapter adapter;
SlidingTabLayout tabs;
CharSequence Titles[]={"نوع","قیمت","برند","همه"};
int Numboftabs = 4;




@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);




    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "درج آگهی رایگان", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });



    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);




    // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
    adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs);

    // Assigning ViewPager View and setting the adapter
    pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(adapter);

    // Assiging the Sliding Tab Layout View
    tabs = (SlidingTabLayout) findViewById(R.id.tabs);
    tabs.setDistributeEvenly(true); // To make the Tabs Fixed set this true, This makes the tabs Space Evenly in Available width

    // Setting Custom Color for the Scroll bar indicator of the Tab View
    tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
        @Override
        public int getIndicatorColor(int position) {
            return getResources().getColor(R.color.colorWhite);
        }
    });

    // Setting the ViewPager For the SlidingTabsLayout
    tabs.setViewPager(pager);
    pager.setCurrentItem(3);




} and ....

Thank you a lot

Answer

There are couple of solution for this.

1). When the FragmentPagerAdapter adds a Fragment to the FragmentManager, it uses a special tag based on the particular position that the Fragment will be placed. So you can simply get the current visible Fragment in your ViewPager using these lines of code

 Fragment fragment = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
 // based on the current position you can then cast the page to the correct Fragment class and call some method inside that fragment to reload the data:
 if (0 == ViewPager.getCurrentItem() && null != fragment) {
      ((TabFragment1)fragment).reloadFragmentData();     
 } 

2). You should keep track of all the active fragments in the FragmentStatePagerAdapter. For demo sample you can refer to second approach here

3). You can make use of EventBus to post events to your Fragment from anywhere. All you have to do is to register your Fragment for that specific event. For official documentation refer this

Categories
discuss

Basic Authentication Using JavaScript

I am building an application that consumes the Caspio API. I am having some trouble authenticating against their API. I have spent 2-3 days trying to figure this out but it may be due to some understanding on my end. I have read countless articles on stackoverflow post and otherwise but have not solved the issue. Below is a code example of my solution based on what i have looked at and i am getting a 400 Status code message; What am i doing wrong here? (Please provide well commented code example and i would prefer to NOT have links posted here referencing other material as i have looked at these extensively. Thanks!):

Some references i have looked at:

1) Pure JavaScript code for HTTP Basic Authentication?

2) How to make http authentication in REST API call from javascript

I would like to use this authentication method as described by caspio below:

As an alternative to including credentials in the request body, a client can use the HTTP Basic authentication scheme. In this case, authentication request will be setup in the following way:

Method: POST

URL: Your token endpoint

Body: grant_type=client_credentials

Header parameter:

Authorization: Basic Basic authentication realm

Below are my Javascript and HTML code.

JavaScript:

var userName = "clientID";
var passWord = "secretKey";

function authenticateUser(user, password)
{
    var token = user + ":" + password;

    // Should i be encoding this value????? does it matter???
    // Base64 Encoding -> btoa
    var hash = btoa(token); 

    return "Basic " + hash;
}

function CallWebAPI() {

    // New XMLHTTPRequest
    var request = new XMLHttpRequest();
    request.open("POST", "https://xxx123.caspio.com/oauth/token", false);
    request.setRequestHeader("Authorization", authenticateUser(userName, passWord));  
    request.send();
    // view request status
    alert(request.status);
    response.innerHTML = request.responseText;
}

HTML:

<div>
<div id="response">

</div>
<input type="button" class="btn btn-primary" value="Call Web API" onclick="javascript:CallWebAPI();" />

Answer

After Spending quite a bit of time looking into this, i came up with the solution for this; In this solution i am not using the Basic authentication but instead went with the oAuth authentication protocol. But to use Basic authentication you should be able to specify this in the “setHeaderRequest” with minimal changes to the rest of the code example. I hope this will be able to help someone else in the future:

var token_ // variable will store the token
var userName = "clientID"; // app clientID
var passWord = "secretKey"; // app clientSecret
var caspioTokenUrl = "https://xxx123.caspio.com/oauth/token"; // Your application token endpoint  
var request = new XMLHttpRequest(); 

function getToken(url, clientID, clientSecret) {
    var key;           
    request.open("POST", url, true); 
    request.setRequestHeader("Content-type", "application/json");
    request.send("grant_type=client_credentials&client_id="+clientID+"&"+"client_secret="+clientSecret); // specify the credentials to receive the token on request
    request.onreadystatechange = function () {
        if (request.readyState == request.DONE) {
            var response = request.responseText;
            var obj = JSON.parse(response); 
            key = obj.access_token; //store the value of the accesstoken
            token_ = key; // store token in your global variable "token_" or you could simply return the value of the access token from the function
        }
    }
}
// Get the token
getToken(caspioTokenUrl, userName, passWord);

If you are using the Caspio REST API on some request it may be imperative that you to encode the paramaters for certain request to your endpoint; see the Caspio documentation on this issue;

NOTE: encodedParams is NOT used in this example but was used in my solution.

Now that you have the token stored from the token endpoint you should be able to successfully authenticate for subsequent request from the caspio resource endpoint for your application

function CallWebAPI() {
    var request_ = new XMLHttpRequest();        
    var encodedParams = encodeURIComponent(params);
    request_.open("GET", "https://xxx123.caspio.com/rest/v1/tables/", true);
    request_.setRequestHeader("Authorization", "Bearer "+ token_);
    request_.send();
    request_.onreadystatechange = function () {
        if (request_.readyState == 4 && request_.status == 200) {
            var response = request_.responseText;
            var obj = JSON.parse(response); 
            // handle data as needed... 

        }
    }
} 

This solution does only considers how to successfully make the authenticated request using the Caspio API in pure javascript. There are still many flaws i am sure…

Categories
discuss

Proper way to get file path when it’s picked using ACTION_GET_CONTENT

I have a file picker implemented in my app like this:

Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Title"), FILE_PICK);

It’s a known issue that you can’t easily get the actual file location this way because Intent will return some weird Uri that you can’t really use to create a File object.

I’m using this method to get the actual File path:

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

This works for some files, and for some not. Right now I noticed that it doesn’t work when I pick a file from “Downloads” folder. It skips all if statements above and returns null.

What would be the correct way, that would be more reliable to get the actual selected File path?

Answer

I have a file picker implemented in my app like this

That is not a “file picker”. It is a content picker.

I need a way to be able to pick any file on Android device so that I can upload it to my backend.

Um, well, that depends a fair bit on how literal you are in that sentence.

ACTION_GET_CONTENT is not a problem in general. However, what you get are not necessarily files. However, you can still upload them, assuming that whatever you are using for the uploading allows you to upload from an InputStream. If so, use openInputStream() on a ContentResolver, passing in the Uri, to get the InputStream to use.

If you really need it to be actual files, you can implement a file-picker UI directly in your app. There are libraries for this. However, you will not be able to access all files — in particular, you cannot use this to upload files from removable storage on Android 4.4+.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..