I'm still learning how to extract data from a website and I really hope I'll get some nice answers adequate for a starter. Anyways, my goal here is to extract the data in the background of my app(without openning and showing it in my app). The idea is that data then would be stored for later use.
The API I'm using has 2 GetMethods:
GetProductJSON(which has JSON Response) and GetProduct(with a Comma Seperated Values(CSV) Response)
Here is an example of the JSON Response website:
{"0":{"productname":"Neutrogena Lips Stick 4.8g","imageurl":"http://ecx.images- amazon.com/images/I/31E1ct854gL._SL160_.jpg","producturl":"","price":"5.65","currency":"USD","saleprice":"","storename":"N/A"}}
The Comma Seperated Values Response looks like this:
"productname","imageurl","producturl","price","currency","saleprice","storename"
"Neutrogena Lips Stick 4.8g","http://ecx.images-amazon.com/images/I/31E1ct854gL._SL160_.jpg","","5.65","USD","","N/A"
Here is how I call the website:
url = url.replace("{CODE}", codeValue);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
cardPresenter.setPendingIntent(createPendingIntent(getContext(), intent));
Any suggestions on how to make this a background task and how to actually get the data in java so that I can use them on a Livecard.
First, you need to have access to the Internet. Include the following permission into your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Using GDK and AsyncTask:
import android.os.AsyncTask;
public class RetrieveData extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... resource) {
String data;
try {
URL url = new URL(resource[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(connection.getInputStream());
data = convertStreamToString(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
private String convertStreamToString(InputStream in) {
Scanner s = new Scanner(in);
return s.useDelimiter("\\A").hasNext() ? s.next() : "";
}
}
The method convertStreamToString() is described there.
In your Service or Activity:
String retrievedData;
try {
retrievedData = new RetrieveData().execute("http://www.example.com/GetProductJSON").get();
} catch (Exception e) {
e.printStackTrace();
}
// Process data
Hope that helps.
You have at least these choices using GDK: 1) create an asynch task; or 2) create a private service that you assign tasks to periodically.
The first thing you will need to do in your Android Application is to add permission to access the Internet in your AndroidManifest.xml file. Add this tag as a sibling of <application>.
<uses-permission android:name="android.permission.INTERNET"/>
You will then need to consider in what manner you will make the HTTP requests. In an ideal application, you need to use the AsyncTask class to make these requests to avoid blocking the UI thread.
If you are just looking for a quick proof of concept, you can permit these requests on the UI thread by modifying your policy. Add this code to your onCreate() method in the MainActivity
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
This is not an advisable long term solution since it blocks the UI thread, but there are StackOverflow topics on it. How to fix android.os.NetworkOnMainThreadException?.
Here is another article on the concept of an AsyncTask http://www.androiddesignpatterns.com/2012/06/app-force-close-honeycomb-ics.html.
Although you seem to be indicating you're using the GDK, you may want to consider a server-push via the Mirror API instead. Since you need to fetch the information via the network anyway, you're losing out on many of the advantages the GDK offers.
With the Mirror API, you would create a new timeline item with timeline.insert and save the id of the card that was created. You would probably want to give your user the option to pin the card so it is placed in the "now" area of the timeline.
When updating, you can call timeline.update with the new information.
Keep in mind that you do need to update the card periodically or it may fall off the timeline or out of the pinned area after seven days of inactivity.
Related
It's my first project in Android Studio, basically I'm trying to develop a map with multiple markers using Mapbox. So, my problem is when loading the markers on the map it takes a lot of time to load ~3-5 seconds and the app freezes until i get the json from my API call.
Here is my retrofit2 call to API:
private void getNearbyStations() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("***")//my API, not relevant
.addConverterFactory(GsonConverterFactory.create())
.build();
jsonPlaceHolderApi = retrofit.create(JsonPlaceHolderApi.class);
Utilizator utilizator = Utilizator.getUtilizatorInstance();
Call<ResponseNearbyStations> call = jsonPlaceHolderApi.getNearbyStations(utilizator.getAuthentificationKey(), 47.1744354, 27.5746688);//Static Lat and Long for test, in future will use current location
try {
ResponseNearbyStations body = call.execute().body();
JsonObject jsonObject = body.getData();
JsonArray ja_data = jsonObject.getAsJsonArray("stationAround");
Station[] statiiPrimite = gson.fromJson(ja_data, Station[].class);
stationList = new ArrayList<>(Arrays.asList(statiiPrimite));
} catch (IOException e) {
e.printStackTrace();
}
}
I'm saving all my Stations in an ArrayList called stationList. In Station class i have the lat and long coordinates besides other information.
Here is my addMarkers function:
private void addMarkers(#NonNull Style loadedMapStyle) {
List<Feature> features = new ArrayList<>();
for(Station statie:stationList){
features.add(Feature.fromGeometry(Point.fromLngLat(Double.valueOf(statie.getCoordinates().getLongitude()),
Double.valueOf(statie.getCoordinates().getLatitude()))));
}
loadedMapStyle.addSource(new GeoJsonSource(MARKER_SOURCE, FeatureCollection.fromFeatures(features)));
loadedMapStyle.addLayer(new SymbolLayer(MARKER_STYLE_LAYER, MARKER_SOURCE)
.withProperties(
PropertyFactory.iconAllowOverlap(true),
PropertyFactory.iconIgnorePlacement(true),
PropertyFactory.iconImage(MARKER_IMAGE),
PropertyFactory.iconOffset(new Float[]{0f, -52f})
));
}
So after several searching i find out that the "problem" here is that i'm using call.execute() in getNearbyStations() which is not async so the main thread is waiting for the Stations to load. I tried to use call.enqueue but after that i got another problem, in my function addMarkers i get NullPointerException because stationList doesn't have enough time to load in
for(Station statie:stationList){
features.add(Feature.fromGeometry(Point.fromLngLat(Double.valueOf(statie.getCoordinates().getLongitude()),
Double.valueOf(statie.getCoordinates().getLatitude()))));
}
I'm guessing that i have to use some sort of Threading to solve this problem, but i'm a beginner in using Threads for Android Studio, and i couldn't figure it out.
I think the solution would be:
1.Display the map empty
2.Add markers after they load.
In this way the user doesn't experience any freezing. Any idea how to solve this problem is welcome.
Since the problem is that you want the application not to wait of the synchronous function, I would recommend to use an asynchronous task for that. You can then execute the addMarkers function once the onPostExecute callback of the async task is invoked. But make sure to run the addMarkers only after the style has been set in your onMapReady
Please see this documentation on how to use an asynchronous task: https://developer.android.com/reference/android/os/AsyncTask
The nice side effect you get by using an aysnchronous task, is that Android will execute it in a different thread and thereby will take load off the main thread.
This question already has answers here:
Android L (5.x) Turn ON/OFF "Mobile Data" programmatically
(3 answers)
Closed 2 years ago.
Although this is a "duplicate", the current answers are out of date and, mostly, no longer apply. I thought it would be good to provide an updated resource here, if possible, to save people time, as I have just done, researching this issue.
I've been googling around to see the latest information on being able to enable and disable mobile data from within an app (if wifi is not available).
This is one of the latest things I can find:
Did you know you can no longer Disable/Enable Data on lollipop from a widget?
There is an answer to that, I quote:
There was never an API for it. Developers were using a workaround by calling the method via Reflections. All Google did was close this "exploit".
There is also this discussion:
Replacement for setMobileDataEnabled() api
Which is Feb 2015.
There are these questions here:
How to disable Mobile Data on Android
This was asked in 2010 and the latest answer was updated with a one liner on Dec 2014.
Enable/disable data connection in android programmatically
And this, the accepted answer in 2012.
What's the latest on this issue?
Can it still be done?
It wont work on non-rooted phone as they added MODIFY_PHONE_STATE permission check. This permission is only given to system or signature apps refer-here.
Check the code below from PhoneInterfaceManager:
#Override
public void setDataEnabled(boolean enable) {
enforceModifyPermission();
mPhone.setDataEnabled(enable);
}
private void enforceModifyPermission() {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
Unless you have a rooted phone I don't think you can enable and disable data programmatically because in order to do so we have to include MODIFY_PHONE_STATE permission which is only given to system or signature apps.
setMobileDataEnabled() method is no longer callable via reflection. It was callable since Android 2.1 (API 7) to Android 4.4 (API 19) via reflection, but as of Android 5.0 and later, even with the rooted phones, the setMobileDataEnabled() method is not callable.
Fast forward to the end of 2018...
The short answer is it is no longer allowed to enable/disable mobile data programmatically. Here is the solution that I use all the time.
This code opens system data usage settings screen, where the user can disable mobile data manually.
public void enableDisableMobileData() {
Intent intent = new Intent();
intent.setComponent(new ComponentName(
"com.android.settings",
"com.android.settings.Settings$DataUsageSummaryActivity"));
startActivity(intent);
}
EDIT 2019:
The answer above does not work starting on API 28. This is what works:
Intent intent = new Intent(Settings.ACTION_DATA_USAGE_SETTINGS);
startActivity(intent);
None of the above worked for me, but if your device is rooted, you may use this for enabling it.
private void enableMobileData(){
try {
String[] cmds = {"svc data enable"};
Process p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
for (String tmpCmd : cmds) {
os.writeBytes(tmpCmd + "\n");
}
os.writeBytes("exit\n");
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
And this for disabling it:
private void disableMobileData(){
try {
String[] cmds = {"svc data disable"};
Process p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
for (String tmpCmd : cmds) {
os.writeBytes(tmpCmd + "\n");
}
os.writeBytes("exit\n");
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
Easy Method.
public void setMobileDataState(boolean mobileDataEnabled)
{
try{
ConnectivityManager dataManager;
dataManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
Method dataMtd = ConnectivityManager.class.getDeclaredMethod("setMobileDataEnabled", boolean.class);
dataMtd.setAccessible(true);
dataMtd.invoke(dataManager, mobileDataEnabled);
}catch(Exception ex){
//Error Code Write Here
}
}
This code shows that I have two videos in my raw file and I want the second one to play after the first one finishes automatically.
VideoView videoView = (VideoView) findViewById(R.id.videoView);
videoView.start();
videoView.setVideoPath("android.resource://" + getPackageName() + "/"+R.raw.badweather);
MediaController mc = new MediaController(this);
mc.setAnchorView(videoView);
videoView.setMediaController(mc);
i=1;
VideoView videoView2 = (VideoView) findViewById(R.id.videoView2);
videoView2.start();
videoView2.setVideoPath("android.resource://" + getPackageName() + "/" + R.raw.rain);
MediaController mc2 = new MediaController(this);
mc2.setAnchorView(videoView2);
videoView2.setMediaController(mc2);
Here's what I'd suggest. The beginning of this response assumes you'd actually prefer to use a single VideoView. If that's not the case, see notes at the end.
Make a queue of resource uris for videos. Something like:
// "list" is fine for this
List<String> videoUris = new ArrayList<>();
Now, write a code which plays a video:
public void playVideo(final String videoUri) {
// hook up to video view, etc.
}
From here, you have maybe two choices:
From a Completion Callback
One approach is to listen for a video completion callback, and then do something like:
public void onVideoCompletedCallback(final String completedVideoUri) {
if (!videoUris.isEmpty()) {
playVideo(videoUris.remove(0));
}
}
From a Background Thread
Alternately, you could potentially run a thread which loops through them if you implement playVideo() to be blocking.
new Thread() {
#Override
public void run() {
while (!videoUris.isEmpty()) {
// assume playVideo() blocks until the video
// is done playing.
playVideo(videoUris.remove(0));
}
}
}.start();
In both cases, I've only suggested some workable approaches. There'll still be some work done to translate them into specific platform bindings on Android.
But if you're still totally lost, start by looking at onPlaybackStateChanged(), which may be roughly a drop-in substitute for whatever I called the callback, above.
On Android O, Use the builtin Queue
Actually, since I just now pulled up that doco, it looks like MediaController actually already supports a queue as of Android O preview. So, third option, on Android O: use addQueueItem(). But obviously since almost literally noone will be using Android O Previous here as of April 2017, option 3 is more academic than practical.
Using k-many VideoViews
If you do actually need and want to do this, then adapt the code above to use a SortedMap. The map will be from resource uri to view id. In other words, from the uri to the video source, to the android id for the videoview in which to play it. The SortedMap is chosen instead of a HashMap as it will preserve the ordering of the videos into the sequence you choose to play them.
Map<String, Integer> videoToViewMap = new SortedMap<>();
videoToViewMap.put("android://blah/blah/video.mp4", R.id.videoView1);
// ... more
Several of the calls on what used to be the videoUris will be the same -- isEmpty() for example. But of course instead of doing things like .remove(), you'll have to write logic that removes the first item from /map/.
I am working on a Spring-MVC application and I would like to integrate Dropbox functionality into it. As I read the examples, I saw that there is some code which I can use. But this involves the user copy pasting the access token, which is not applicable in real world applications, plus I cannot find a way to set the redirect URL when authentication is complete. What changes should I make so the code does not need to copy pasted, but can be retrieved directly.
Code :
public void connectToDropbox() {
DbxAppInfo appInfo = new DbxAppInfo(APP_KEY, APP_SECRET);
DbxRequestConfig config = new DbxRequestConfig("JavaTutorial/1.0",
Locale.getDefault().toString());
DbxWebAuthNoRedirect webAuth = new DbxWebAuthNoRedirect(config, appInfo);
// Have the user sign in and authorize your app.
String authorizeUrl = webAuth.start();
System.out.println("1. Go to: " + authorizeUrl);
System.out.println("2. Click \"Allow\" (you might have to log in first)");
// No, I dont want to copy the authorization code.
System.out.println("3. Copy the authorization code.");
String code = null;
try {
code = new BufferedReader(new InputStreamReader(System.in)).readLine().trim();
} catch (IOException e) {
e.printStackTrace();
}
Controller code :
#RequestMapping(value = "/logindropbox")
public String loginIntoDropbox(){
ConnectDropbox connectDropbox = new ConnectDropbox();
connectDropbox.connectToDropbox();
return "rediect:/dashboard";
}
There was only one answer I could find on SO, but that was of no use. Any help would be nice. Thanks a lot. :-)
[Cross-linking for reference: https://www.dropboxforum.com/hc/communities/public/questions/203308909-Dropbox-authentication-without-copy-pasting-the-access-token-manually-in-Java- ]
The Dropbox Java Core SDK tutorial does use the flow where the user copies and pastes the authorization code manually. This is done using the provided DbxWebAuthNoRedirect class.
For apps where a redirect URI can be used to deliver the authorization code automatically, you'll want to use the DbxWebAuth class instead. The documentation has some sample code:
https://dropbox.github.io/dropbox-sdk-java/api-docs/v1.7.x/com/dropbox/core/DbxWebAuth.html
There's also a "web-file-browser" sample app included with the SDK download that uses DbxWebAuth.
I have developed an application in Android that downloads a lot of data through XML query REST.
The problem is that every time you start the app takes a long time to download the data.
My question is:
How can I serialize these data, and update perhaps after a certain period of time?
I want some advice or idea to implement, or even better an example.
thanks
Use that one to serialize:
http://simple.sourceforge.net/
You can schedule an async task or a thread to update it.
Example for a thread that serializes data incl. a lock (only parts of the code)
static final Object sDataLock = new Object();
Serializer mSerializer;
class AsyncSave implements Runnable
{
Object mSerialize;
File mStorage;
public AsyncSave(Object serialize, File storage)
{
mSerialize = serialize;
mStorage = storage;
}
#Override
public void run()
{
try {
synchronized (sDataLock) {
// write
mSerializer.write(mSerialize, mStorage);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}
How can I serialize these data, and update perhaps after a certain period of time?
You can use thread/async task if you want certain data to be downloaded in certain activity. The async task/thread will be destroyed if the activity is destroyed.
If you want to download the data in certain time, use a Service instead. With a service, your data will be downloaded even if the apps is closed. For example, you can set your apps to download certain data once a day.