Android - WebView Default Selected Toolbar's Copy Button doesn't Work - android

As you know, when selecting a text, it appears a popup menu.
When I click the "copy" on the menu, I can't get selected/copied text but the event is fired. (in MyMenuItemOnMenuItemClickListener class > OnMenuItemClick method)
--
If I copy it using "Share > Copy to Clipboard" menu, I can get it. The event isn't fired. (This is not our goal. Just for comparison.)
And if I copy any text except the WebView and then click the copy button (not 'copy to clipboard') I could get last copied text. What's wrong with Webview's copy button? Why I couldn't get selected text?
There is no problem with iOS.
--
Xaml;
CustomWebView Class;
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(CustomWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
Custom Renderer;
[assembly: ExportRenderer(typeof(CustomWebView), typeof(WebViewRendererDroid))]
namespace TApp.Droid
{
public class WebViewRendererDroid : ViewRenderer<CustomWebView, Android.Webkit.WebView>, IOnPrimaryClipChangedListener /* ViewRenderer<CustomWebView, Android.Webkit.WebView>*/
{
Context _context;
public WebViewRendererDroid(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var webView = new Android.Webkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.Settings.AllowContentAccess = true;
webView.LoadUrl("https://learn.microsoft.com/en-us/xamarin/ios/");
SetNativeControl(webView);
}
if (e.NewElement != null)
{
Control.LoadUrl("https://learn.microsoft.com/en-us/xamarin/ios/");
}
}
ClipboardManager myClipBoard;
public void OnPrimaryClipChanged()
{
ClipData clipData = myClipBoard.PrimaryClip;
ClipData.Item item = clipData.GetItemAt(0);
MessagingCenter.Send<object, string>(this, "Hi", item.Text);
}
}
}
MainActivity.cs;
namespace TApp.Droid
{
[Activity(Label = "TApp", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnActionModeStarted(ActionMode mode)
{
IMenu menu = mode.Menu;
menu.GetItem(0).SetOnMenuItemClickListener(new MyMenuItemOnMenuItemClickListener(this));
base.OnActionModeStarted(mode);
}
}
public class MyMenuItemOnMenuItemClickListener : Java.Lang.Object, IMenuItemOnMenuItemClickListener
{
private MainActivity mContext;
public MyMenuItemOnMenuItemClickListener(MainActivity activity)
{
this.mContext = activity;
}
public bool OnMenuItemClick(IMenuItem item)
{
var clipboard = (ClipboardManager)mContext.GetSystemService(Context.ClipboardService);
var clipboard2 = (Android.Text.ClipboardManager)Android.App.Application.Context.GetSystemService(Context.ClipboardService);
var pasteData = "";
string aaa = clipboard.Text;
if (!(clipboard.HasPrimaryClip))
{
// If it does contain data, decide if you can handle the data.
}
else if (!(clipboard.PrimaryClipDescription.HasMimeType(ClipDescription.MimetypeTextPlain)))
{
// since the clipboard has data but it is not plain text
}
else
{
//since the clipboard contains plain text
var copiedText = clipboard.PrimaryClip.GetItemAt(0);
// Gets the clipboard as text
pasteData = copiedText.Text;
}
Toast.MakeText(mContext, pasteData, ToastLength.Short).Show();
return true;
}
}
}

i write a simple sample,you could check it :
1.define an Interface ActionSelectListener in your Android.Project:
public interface ActionSelectListener
{
void onClick(string selectText);
}
2.custom a WebView CustomActionWebViewin your Android.Project:
class CustomActionWebView:WebView,IMenuItemOnMenuItemClickListener
{
public CustomActionWebView(Context context):base(context)
{
_context = context;
}
public CustomActionWebView(Context context, IAttributeSet attrs):base(context,attrs)
{
_context = context;
}
public CustomActionWebView(Context context, IAttributeSet attrs, int defStyleAttr):base(context,attrs,defStyleAttr)
{
_context = context;
}
private Context _context;
ActionMode mActionMode;
static ActionSelectListener mActionSelectListener;
private ActionMode resolveActionMode(ActionMode actionMode)
{
if (actionMode != null)
{
IMenu menu = actionMode.Menu;
mActionMode = actionMode;
IMenuItem menuItem = menu.GetItem(0);//here only handle the copy button
menuItem.SetOnMenuItemClickListener(this);
}
mActionMode = actionMode;
return actionMode;
}
public override ActionMode StartActionMode(ActionMode.ICallback callback)
{
ActionMode actionMode = base.StartActionMode(callback);
return resolveActionMode(actionMode);
}
public override ActionMode StartActionMode(ActionMode.ICallback callback, [GeneratedEnum] ActionModeType type)
{
ActionMode actionMode = base.StartActionMode(callback, type);
return resolveActionMode(actionMode);
}
private void releaseAction()
{
if (mActionMode != null)
{
mActionMode.Finish();
mActionMode = null;
}
}
/**
* When you click, get the selected text from the page and drop it back to the native js interface
* #param passes in the text of the clicked item, which is returned to the native interface via js
*/
private void getSelectedData()
{
String js = "(function getSelectedText() {" +
"var txt;" +
"if (window.getSelection) {" +
"txt = window.getSelection().toString();" +
"} else if (window.document.getSelection) {" +
"txt = window.document.getSelection().toString();" +
"} else if (window.document.selection) {" +
"txt = window.document.selection.createRange().text;" +
"}" +
"JSInterface.callback(txt);" +
"})()";
if (Build.VERSION.SdkInt >= Build.VERSION_CODES.Kitkat)
{
EvaluateJavascript( js, null);
}
else
{
LoadUrl("javascript:" + js);
}
}
public void linkJSInterface()
{
AddJavascriptInterface(new ActionSelectInterface(_context), "JSInterface");
}
/**
* click call back
* #param actionSelectListener
*/
public void setActionSelectListener(ActionSelectListener actionSelectListener)
{
mActionSelectListener = actionSelectListener;
}
public bool OnMenuItemClick(IMenuItem item)
{
getSelectedData();
releaseAction();
return true;
}
/**
* js call back interface
*/
class ActionSelectInterface : Java.Lang.Object
{
Context mContext;
public ActionSelectInterface(Context c)
{
mContext = c;
}
[JavascriptInterface]
[Export]
public void callback(string value)
{
if (mActionSelectListener != null)
{
mActionSelectListener.onClick(value);
}
}
}
}
3.use in your CustomRenderer :
[assembly: ExportRenderer(typeof(CustomWebView), typeof(WebViewRendererDroid))]
namespace TApp.Droid
{
public class WebViewRendererDroid : ViewRenderer<CustomWebView, Android.Webkit.WebView>, ActionSelectListenerr /* ViewRenderer<CustomWebView, Android.Webkit.WebView>*/
{
Context _context;
public WebViewRendererDroid(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var webView = new CustomActionWebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.Settings.AllowContentAccess = true;
webView.linkJSInterface();
webView.LoadUrl("https://learn.microsoft.com/en-us/xamarin/ios/");
SetNativeControl(webView);
}
}
public void onClick(string selectText)
{
ClipboardManager cm = (ClipboardManager)_context.GetSystemService(Context.ClipboardService);
// Place the text in the system clipboard.
ClipData clipData = ClipData.NewPlainText(null, selectText);
// Set (copy) the dataset to the clipboard
cm.PrimaryClip = clipData;
}
}

Related

Porting View Renderer in .net

I am currently migrating my Xamarin.Forms app to .NET MAUI, and having a difficulty in migrating view renderer. In .NET MAUI I am using camera2 in my app, and using the renderer for same.
My Xamarin forms code is
public class CameraRecordV3 : View
{
public static readonly BindableProperty StartProperty = BindableProperty.Create(
"Start", typeof(int), typeof(int), 6000);
public int Start
{
set { SetValue(StartProperty, value); }
get { return (int)GetValue(StartProperty); }
}
}
using iVue.Views;
using System.ComponentModel;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.Handlers.Compatibility;
namespace iVue.Platforms.Android.Renderers;
public class CameraRecordRenderer_V3 : ViewRenderer<CameraRecordV3, CameraRecordControl_V3>
{
private CameraRecordControl_V3 _cameraControl;
private DisplayTimeHelper _displayTimeHelper = new DisplayTimeHelper();
public CameraRecordRenderer_V3(Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CameraRecordV3> e)
{
base.OnElementChanged(e);
if (Control == null)
{
_cameraControl = new CameraRecordControl_V3(Context, e.NewElement);
SetNativeControl(_cameraControl);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var model = (CameraRecordV3)sender;
base.OnElementPropertyChanged(sender, e);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_cameraControl.Dispose();
if(Control != null)
Control.Dispose();
}
}
}
CameraRecordControl_V3 is a viewgroup which contains a native view for android, which contains buttons and camera
public class CameraRecordControl_V3 : ViewGroup
{
public CameraRecordControl_V3(Context context, CameraRecordV3 vm) : base(context)
{
_activity = this.Context as Activity;
_view = _activity.LayoutInflater.Inflate(Resource.Layout.CameraRecordLayoutV2, this, false);
AddView(_view);
_toolbar = (Toolbar)_view.FindViewById(Resource.Id.toolbar);
textureView = (AutoFitTextureView)_view.FindViewById(Resource.Id.textureview)
_questionTitleView = (Button)_view.FindViewById(Resource.Id.Start);
}
}
I tried using handler in .net maui but no luck with it.
My Maui Code is as follows
public interface ICameraRecordV3 : IView
{
public int StartTime { get; }
}
public partial class CameraRecordV3Handler
{
public static PropertyMapper<ICameraRecordV3, CameraRecordV3Handler> CustomMapper
= new PropertyMapper<ICameraRecordV3, CameraRecordV3Handler>(ViewHandler.ViewMapper)
{
[nameof(ICameraRecordV3.StartTime)] = MapStartTime,
};
public CameraRecordV3Handler() : base(CustomMapper)
{
}
public CameraRecordV3Handler(PropertyMapper mapper = null) : base(mapper ?? CustomMapper)
{
}
}
public class CameraRecordV3 : View, ICameraRecordV3
{
public static readonly BindableProperty StartProperty = BindableProperty.Create(
"StartTime", typeof(int), typeof(int), 6000);
public int Start
{
set { SetValue(StartTimeProperty, value); }
get { return (int)GetValue(StartTimeProperty); }
}
}
//Platform Specific code
public partial class CameraRecordV3Handler : ViewHandler<ICameraRecordV3, CameraRecordControl_V3>
{
private CameraRecordControl_V3 _cameraControl;
protected override CameraRecordControl_V3 CreatePlatformView()
{
_cameraControl = new CameraRecordControl_V3(Context, null);
return _cameraControl;
}
protected override void ConnectHandler(CameraRecordControl_V3 platformView)
{
base.ConnectHandler(platformView);
}
private static void MapStartTime(CameraRecordV3Handler handler, ICameraRecordV3 arg2)
{
handler.PlatformView?.UpdateStartTime(arg2.StartTime);
}
}
//MauiProgram
builder.ConfigureMauiHandlers(handlers =>
{
#if __ANDROID__
handlers.AddHandler(typeof(CameraRecordV3), typeof(iVue.Handlers.CameraRecordV3Handler));
#endif
});
You can continue to use CustomRenderer in MAUI, you just need to Remove any ExportRenderer directives as they won't be needed in .NET MAUI. And then configure each renderer using conditional compilation for each platform. You can replace handlers.AddCompatibilityRenderer with handlers.AddHandler in the documentation. Using handlers.AddCompatibilityRenderer will cause a crash.

WebViewPool, Last html page show before new url when reuse WebView from WebViewPool

There is a WebViewPool,when Activity/Fragment is destoryed,webview will be reset and add to the WebViewPool.
The following is the code of WebViewPool:
public class WebViewPool {
private static volatile WebViewPool sINSTANCE;
private int mMaxSize;
private List<WebView> mAvailableList;
private List<WebView> mInUsedList;
private IWebViewPoolFactory mFactory;
private WebViewPool() {
}
public static WebViewPool getInstance() {
if (sINSTANCE == null) {
synchronized (WebViewPool.class) {
if (sINSTANCE == null) {
sINSTANCE = new WebViewPool();
}
}
}
return sINSTANCE;
}
public void init(IWebViewPoolFactory factory,boolean lazy) {
init(2, factory,lazy);
}
public void init(int maxSize, IWebViewPoolFactory factory,boolean lazy) {
mMaxSize = maxSize;
mFactory = factory;
mAvailableList = new ArrayList<>(maxSize);
mInUsedList = new ArrayList<>(maxSize);
if (!lazy) {
create();
}
}
private synchronized void create() {
if (mFactory == null) {
return;
}
for (int i = 0; i < mMaxSize; i++) {
WebView webView = mFactory.create(new MutableContextWrapper(APP.getApplicationContext()));
mAvailableList.add(webView);
}
}
/**
* get webview form pool
* #param context
* #return
*/
public synchronized WebView getWebView(Context context) {
if(!(context instanceof Activity)){
throw new IllegalStateException("Context must be Activity");
}
WebView webView = null;
if (mAvailableList.size() > 0) {
webView = mAvailableList.remove(0);
} else {
if (mFactory != null) {
webView = mFactory.create(new MutableContextWrapper(APP.getApplicationContext()));
}
}
if (webView != null) {
((MutableContextWrapper) webView.getContext()).setBaseContext(context);
mInUsedList.add(webView);
}
return webView;
}
/**
* reset/destroy webview when activity/fragemnt is destroyed
* #param webView
*/
public synchronized void restWebView(WebView webView) {
if (webView == null || mFactory == null) {
return;
}
mFactory.reset(webView);
((MutableContextWrapper) webView.getContext()).setBaseContext(APP.getApplicationContext());
if (mInUsedList.contains(webView)) {
mInUsedList.remove(webView);
if (mAvailableList.size() < mMaxSize) {
mAvailableList.add(webView);
} else {
mFactory.destroy(webView);
}
} else {
mFactory.destroy(webView);
}
}
}
the following is some code of reset function:
public void reset(WebView webView) {
if(webView==null){
return;
}
ViewParent viewParent = webView.getParent();
if (viewParent!=null) {
((ViewGroup)viewParent).removeView(webView);
}
webView.stopLoading();
webView.clearCache(false);
webView.loadUrl("about:blank");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
webView.clearHistory();
}
}, 1000);
}
But when reuse the webview,last html page show first before new url sometimes.It doesn't happened everytime. I searched in Google, but not work.Does anyone know the reason? Thank you!
This problem is solved finally! The reason is that add the reseted WebView to available list while about:blank is not loaded so that clearHistory() doesn't work.
So, reset the webview but do not add to available list when activity/fragment is destroyed, call clearHistory() at onPageFinished() when url is about:blank:
#Override
public void onPageFinish(String url, boolean success) {
if("about:blank".equals(url)){
webView.clearHistory();
//then add the webview to available list;
}
}

Open Custom dialog on text selection of webview

I have implement code of text selection in webview. It's working very fine without any issue. But I want to open custom dialog instead of default dialog. The which I have use its link is below
How to override default text selection of android webview os 4.1+?
But its not working for custom dialog.
Find code below
public class CustomWebView extends WebView {
private Context context;
private ActionMode mActionMode;
private ActionMode.Callback mSelectActionModeCallback;
private GestureDetector mDetector;
public CustomWebView(Context context) {
super(context);
this.context = context;
WebSettings webviewSettings = getSettings();
webviewSettings.setJavaScriptEnabled(true);
// add JavaScript interface for copy
WebAppInterface webAppInterface = new WebAppInterface(context);
addJavascriptInterface(webAppInterface, "JSInterface");
}
public CustomWebView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public CustomWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
}
// this will over ride the default action bar on long press
#Override
public ActionMode startActionMode(ActionMode.Callback callback) {
ViewParent parent = getParent();
if (parent == null) {
return null;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
String name = callback.getClass().toString();
if (name.contains("SelectActionModeCallback")) {
mSelectActionModeCallback = callback;
mDetector = new GestureDetector(context, new CustomGestureListener());
}
}
CustomActionModeCallback mActionModeCallback = new CustomActionModeCallback();
return super.startActionMode(mActionModeCallback);
}
private class CustomActionModeCallback implements ActionMode.Callback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(com.depressiv.R.menu.menu, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.copy:
getSelectedData();
mode.finish();
return true;
case R.id.share:
mode.finish();
return true;
default:
mode.finish();
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
clearFocus();
} else {
if (mSelectActionModeCallback != null) {
mSelectActionModeCallback.onDestroyActionMode(mode);
}
mActionMode = null;
}
}
}
private void getSelectedData() {
String js = "(function getSelectedText() {" +
"var txt;" +
"if (window.getSelection) {" +
"txt = window.getSelection().toString();" +
"} else if (window.document.getSelection) {" +
"txt = window.document.getSelection().toString();" +
"} else if (window.document.selection) {" +
"txt = window.document.selection.createRange().text;" +
"}" +
"JSInterface.getText(txt);" +
"})()";
// calling the js function
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
evaluateJavascript("javascript:" + js, null);
} else {
loadUrl("javascript:" + js);
}
}
private class CustomGestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapUp(MotionEvent e) {
if (mActionMode != null) {
mActionMode.finish();
return true;
}
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Send the event to our gesture detector
// If it is implemented, there will be a return value
if (mDetector != null)
mDetector.onTouchEvent(event);
// If the detected gesture is unimplemented, send it to the superclass
return super.onTouchEvent(event);
}
}
WebAppInterface code
public class WebAppInterface {
Context mContext;
WebAppInterface(Context c) {
mContext = c;
}
#JavascriptInterface
public void getText(String text) {
// put selected text into clipdata
ClipboardManager clipboard = (ClipboardManager)
mContext.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("simple text", text);
clipboard.setPrimaryClip(clip);
// gives the toast for selected text
Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
}
}

Making Nest Thermostat work with Android App

I'm trying to follow the Nest SDK on github and the sample code. My fragment code is as follows -
/**
* A placeholder fragment containing a simple view.
*/
public class ThermoActivityFragment extends Fragment implements View.OnClickListener {
private static final String TAG = ThermoActivity.class.getSimpleName(); // for log
// Nest API instance holder
private NestAPI tNest;
private NestToken tToken;
private Thermostat tThermo;
private Structure tStruct;
// Save the ID's and secret
private static final String CLIENT_ID = Constants.CLIENT_ID;
private static final String CLIENT_SECRET = Constants.CLIENT_SECRET;
private static final String REDIRECT_URL = Constants.REDIRECT_URL;
private static final int AUTH_TOKEN_REQUEST_CODE = 111;
private static final int RESULT_OK = -1;
private static final String THERMOSTAT_KEY = "thermostat_key";
private static final String STRUCTURE_KEY = "structure_key";
private static final String DEG_F = "%d°F";
// Text View
private TextView tTempIncr;
private TextView tTempDecr;
private TextView tSetTemp;
public ThermoActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_thermo, container, false);
tTempIncr = (TextView) view.findViewById(R.id.temp_incr);
tTempDecr = (TextView) view.findViewById(R.id.temp_decr);
tSetTemp = (TextView) view.findViewById(R.id.temp_value);
view.findViewById(R.id.temp_incr).setOnClickListener(this);
view.findViewById(R.id.temp_decr).setOnClickListener(this);
NestAPI.setAndroidContext(getContext());
tNest = NestAPI.getInstance();
tNest.setConfig(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
// Auth flow
tToken = ThermoSettings.loadAuthToken(getContext());
if (tToken != null) {
authenticate(tToken);
} else {
tNest.launchAuthFlow(getActivity(), AUTH_TOKEN_REQUEST_CODE);
}
if (savedInstanceState != null) {
tThermo = savedInstanceState.getParcelable(THERMOSTAT_KEY);
tStruct = savedInstanceState.getParcelable(STRUCTURE_KEY);
//updateViews();
}
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(THERMOSTAT_KEY, tThermo);
outState.putParcelable(STRUCTURE_KEY, tStruct);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode != RESULT_OK || requestCode != AUTH_TOKEN_REQUEST_CODE) {
Log.e(TAG, "Finished with no result.");
return;
}
tToken = NestAPI.getAccessTokenFromIntent(intent);
if (tToken != null) {
ThermoSettings.saveAuthToken(getContext(), tToken);
authenticate(tToken);
} else {
Log.e(TAG, "Unable to resolve access token from payload.");
}
}
#Override
public void onStop() {
Log.d(TAG, "onStop");
super.onStop();
tNest.removeAllListeners();
}
#Override
public void onClick(View v) {
if (tThermo == null || tStruct == null)
return;
String tThermoId = tThermo.getDeviceId();
long temp = tThermo.getTargetTemperatureF();
switch (v.getId()) {
case R.id.temp_incr:
System.out.println("Temp Incr");
++temp;
tSetTemp.setText(String.format(DEG_F, temp));
tNest.thermostats.setTargetTemperatureF(tThermoId, temp);
break;
case R.id.temp_decr:
--temp;
tSetTemp.setText(String.format(DEG_F, temp));
tNest.thermostats.setTargetTemperatureF(tThermoId, temp);
break;
}
}
/**
* Authenticate with the Nest API and start listening for updates.
*
* #param token the token used to authenticate.
*/
private void authenticate(NestToken token) {
//NestAPI nest = NestAPI.getInstance();
tNest.authWithToken(token, new NestListener.AuthListener() {
#Override
public void onAuthSuccess() {
Log.v(TAG, "Authentication succeeded.");
fetchData();
}
#Override
public void onAuthFailure(NestException exception) {
Log.e(TAG, "Authentication failed with error: " + exception.getMessage());
ThermoSettings.saveAuthToken(getActivity(), null);
tNest.launchAuthFlow(getActivity(), AUTH_TOKEN_REQUEST_CODE);
}
#Override
public void onAuthRevoked() {
Log.e(TAG, "Auth token was revoked!");
ThermoSettings.saveAuthToken(getActivity(), null);
tNest.launchAuthFlow(getActivity(), AUTH_TOKEN_REQUEST_CODE);
}
});
}
/**
* Setup global listener, start listening, and update view when update received.
*/
private void fetchData() {
tNest.addGlobalListener(new NestListener.GlobalListener() {
#Override
public void onUpdate(#NonNull GlobalUpdate update) {
tThermo = update.getThermostats().get(0);
//System.out.println(tThermo);
tStruct = update.getStructures().get(0);
//updateViews();
}
});
}
}
The Settings file where I save the token is as follows -
public class ThermoSettings {
private static final String TOKEN_KEY = "token";
private static final String EXPIRATION_KEY = "expiration";
public static void saveAuthToken(Context context, NestToken token) {
if (token == null) {
getPrefs(context).edit().remove(TOKEN_KEY).remove(EXPIRATION_KEY).commit();
return;
}
getPrefs(context).edit()
.putString(TOKEN_KEY, token.getToken())
.putLong(EXPIRATION_KEY, token.getExpiresIn())
.commit();
}
public static NestToken loadAuthToken(Context context) {
final SharedPreferences prefs = getPrefs(context);
final String token = prefs.getString(TOKEN_KEY, null);
final long expirationDate = prefs.getLong(EXPIRATION_KEY, -1);
if (token == null || expirationDate == -1) {
return null;
}
return new NestToken(token, expirationDate);
}
private static SharedPreferences getPrefs(Context context) {
return context.getSharedPreferences(NestToken.class.getSimpleName(), 0);
}
}
What I'm trying to do -
I'm using a button on the homepage to enter the Activity. When I press the button, I see the Nest Authorization webpage, When I click on Accept, I see my UI but don't see the 'Authentication Succeeded' message in the log.
Can someone tell me what I'm doing wrong?
I finally got it to work. Earlier I was trying to make it work from a fragment. After I deleted the fragment and moved the code to MainActivity, it started to work.

Launching error on Android Mdpi Tablet: black screen shows

Hey I am working on Xamarin and Mvvmcross, my issue is, when I start the app on (Samsung Galaxy Tab 3) Mdpi device then it show black screen and nothing happens,
here is my code,
Splash.cs
namespace Mobile.UI.Droid
{
[Activity(
Label = "Mobile Tasks"
, MainLauncher = true
, Icon = "#drawable/icon"
,Theme = "#style/MyActionBarSplash"
, NoHistory = true)]
public class SplashScreen : MvxSplashScreenActivity
{
public SplashScreen()
: base(Resource.Layout.SplashScreen)
{
}
}
}
setup.cs
public class Setup : MvxAndroidSetup
{
Context _context;
public Setup(Context applicationContext) : base(applicationContext)
{
_context = applicationContext;
CurrentPlatform.Init();
Insights.Initialize(XamarinInsightsConstants.APIKey, applicationContext);
}
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var presenter = new CustomDroidViewPresenter();
Mvx.RegisterSingleton(presenter);
return presenter;
}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
protected override System.Collections.Generic.IList<string> ViewNamespaces
{
get
{
var toReturn = base.ViewNamespaces;
toReturn.Add("MOBILE.Mobile.UI.Droid.Controls");
toReturn.Add("MOBILE.Mobile.UI.Droid.Utilities");
return toReturn;
}
}
protected override IMvxTrace CreateDebugTrace()
{
return new DebugTrace();
}
protected override void InitializeLastChance()
{
var errorHandler = new ErrorDisplayer(ApplicationContext);
//Cirrious.MvvmCross.Plugins.Color.PluginLoader.Instance.EnsureLoaded();
base.InitializeLastChance();
Mvx.RegisterSingleton<IDeviceDetails>(new CustomDroidDetails());
//Mvx.RegisterSingleton<ILogger>(new DroidLogger());
Mvx.RegisterSingleton<IVersionDetail>(new VersionDetail());
}
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<LinearLayout>("ShapeBackground", (view) => new ShapeBackgroundBinding(view)));
registry.RegisterFactory(new MvxCustomBindingFactory<TextView>("CustomText", (view) => new CustomTextBinding(view)));
}
}
VersionDetail.cs
public class VersionDetail : IVersionDetail
{
public string GetAppVersion()
{
var version = Application.Context.PackageManager.GetPackageInfo(Application.Context.PackageName, 0).VersionName;
return version;
}
}
CustomDroidDetails
public class CustomDroidDetails : IDeviceDetails
{
public Platform Platform
{
get { return Platform.Droid; }
}
public DeviceType DeviceType
{
get { return DeviceType.Phone; }
}
}
logcat
Error

Categories

Resources