please I need your help after searching a lot without issues.
I have an demostration app to use an second screen attached to my device.
I have the source code of the app, they use the Mediarouter class and an class named LauncherSecondScreen extended from the Presentation class
I have tried to make the app as an service to keep runnig the app in background, but the mediarouter callback seems running only on the princpal thread ( I'm not sure I am just a beginner in android dev).
I have the full code of the app : there is two layout activity one showed on the princpal screen and the other on the second screen:
public class MainActivity extends Activity {
private final String TAG = "PresentationWithMediaRouterActivity";
private MediaRouter mMediaRouter;
private LauncherSecondScreen mPresentation;
private boolean mPaused;
/**
* Initialization of the Activity after it is first created. Must at least
* call {#link android.app.Activity#setContentView setContentView()} to
* describe what is to be displayed in the screen.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
// Be sure to call the super class.
super.onCreate(savedInstanceState);
// Get the media router service.
mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE);
// See assets/res/any/layout/presentation_with_media_router_activity.xml for this
// view layout definition, which is being set here as
// the content of our screen.
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
// Be sure to call the super class.
super.onResume();
// Listen for changes to media routes.
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback);
// Update the presentation based on the currently selected route.
mPaused = false;
updatePresentation();
}
private void updatePresentation() {
// Get the current route and its presentation display.
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_LIVE_VIDEO);
Display presentationDisplay = route != null ? route.getPresentationDisplay() : null;
// Dismiss the current presentation if the display has changed.
if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
Log.i(TAG, "Dismissing presentation because the current route no longer "
+ "has a presentation display.");
mPresentation.dismiss();
mPresentation = null;
}
// Show a new presentation if needed.
if (mPresentation == null && presentationDisplay != null) {
Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
mPresentation = new LauncherSecondScreen(this, presentationDisplay);
mPresentation.setOnDismissListener(mOnDismissListener);
try {
mPresentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Log.w(TAG, "Couldn't show presentation! Display was removed in "
+ "the meantime.", ex);
mPresentation = null;
}
}
// Update the contents playing in this activity.
updateContents();
}
private void updateContents() {
// Show either the content in the main activity or the content in the presentation
// along with some descriptive text about what is happening.
if (mPresentation != null) {
if (mPaused) {
mPresentation.dismiss();//getSurfaceView().onPause();
} else {
mPresentation.show();//getSurfaceView().onResume();
}
} else {
/* mInfoTextView.setText("presentation_with_media_router_now_playing_locally");
mSurfaceView.setVisibility(View.VISIBLE);
if (mPaused) {
mSurfaceView.onPause();
} else {
mSurfaceView.onResume();
}*/
}
}
private final MediaRouter.SimpleCallback mMediaRouterCallback =
new MediaRouter.SimpleCallback() {
#Override
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
updatePresentation();
}
#Override
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
updatePresentation();
}
#Override
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
updatePresentation();
}
};
/**
* Listens for when presentations are dismissed.
*/
private final DialogInterface.OnDismissListener mOnDismissListener =
new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
if (dialog == mPresentation) {
Log.i(TAG, "Presentation was dismissed.");
mPresentation = null;
updateContents();
}
}
};
#SuppressLint({"NewApi"})
public class LauncherSecondScreen extends Presentation
{
public LauncherSecondScreen(Context paramContext, Display paramDisplay)
{
super(paramContext, paramDisplay/*,android.R.style.Theme_Holo_Light_Dialog_NoActionBar*/);
}
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(R.layout.dialog_second_screen_content);
//// this.iv_secondScreen_banner = ((ImageView)findViewById(R.id.titleImage));
}
}
}
the app is well, it make one view in the princpale screen and a second view in the second screen , but when i resume the app to background the second screen take the same view of the first screen.
I want to keep the second view showing in the second screen even i resume the app to use another app
Related
it's my first question, so sorry for bad formatting or anything like this.
I'm trying to add info windows to markers, which are clustered by ClusterManager from google utils, but my item does not get rendered by my custom InfoWindowAdapter and that's what i'm trying to do.
After onMapReady callback, i initialize cluster manager like this:
mClusterManager = new ClusterManager<>(this, mMap);
mClusterManager.setRenderer(new ClusterMarkerRenderer(this, mMap, mClusterManager));
mClusterManager.setOnClusterClickListener(cluster -> {
LatLngBounds.Builder builder = LatLngBounds.builder();
for (CarMapItem item : cluster.getItems()) {
builder.include(item.getPosition());
}
final LatLngBounds bounds = builder.build();
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
return true;
});
mMap.setInfoWindowAdapter(new InfoWindowCarAdapter(this));
ClusterMarkerRenderer Class (only important parts):
public class ClusterMarkerRenderer extends DefaultClusterRenderer<CarMapItem> {
public ClusterMarkerRenderer(Context context, GoogleMap map, ClusterManager<CarMapItem> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected void onBeforeClusterItemRendered(CarMapItem item, #NotNull MarkerOptions markerOptions) {
if (item.getIcon() != null) {
markerOptions.icon(item.getIcon());
}
if (!item.hasCustomBitmap()) {
markerOptions.rotation(item.getRotation());
}
markerOptions.title(item.getName());
markerOptions.anchor(0.5f, 0.5f);
markerOptions.flat(true);
markerOptions.infoWindowAnchor(0.5f, 0.5f);
markerOptions.snippet(item.getImei() + "#" + item.getPosition().latitude
+ "#" + item.getPosition().longitude + "#" + item.getRotation() + "#" + item.getIconName() + "#" + item.getName());
super.onBeforeClusterItemRendered(item, markerOptions);
}
#Override
protected void onClusterItemRendered(#NotNull CarMapItem item, #NotNull Marker marker) {
super.onClusterItemRendered(item, marker);
marker.setTag(item);
}
}
This is where i set marker options, before cluster item gets rendered, my icon is set and everything is fine, but after rendering, i just set my item as tag to retrieve information for drawing and it won't render anything after this. And there is my last class, InfoWindowCarAdapter:
public class InfoWindowCarAdapter implements GoogleMap.InfoWindowAdapter {
private View myContentsView;
private Activity context;
#SuppressLint("InflateParams")
public InfoWindowCarAdapter(Activity context) {
this.context = context;
myContentsView = context.getLayoutInflater().inflate(R.layout.item_car_info, null);
}
#Override
public View getInfoContents(Marker marker) {
Log.e(TAG, "GET INFO CONTENTS FROM MARKER");
CarMapItem object = (CarMapItem) marker.getTag();
if (object != null) {
TextView tvTitle = myContentsView.findViewById(R.id.txt_car_name);
if (object.getItemType() == 1) {
((TextView) myContentsView.findViewById(R.id.txt_adit_info)).setText(String.format(Locale.getDefault(), "%s", Helper.getFormatedAlarmWithVars(context, object.getSpeed(),
object.getIconName(), object.getVar2())));
(myContentsView.findViewById(R.id.txt_status)).setVisibility(View.GONE);
(myContentsView.findViewById(R.id.ic_status_icon)).setVisibility(View.GONE);
} else {
TextView title = myContentsView.findViewById(R.id.txt_status);
ImageView icon = myContentsView.findViewById(R.id.ic_status_icon);
((TextView) myContentsView.findViewById(R.id.txt_adit_info)).setText(String.format(Locale.getDefault(), "%s", "Click for more info"));
if (object.getCarEngineStatus() == 1) {
title.setText(String.format(Locale.getDefault(), "%d KM/h", object.getSpeed()));
} else {
title.setText(Helper.formatSecondPrecise(context, object.getLastActivityTime()));
}
icon.setImageResource(Helper.getIconResIdByCarStatus(object.getVehicleStatus()));
(myContentsView.findViewById(R.id.txt_status)).setVisibility(View.VISIBLE);
(myContentsView.findViewById(R.id.ic_status_icon)).setVisibility(View.VISIBLE);
}
tvTitle.setText(object.getName());
return myContentsView;
}
return myContentsView;
}
#Override
public View getInfoWindow(Marker marker) {
return null;
}
}
When this class gets called, get info contents never does neither get info window, even tho i tried to log messages, still no output from this class, only from constructor.
Can u help me?
If anyone needs, i fixed it myself, and here is the mistake: In my activity, where i set InfoWindowAdapter to google maps object, i had to set ClusterManager's MarkerManager to Google Maps object and then set my custom info window adapter to cluster manager's marker collection just like this:
mMap.setInfoWindowAdapter(mClusterManager.getMarkerManager());
mClusterManager.getMarkerCollection().setInfoWindowAdapter(new InfoWindowCarAdapter(this));
if anybody needs this, it's here :)
I am not particularly sure how to ask this question, so I will just use images to explain
In my App, onClick of the download icon calls a download method for the video in the row. When it is completely downloaded, the Icon changes from black to green. A boolean flag is used to save this state in SharedPreference. This saved state is called again in my RecyclerView Adapter so the downloaded state can reflect when the app is relaunched.
THE CHALLENGE IS...
When the app relaunches, instead only the downloaded row icon to show green, the Icon turns green for every row even when they have not been downloaded. below is my code.
class DownloadReceiver extends ResultReceiver { //DownloadReceiver class
public DownloadReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == TichaDownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress"); //get the progress
//Set the progress to progressBarCir
progressBarCir.setProgress(progress);
icon_download.setVisibility(View.GONE);
progressBarCir.setVisibility(View.VISIBLE);
Log.i("STATUS", "DOWNLOADING>>>");
if (progress == 100) { // Downloade process is completed
isNotDownloaded = false; // Flagging that the video has been downloaded
progressBarCir.setVisibility(View.GONE);
// Setting the Download Icon to reflect the New color state
icon_download.setColorFilter(itemView.getContext().getResources().getColor(R.color.funnygreen));
icon_download.setVisibility(View.VISIBLE);
// Saving the boolean flag in SharedPreferece
SharedPreferences sharedPreferences = getSharedPreferences("com.example.instagramclone",MODE_PRIVATE);
sharedPreferences.edit().putBoolean("isDownloadedState",isNotDownloaded).commit();
// Logging of the save state to confirm state is saved.
boolean newState= sharedPreferences.getBoolean("isDownloadedState",true);
Log.i("STATE XCHANGE", "DOWNLOADED HENCE, "+String.valueOf(newState));
}
} else {
Log.i("STATUS", " NOT DOWNLOADING,BOSS");
}
}
}
Below is a snippet of Holder section of my Adapter Class
public LectureClassesHolder(#NonNull final View itemView) {
super(itemView);
// Now we ref each custom layout view item using the itemView
textViewTitle = itemView.findViewById(R.id.subject_topic);
textViewDescription = itemView.findViewById(R.id.subject_description);
textViewDuration = itemView.findViewById(R.id.subject_duration);
imageViewDownload = itemView.findViewById(R.id.download);
textViewUrl = itemView.findViewById(R.id.url_link);
SharedPreferences sharedPreferences = itemView.getContext().getSharedPreferences("com.example.instagramclone",Context.MODE_PRIVATE);
isNotDownloaded = sharedPreferences.getBoolean("isDownloadedState",true);
if (isNotDownloaded){
imageViewDownload.setColorFilter(itemView.getContext().getResources().getColor(R.color.black));
Log.i("DOWNLOAD STATE ","NOT Downloaded State is "+ isNotDownloaded);
}else {
imageViewDownload.setColorFilter(itemView.getContext().getResources().getColor(R.color.funnygreen));
Log.i("DOWNLOAD STATE ","NOT Downloaded State is "+ isNotDownloaded);
}
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && listener != null) {
listener.onItemClick(getSnapshots().getSnapshot(position), position, itemView);
}
}
});
// Incase e no work
imageViewDownload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && listener != null) {
listener.onViewItemClick(getSnapshots().getSnapshot(position), position, itemView);
}
}
});
}
I NEED a way to make the change effective for only the row concerned. Would appreciate any assistance on how to achieve this.
The problem is there is of course no association between the individual records and what is stored .The best solution would be to use a database with a table containing atleast a column for the download url and another for status,store the records with the desired status (for example use 0 for not downloaded status and 1 for downloaded status ) and update status column of the row with your url after download then you can query to check the status of the record .however a quick fix to your solution while still using shared prefrence would be to store the links in an array and check against that array see:
public static String all_records(Context act)
{
SharedPreferences prefs = act.getSharedPreferences("SHARED_PREFS_NAME", act.MODE_PRIVATE);
return prefs.getString("all_records", "[]");
}
public static void add_download(Activity act,String url)
{
JSONArray ja=new JSONArray();
try {
ja=new JSONArray(all_records(act));
JSONObject jo=new JSONObject();
jo.put("url",url);
ja.put(jo);
} catch (JSONException e) {
e.printStackTrace();
}
SharedPreferences.Editor saver =act.getSharedPreferences("SHARED_PREFS_NAME", act.MODE_PRIVATE).edit();
saver.putString("all_records",ja.toString());
saver.commit();
}
public static boolean is_downloaded(Activity act,String url)
{
JSONArray ja=new JSONArray();
try {
ja=new JSONArray(all_records(act));
for (int i=0;i<ja.length();i++)
{
try {
Log.e("Check ", "" + ja.getJSONObject(i).getString("url") + " Against " + url);
if (ja.getJSONObject(i).getString("url").equalsIgnoreCase(url)) {
return true;
}
}catch (Exception ex){}
}
} catch (JSONException e) {
e.printStackTrace();
}
return false;
}
in this case you would use it like this to when downloading
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == TichaDownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress"); //get the progress
//Set the progress to progressBarCir
progressBarCir.setProgress(progress);
icon_download.setVisibility(View.GONE);
progressBarCir.setVisibility(View.VISIBLE);
Log.i("STATUS", "DOWNLOADING>>>");
if (progress == 100) { // Downloade process is completed
isNotDownloaded = false; // Flagging that the video has been downloaded
progressBarCir.setVisibility(View.GONE);
// Setting the Download Icon to reflect the New color state
icon_download.setColorFilter(itemView.getContext().getResources().getColor(R.color.funnygreen));
icon_download.setVisibility(View.VISIBLE);
// Saving the boolean flag in SharedPreferece
add_download(/*your context*/,/*your url*/);
}
} else {
Log.i("STATUS", " NOT DOWNLOADING,BOSS");
}
}
on your recycle view adapter onbindview or wherever you want to get retrieve the status of whether downloaded call is_downloaded(/*YOUR CONTEXT*/,"YOUR URL");
I try to put advanced ads inside the dialog box when you close the application, but when you open a dialog box does not load the ad for the first time. ... I am worried that I load the ad inside the application
without appearing and at closing I put it in the dialog box for fear that the agent considers it a google violation to download the ad without its appearance
Constant code from android developer
public class MainActivity extends AppCompatActivity {
private static final String ADMOB_AD_UNIT_ID = "ca-app-pub-3940256099942544/2247696110";
private static final String ADMOB_APP_ID = "ca-app-pub-3940256099942544~3347511713";
AdLoader.Builder builder;
UnifiedNativeAdView adView;
private Button refresh;
private CheckBox startVideoAdsMuted;
private TextView videoStatus;
private UnifiedNativeAd nativeAd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the Mobile Ads SDK.
MobileAds.initialize(this, ADMOB_APP_ID);
refresh = findViewById(R.id.btn_refresh);
startVideoAdsMuted = findViewById(R.id.cb_start_muted);
videoStatus = findViewById(R.id.tv_video_status);
refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View unusedView) {
refreshAd();
}
});
refreshAd();
}
/**
* Populates a {#link UnifiedNativeAdView} object with data from a given
* {#link UnifiedNativeAd}.
*
* #param nativeAd the object containing the ad's assets
* #param adView the view to be populated
*/
private void populateUnifiedNativeAdView(UnifiedNativeAd nativeAd, UnifiedNativeAdView adView) {
// Set the media view. Media content will be automatically populated in the media view once
// adView.setNativeAd() is called.
MediaView mediaView = adView.findViewById(R.id.ad_media);
adView.setMediaView(mediaView);
// Set other ad assets.
adView.setHeadlineView(adView.findViewById(R.id.ad_headline));
adView.setBodyView(adView.findViewById(R.id.ad_body));
adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action));
adView.setIconView(adView.findViewById(R.id.ad_app_icon));
adView.setPriceView(adView.findViewById(R.id.ad_price));
adView.setStarRatingView(adView.findViewById(R.id.ad_stars));
adView.setStoreView(adView.findViewById(R.id.ad_store));
adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser));
// The headline is guaranteed to be in every UnifiedNativeAd.
((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
if (nativeAd.getBody() == null) {
adView.getBodyView().setVisibility(View.INVISIBLE);
} else {
adView.getBodyView().setVisibility(View.VISIBLE);
((TextView) adView.getBodyView()).setText(nativeAd.getBody());
}
if (nativeAd.getCallToAction() == null) {
adView.getCallToActionView().setVisibility(View.INVISIBLE);
} else {
adView.getCallToActionView().setVisibility(View.VISIBLE);
((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
}
if (nativeAd.getIcon() == null) {
adView.getIconView().setVisibility(View.GONE);
} else {
((ImageView) adView.getIconView()).setImageDrawable(
nativeAd.getIcon().getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (nativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
} else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView) adView.getPriceView()).setText(nativeAd.getPrice());
}
if (nativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
} else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView) adView.getStoreView()).setText(nativeAd.getStore());
}
if (nativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
} else {
((RatingBar) adView.getStarRatingView())
.setRating(nativeAd.getStarRating().floatValue());
adView.getStarRatingView().setVisibility(View.VISIBLE);
}
if (nativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
} else {
((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());
adView.getAdvertiserView().setVisibility(View.VISIBLE);
}
// This method tells the Google Mobile Ads SDK that you have finished populating your
// native ad view with this native ad. The SDK will populate the adView's MediaView
// with the media content from this native ad.
adView.setNativeAd(nativeAd);
// Get the video controller for the ad. One will always be provided, even if the ad doesn't
// have a video asset.
VideoController vc = nativeAd.getVideoController();
// Updates the UI to say whether or not this ad has a video asset.
if (vc.hasVideoContent()) {
videoStatus.setText(String.format(Locale.getDefault(),
"Video status: Ad contains a %.2f:1 video asset.",
vc.getAspectRatio()));
// Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
// VideoController will call methods on this object when events occur in the video
// lifecycle.
vc.setVideoLifecycleCallbacks(new VideoController.VideoLifecycleCallbacks() {
#Override
public void onVideoEnd() {
// Publishers should allow native ads to complete video playback before
// refreshing or replacing them with another ad in the same UI location.
refresh.setEnabled(true);
videoStatus.setText("Video status: Video playback has ended.");
super.onVideoEnd();
}
});
} else {
videoStatus.setText("Video status: Ad does not contain a video asset.");
refresh.setEnabled(true);
}
}
/**
* Creates a request for a new native ad based on the boolean parameters and calls the
* corresponding "populate" method when one is successfully returned.
*
*/
private void refreshAd() {
refresh.setEnabled(false);
builder = new AdLoader.Builder(this, ADMOB_AD_UNIT_ID);
builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
// OnUnifiedNativeAdLoadedListener implementation.
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
// You must call destroy on old ads when you are done with them,
// otherwise you will have a memory leak.
if (nativeAd != null) {
nativeAd.destroy();
}
nativeAd = unifiedNativeAd;
FrameLayout frameLayout =
findViewById(R.id.fl_adplaceholder);
adView = (UnifiedNativeAdView) getLayoutInflater()
.inflate(R.layout.ad_unified, null);
populateUnifiedNativeAdView(unifiedNativeAd, adView);
frameLayout.removeAllViews();
frameLayout.addView(adView);
}
});
VideoOptions videoOptions = new VideoOptions.Builder()
.setStartMuted(startVideoAdsMuted.isChecked())
.build();
NativeAdOptions adOptions = new NativeAdOptions.Builder()
.setVideoOptions(videoOptions)
.build();
builder.withNativeAdOptions(adOptions);
AdLoader adLoader = builder.withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int errorCode) {
refresh.setEnabled(true);
Toast.makeText(MainActivity.this, "Failed to load native ad: "
+ errorCode, Toast.LENGTH_SHORT).show();
}
}).build();
adLoader.loadAd(new AdRequest.Builder().build());
videoStatus.setText("");
}
Now I'm trying to put the code refresh method insaid dialog box instead of refresh method
public void showdilog(){
builder = new AdLoader.Builder(this, ADMOB_AD_UNIT_ID);
builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
// OnUnifiedNativeAdLoadedListener implementation.
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
// You must call destroy on old ads when you are done with them,
// otherwise you will have a memory leak.
if (nativeAd != null) {
nativeAd.destroy();
}
nativeAd = unifiedNativeAd;
FrameLayout frameLayout =
findViewById(R.id.fl_adplaceholder);
adView = (UnifiedNativeAdView) getLayoutInflater()
.inflate(R.layout.ad_unified, null);
populateUnifiedNativeAdView(unifiedNativeAd, adView);
frameLayout.removeAllViews();
frameLayout.addView(adView);
}
});
VideoOptions videoOptions = new VideoOptions.Builder()
.setStartMuted(startVideoAdsMuted.isChecked())
.build();
NativeAdOptions adOptions = new NativeAdOptions.Builder()
.setVideoOptions(videoOptions)
.build();
builder.withNativeAdOptions(adOptions);
AdLoader adLoader = builder.withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int errorCode) {
refresh.setEnabled(true);
Toast.makeText(MainActivity.this, "Failed to load native ad: "
+ errorCode, Toast.LENGTH_SHORT).show();
}
}).build();
adLoader.loadAd(new AdRequest.Builder().build());
AlertDialog.Builder builder = new AlertDialog.Builder(this);
bulider.setView(adView);
builder.setMessage(R.string.onfirm_exit)
.setCancelable(false)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
finish();
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
What you're doing is that you're building and showing your dialog box at the same time. So, at the time you show your dialog box you're also loading your native ad.
You should BUILD your dialog box in advance, and don't call alert.show() in your build function.
So, call your buildDialog() in MainActivity in advance.
In your onBackPressed function call alert.show().
Hope it helps.
My app recently reached the "maximum" size of 100 MBs so I had to learn about the apk expansion procedure. I read google's documentation but i found it kind of bad so i looked around and followed this guide: https://kitefaster.com/2017/02/15/expansion-apk-files-android-studio/ . Now, after adding the SampleDownloaderActivity,SampleDownloaderService and the SampleAlarmReceiver classes i got lost. I created a directory in my phone's internal storage in this location Android/obb/com.mypackage.example like the Google documentation mentions so i can put my expansion files there. But here are my questions:
1) I've added the required permissions in the app's manifest.xml file but do i need to ask for them first through code in order for the receiver,downloader and the downloaderActivity to work?
2) The files that i want to include in my main expansion file are some images from my xhdpi drawable folder but i am not sure on what i need to do in order to create my .obb file containing those images. Do i need to create a .zip file with them? Do i just drop them inside the directory i created that i mentioned above? What do i need to do?
3) Supposing the previous questions have been answered, if the files already exist in the directory or have been downloaded, i must get that dir through code and read the files in order to use them in my app, correct?
4) If the files are not there,how do i initiate the download from Google-Play? From what i understand by the doc, i need to make my app's main activity the "SampleDownloaderActivity" one, right?
5) After the files are done downloading, do i have to create an intent in the SampleDownloaderActivity's onCreate method in order for the app to go to my desired activity which uses those files?
Below, i'm posting the apk expansion related code files in which i've changed what i understood was needed. Do i need to change something else? Thank you in advance for your help!
ApkExpDownloaderService.java
public class ApkExpDownloaderService extends DownloaderService {
// stuff for LVL -- MODIFY FOR YOUR APPLICATION!
private static final String BASE64_PUBLIC_KEY = "MY_KEY";
// used by the preference obfuscater
private static final byte[] SALT = new byte[] {
// my array of bytes
};
/**
* This public key comes from your Android Market publisher account, and it
* used by the LVL to validate responses from Market on your behalf.
*/
#Override
public String getPublicKey() {
return BASE64_PUBLIC_KEY;
}
/**
* This is used by the preference obfuscater to make sure that your
* obfuscated preferences are different than the ones used by other
* applications.
*/
#Override
public byte[] getSALT() {
return SALT;
}
/**
* Fill this in with the class name for your alarm receiver. We do this
* because receivers must be unique across all of Android (it's a good idea
* to make sure that your receiver is in your unique package)
*/
#Override
public String getAlarmReceiverClassName() {
return ApkExpAlarmReceiver.class.getName();
}
}
ApkExpDownloaderActivity.java
public class ApkExpDownloaderActivity extends Activity implements IDownloaderClient {
private static final String LOG_TAG = "LVLDownloader";
private ProgressBar mPB;
private TextView mStatusText;
private TextView mProgressFraction;
private TextView mProgressPercent;
private TextView mAverageSpeed;
private TextView mTimeRemaining;
private View mDashboard;
private View mCellMessage;
private Button mPauseButton;
private Button mWiFiSettingsButton;
private boolean mStatePaused;
private int mState;
private IDownloaderService mRemoteService;
private IStub mDownloaderClientStub;
private void setState(int newState) {
if (mState != newState) {
mState = newState;
mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
}
}
private void setButtonPausedState(boolean paused) {
mStatePaused = paused;
int stringResourceID = paused ? R.string.text_button_resume :
R.string.text_button_pause;
mPauseButton.setText(stringResourceID);
}
/**
* This is a little helper class that demonstrates simple testing of an
* Expansion APK file delivered by Market. You may not wish to hard-code
* things such as file lengths into your executable... and you may wish to
* turn this code off during application development.
*/
private static class XAPKFile {
public final boolean mIsMain;
public final int mFileVersion;
public final long mFileSize;
XAPKFile(boolean isMain, int fileVersion, long fileSize) {
mIsMain = isMain;
mFileVersion = fileVersion;
mFileSize = fileSize;
}
}
/**
* Here is where you place the data that the validator will use to determine
* if the file was delivered correctly. This is encoded in the source code
* so the application can easily determine whether the file has been
* properly delivered without having to talk to the server. If the
* application is using LVL for licensing, it may make sense to eliminate
* these checks and to just rely on the server.
*/
private static final XAPKFile[] xAPKS = {
new XAPKFile(
true, // true signifies a main file
21, // the version of the APK that the file was uploaded
// against
687801613L // the length of the file in bytes
)
};
/**
* Go through each of the APK Expansion files defined in the structure above
* and determine if the files are present and match the required size. Free
* applications should definitely consider doing this, as this allows the
* application to be launched for the first time without having a network
* connection present. Paid applications that use LVL should probably do at
* least one LVL check that requires the network to be present, so this is
* not as necessary.
*
* #return true if they are present.
*/
boolean expansionFilesDelivered() {
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false))
return false;
}
return true;
}
/**
* Calculating a moving average for the validation speed so we don't get
* jumpy calculations for time etc.
*/
static private final float SMOOTHING_FACTOR = 0.005f;
/**
* Used by the async task
*/
private boolean mCancelValidation;
/**
* Go through each of the Expansion APK files and open each as a zip file.
* Calculate the CRC for each file and return false if any fail to match.
*
* #return true if XAPKZipFile is successful
*/
void validateXAPKZipFiles() {
AsyncTask<Object, DownloadProgressInfo, Boolean> validationTask = new AsyncTask<Object, DownloadProgressInfo, Boolean>() {
#Override
protected void onPreExecute() {
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_verifying_download);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCancelValidation = true;
}
});
mPauseButton.setText(R.string.text_button_cancel_verify);
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Object... params) {
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(
ApkExpDownloaderActivity.this,
xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(ApkExpDownloaderActivity.this, fileName,
xf.mFileSize, false))
return false;
fileName = Helpers
.generateSaveFileName(ApkExpDownloaderActivity.this, fileName);
ZipResourceFile zrf;
byte[] buf = new byte[1024 * 256];
try {
zrf = new ZipResourceFile(fileName);
ZipEntryRO[] entries = zrf.getAllEntries();
/**
* First calculate the total compressed length
*/
long totalCompressedLength = 0;
for (ZipEntryRO entry : entries) {
totalCompressedLength += entry.mCompressedLength;
}
float averageVerifySpeed = 0;
long totalBytesRemaining = totalCompressedLength;
long timeRemaining;
/**
* Then calculate a CRC for every file in the Zip file,
* comparing it to what is stored in the Zip directory.
* Note that for compressed Zip files we must extract
* the contents to do this comparison.
*/
for (ZipEntryRO entry : entries) {
if (-1 != entry.mCRC32) {
long length = entry.mUncompressedLength;
CRC32 crc = new CRC32();
DataInputStream dis = null;
try {
dis = new DataInputStream(
zrf.getInputStream(entry.mFileName));
long startTime = SystemClock.uptimeMillis();
while (length > 0) {
int seek = (int) (length > buf.length ? buf.length
: length);
dis.readFully(buf, 0, seek);
crc.update(buf, 0, seek);
length -= seek;
long currentTime = SystemClock.uptimeMillis();
long timePassed = currentTime - startTime;
if (timePassed > 0) {
float currentSpeedSample = (float) seek
/ (float) timePassed;
if (0 != averageVerifySpeed) {
averageVerifySpeed = SMOOTHING_FACTOR
* currentSpeedSample
+ (1 - SMOOTHING_FACTOR)
* averageVerifySpeed;
} else {
averageVerifySpeed = currentSpeedSample;
}
totalBytesRemaining -= seek;
timeRemaining = (long) (totalBytesRemaining / averageVerifySpeed);
this.publishProgress(
new DownloadProgressInfo(
totalCompressedLength,
totalCompressedLength
- totalBytesRemaining,
timeRemaining,
averageVerifySpeed)
);
}
startTime = currentTime;
if (mCancelValidation)
return true;
}
if (crc.getValue() != entry.mCRC32) {
Log.e(Constants.TAG,
"CRC does not match for entry: "
+ entry.mFileName);
Log.e(Constants.TAG,
"In file: " + entry.getZipFileName());
return false;
}
} finally {
if (null != dis) {
dis.close();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
#Override
protected void onProgressUpdate(DownloadProgressInfo... values) {
onDownloadProgress(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_validation_complete);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
mPauseButton.setText(android.R.string.ok);
} else {
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_validation_failed);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
mPauseButton.setText(android.R.string.cancel);
}
super.onPostExecute(result);
}
};
validationTask.execute(new Object());
}
/**
* If the download isn't present, we initialize the download UI. This ties
* all of the controls into the remote service calls.
*/
private void initializeDownloadUI() {
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub
(this, ApkExpDownloaderService.class);
setContentView(R.layout.main);
mPB = (ProgressBar) findViewById(R.id.progressBar);
mStatusText = (TextView) findViewById(R.id.statusText);
mProgressFraction = (TextView) findViewById(R.id.progressAsFraction);
mProgressPercent = (TextView) findViewById(R.id.progressAsPercentage);
mAverageSpeed = (TextView) findViewById(R.id.progressAverageSpeed);
mTimeRemaining = (TextView) findViewById(R.id.progressTimeRemaining);
mDashboard = findViewById(R.id.downloaderDashboard);
mCellMessage = findViewById(R.id.approveCellular);
mPauseButton = (Button) findViewById(R.id.pauseButton);
mWiFiSettingsButton = (Button) findViewById(R.id.wifiSettingsButton);
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mStatePaused) {
mRemoteService.requestContinueDownload();
} else {
mRemoteService.requestPauseDownload();
}
setButtonPausedState(!mStatePaused);
}
});
mWiFiSettingsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}
});
Button resumeOnCell = (Button) findViewById(R.id.resumeOverCellular);
resumeOnCell.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
mRemoteService.requestContinueDownload();
mCellMessage.setVisibility(View.GONE);
}
});
}
/**
* Called when the activity is first create; we wouldn't create a layout in
* the case where we have the file and are moving to another activity
* without downloading.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Both downloading and validation make use of the "download" UI
*/
initializeDownloadUI();
/**
* Before we do anything, are the files we expect already here and
* delivered (presumably by Market) For free titles, this is probably
* worth doing. (so no Market request is necessary)
*/
if (!expansionFilesDelivered()) {
try {
Intent launchIntent = ApkExpDownloaderActivity.this
.getIntent();
Intent intentToLaunchThisActivityFromNotification = new Intent(
ApkExpDownloaderActivity
.this, ApkExpDownloaderActivity.this.getClass());
intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null) {
for (String category : launchIntent.getCategories()) {
intentToLaunchThisActivityFromNotification.addCategory(category);
}
}
// Build PendingIntent used to open this activity from
// Notification
PendingIntent pendingIntent = PendingIntent.getActivity(
ApkExpDownloaderActivity.this,
0, intentToLaunchThisActivityFromNotification,
PendingIntent.FLAG_UPDATE_CURRENT);
// Request to start the download
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
pendingIntent, ApkExpDownloaderService.class);
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
// The DownloaderService has started downloading the files,
// show progress
initializeDownloadUI();
return;
} // otherwise, download not needed so we fall through to
// starting the movie
} catch (NameNotFoundException e) {
Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
e.printStackTrace();
}
} else {
validateXAPKZipFiles();
}
}
/**
* Connect the stub to our service on start.
*/
#Override
protected void onStart() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onStart();
}
/**
* Disconnect the stub from our service on stop
*/
#Override
protected void onStop() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.disconnect(this);
}
super.onStop();
}
//TODO:sp need more info on this
/**
* Critical implementation detail. In onServiceConnected we create the
* remote service and marshaler. This is how we pass the client information
* back to the service so the client can be properly notified of changes. We
* must do this every time we reconnect to the service.
*/
#Override
public void onServiceConnected(Messenger m) {
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
/**
* The download state should trigger changes in the UI --- it may be useful
* to show the state as being indeterminate at times. This sample can be
* considered a guideline.
*/
#Override
public void onDownloadStateChanged(int newState) {
setState(newState);
boolean showDashboard = true;
boolean showCellMessage = false;
boolean paused;
boolean indeterminate;
switch (newState) {
case IDownloaderClient.STATE_IDLE:
// STATE_IDLE means the service is listening, so it's
// safe to start making calls via mRemoteService.
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_CONNECTING:
case IDownloaderClient.STATE_FETCHING_URL:
showDashboard = true;
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_DOWNLOADING:
paused = false;
showDashboard = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_FAILED_CANCELED:
case IDownloaderClient.STATE_FAILED:
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
case IDownloaderClient.STATE_FAILED_UNLICENSED:
paused = true;
showDashboard = false;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
showDashboard = false;
paused = true;
indeterminate = false;
showCellMessage = true;
break;
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_ROAMING:
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_COMPLETED:
showDashboard = false;
paused = false;
indeterminate = false;
validateXAPKZipFiles();
return;
default:
paused = true;
indeterminate = true;
showDashboard = true;
}
int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;
if (mDashboard.getVisibility() != newDashboardVisibility) {
mDashboard.setVisibility(newDashboardVisibility);
}
int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;
if (mCellMessage.getVisibility() != cellMessageVisibility) {
mCellMessage.setVisibility(cellMessageVisibility);
}
mPB.setIndeterminate(indeterminate);
setButtonPausedState(paused);
}
/**
* Sets the state of the various controls based on the progressinfo object
* sent from the downloader service.
*/
#Override
public void onDownloadProgress(DownloadProgressInfo progress) {
mAverageSpeed.setText(getString(R.string.kilobytes_per_second,
Helpers.getSpeedString(progress.mCurrentSpeed)));
mTimeRemaining.setText(getString(R.string.time_remaining,
Helpers.getTimeRemaining(progress.mTimeRemaining)));
progress.mOverallTotal = progress.mOverallTotal;
mPB.setMax((int) (progress.mOverallTotal >> 8));
mPB.setProgress((int) (progress.mOverallProgress >> 8));
mProgressPercent.setText(Long.toString(progress.mOverallProgress
* 100 /
progress.mOverallTotal) + "%");
mProgressFraction.setText(Helpers.getDownloadProgressString
(progress.mOverallProgress,
progress.mOverallTotal));
}
#Override
protected void onDestroy() {
this.mCancelValidation = true;
super.onDestroy();
}
}
ApkExpReceiver.java
public class ApkExpAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, ApkExpDownloaderService.class);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
Permissions is a bigger topic and independent of OBB files. Rather than explain it here, I recommend the documentation. In short, for Android devices before 6.0 they only need to be in the manifest. For Android devices after 6.0 you should ask for them at an appropriate moment at runtime.
OBB is as the name suggests an "Opaque" binary blob. You can choose whatever file format you want, google just treats it as a blob of bits. Lots of apps use zip files, but you don't have to, you can use whatever file format you like.
Correct.
The sample download activity is what the name suggests - a sample. You can use that activity, or you can re-use the parts of the code to do the downloading in your own app. Whatever you do, downloading may take a little while, needs permissions, and won't work while offline, so you need to display an appropriate user interface to the user for all these cases. What an appropriate UI is will vary for different apps.
Apparently, I am developing an Indoor Positioning System and I want to resize an image which is being fetched from the server using picasso library, however I am getting a null pointer exception error which says: Attempt to read from field 'double com.indooratlas.android.sdk.resources.IALatLng.latitude' on a null object reference.
I know why I am getting this null pointer exception, however I do not see any other approach to resize the image.
Here is my ImageViewActivity named Extra_Activity which does not contain full code and only contains related code snippet:
public class Extra_activity extends FragmentActivity
{
private static final float dotRadius = 1.0f;
private static final int MAX_DIMENSION = 2048;
private IALocationManager mIALocationManager;
private IAResourceManager mResourceManager;
private IATask<IAFloorPlan> mPendingAsyncResult;
//private IAFloorPlan mFloorPlan;
private BlueDotView mImageView;
private Target mLoadTarget;
private static final String TAG ="FloorPlanLoader";
private IALatLng latLng;
private IALocationListener mIALocationListener = new IALocationListener()
{
#Override
public void onLocationChanged(IALocation location) {
Log.d(TAG, "location is: " + location.getLatitude() + "," + location.getLongitude());
if (mImageView != null && mImageView.isReady()) {
latLng = new IALatLng(location.getLatitude(), location.getLongitude());
//PointF point = mFloorPlan.coordinateToPoint(latLng);
//mImageView.setDotCenter(point);
//mImageView.postInvalidate();
}
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
};
private IARegion.Listener mRegionListener = new IARegion.Listener()
{
#Override
public void onEnterRegion(IARegion region) {
if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
String id = region.getId();
Log.d(TAG, "floorPlan changed to " + id);
fetchFloorPlan(id);
}
}
#Override
public void onExitRegion(IARegion region) {
// leaving a previously entered region
}
};
private void showFloorPlanImage(final IAFloorPlan floorPlan) {
final String filePath = floorPlan.getUrl();
if (mLoadTarget == null)
{
mLoadTarget = new Target()
{
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
{
Log.d(TAG, "onBitmap loaded with dimensions: " + bitmap.getWidth() + "x"
+ bitmap.getHeight());
mImageView.setImage(ImageSource.bitmap(bitmap.copy(bitmap.getConfig(), true)));
mImageView.setRadius(floorPlan.getMetersToPixels() * dotRadius);
PointF point = floorPlan.coordinateToPoint(latLng);
mImageView.setDotCenter(point);
mImageView.postInvalidate();
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
#Override
public void onBitmapFailed(Drawable placeHolderDrawable) {
Toast.makeText(Extra_activity.this, "Failed to load bitmap",
Toast.LENGTH_SHORT).show();
}
};
}
RequestCreator request = Picasso.with(this).load(filePath).rotate(90); //.resize(0,0)
final int bitmapWidth = floorPlan.getBitmapWidth();
final int bitmapHeight = floorPlan.getBitmapHeight();
if (bitmapHeight > MAX_DIMENSION) {
request.resize(0, MAX_DIMENSION);
} else if (bitmapWidth > MAX_DIMENSION) {
request.resize(MAX_DIMENSION, 0);
}
request.into(mLoadTarget);
Log.w(TAG, "showFloorPlanImage: " + filePath);
progressDialog.dismiss();
}
private void fetchFloorPlan(String id) {
cancelPendingNetworkCalls();
final IATask<IAFloorPlan> asyncResult = mResourceManager.fetchFloorPlanWithId(id);
mPendingAsyncResult = asyncResult;
if (mPendingAsyncResult != null) {
mPendingAsyncResult.setCallback(new IAResultCallback<IAFloorPlan>() {
#Override
public void onResult(IAResult<IAFloorPlan> result) {
Log.d(TAG, "fetch floor plan result:" + result);
if (result.isSuccess() && result.getResult() != null)
{
//mFloorPlan = result.getResult();
showFloorPlanImage(result.getResult());
} else {
if (!asyncResult.isCancelled()) {
Toast.makeText(Extra_activity.this,
(result.getError() != null
? "error loading floor plan: " + result.getError()
: "access to floor plan denied"), Toast.LENGTH_LONG)
.show();
}
}
}
}, Looper.getMainLooper());
}
}
private void cancelPendingNetworkCalls() {
if (mPendingAsyncResult != null && !mPendingAsyncResult.isCancelled()) {
mPendingAsyncResult.cancel();
}
}
}
The error message from logcat:
The (Extra_activity.java:154) refers to PointF point = floorPlan.coordinateToPoint(latLng); in the showFloorPlanImage method. The PointF point = floorPlan.coordinateToPoint(latLng); should be placed in the onLocationChanged method however, in this code snippet I can't use PointF point = floorPlan.coordinateToPoint(latLng); inside that method as variable floorplan can only be accessed inside showFloorPlanImage method. And this whole leads to null pointer exception.
I have asked a similar question to this one where I used a different approach (different code), however, I was able to resize the image but the blue dot was getting displayed out of sight: "IndoorAtlas SDK 2.0: Using Picasso with custom ImageView"
What is the correct solution as none of my approaches seems to work? Many thanks in advance if you can help me solve this problem.
I don't really understand the relation between your Nullpointer and the image resizing. However what you want to achieve here is only resizing an image if I understand it right. Instead of trying to resize the image itself, why don't you resize its container instead? (here the ImageView). You can achieve that with getLayoutParams().height and getLayoutParams().width or in xml like in this example:
<ImageView
android:id="#+id/image"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="#drawable/image" />
It's hard to tell from incomplete code snippets, but to me it looks like your error is unrelated to either Picasso or IndoorAtlas. You seem to have used our example code as a template, but made latLng an instance variable.
As a consequence, your onBitmapLoaded() where you get the NPE can be called (and apparently is called) before latLng is initialized. The latter happens asynchronously at the onLocationChanged() callback (and in this case, after you needed it). You can't depend on a particular calling order when working with asynchronous callback constructs.
Best of luck in your development!