First of all, I'm working with some specific API ( Grand Stream GXV3275 phone ) which requires that Intent - BroadcastReceiver combo breaker.
When my device is on landscape orientation it works good so the problem came with Intent - BroadcastReceiver.
So I need that IntentFilter to know my HOOKEVENT ans then receive it with that BroadcastReceiver.
I just want to know why it doesn't even show the alert or don't work at all.
Is that possible to deal with IntentFilter on CordovaPlugin? With BroadcastReceiver?
I made some test on my CordovaActivity and HOOKEVENT ; updating a text-view.
So I assume that's a problem with CordovaPlugin.
I also tried to do:
CordovaActivity activity = (CordovaActivity) this.cordova.getActivity();
activity.getJs();
Which normally allow me to get string that works on my activity but gave me NPE..
public class Toast extends CordovaPlugin {
private String javascript = "";
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
initHookEvent();
switch (action) {
case "reversed":
reversedTest();
return true;
}
return false;
}
private Activity getActivity() { return this.cordova.getActivity();}
private void reversedTest(){
Configuration configuration = getActivity().getResources().getConfiguration();
if(configuration.orientation == Configuration.ORIENTATION_LANDSCAPE){
webView.sendJavascript("javascript:document.getElementById(\"combi\").innerHTML=\"Landscape\";");
}
webView.sendJavascript(javascript);
}
public void initHookEvent() {
IntentFilter filter = new IntentFilter("com.base.module.phone.HOOKEVENT");
getActivity().registerReceiver(broadcastReceiver, filter);
}
public BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
webView.sendJavascript("javascript:alert(\"test\");");
if (intent.getBooleanExtra("hookoff", false)){
javascript = "javascript:document.getElementById(\"combi\").innerHTML=\"decroche\";";
}
else{
javascript = "javascript:document.getElementById(\"combi\").innerHTML=\"raccroche\";";
}
}
};
I found myself my problem.
I create a specific plugin only for that after.
You just needed to :
webView.sendJavascript("javascript:document.getElementById(\"combi\").innerHTML=\"decroche\";");
And
getActivity().getApplicationContext().registerReceiver(broadcastReceiver_hook, filter_hook);
Here's my final plugin :
public class Hook extends CordovaPlugin {
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
initHookEvent();
return false;
}
/**
* Use to get the current Cordova Activity
* #return your Cordova activity
*/
private Activity getActivity() { return this.cordova.getActivity();}
/**
* Initializing GXV 3275 Hook Event
* You ABSOLUTELY need to precise getActivity().getApplicationContext()
* before registerReceiver() otherwise it won't get the good context.
*/
public void initHookEvent() {
IntentFilter filter_hook = new IntentFilter("com.base.module.phone.HOOKEVENT");
getActivity().getApplicationContext().registerReceiver(broadcastReceiver_hook, filter_hook);
}
/**
* BroadcastReceiver is also needed with GXV 3275 Hook Event
* Just sendJavascript for each cases
* /!\ webView /!\
* Is natively created by extending CordovaPlugin
*/
public BroadcastReceiver broadcastReceiver_hook = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if ( intent.getBooleanExtra("hookoff", false)){
webView.sendJavascript("javascript:document.getElementById(\"combi\").innerHTML=\"decroche\";");
webView.sendJavascript("javascript:document.getElementById(\"combi\").style.opacity = 1;");
}
else{
webView.sendJavascript("javascript:document.getElementById(\"combi\").innerHTML=\"raccroche\";");
webView.sendJavascript("javascript:document.getElementById(\"combi\").style.opacity = 1;");
}
}
};
}
Related
I have a Blazor Server App with the following DIV tags
<div class=mainScreen id="outerBox" style="width:#(TotalWidth)px;height:#(TotalHeight)px;">
foreach(Device thisDevice in MyDevices)
{
<div class=column id="#MainDiv" style="width:#(thisDevice.Width)px;height:#(thisDevice.Height)px;left:#thisDevice.XCoordinate;top:#thisDevice.YCoordinate">
Main Content Here...
</div>
}
</div>
I attempted to set the Height, Width, and X/Y coordinates using the code samples from this page - https://blazor.tips/blazor-how-to-ready-window-dimensions/ but that never worked and simply threw an uncaught exception no matter where I placed Try... blocks.
I then moved to a more straightforward JS call:
await Task.Run(async () =>
{
//Commenting out the OnInitializeAsync makes no difference but needs to be commented out when embedded
//On the main component
await this.OnInitializedAsync();
string data = await JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { });
JObject offsets = (JObject)JsonConvert.DeserializeObject(data);
TotalHeight = offsets.Value<int>("height");
TotalHeight = offsets.Value<int>("width");
}
//In my JS file, the function looks as follows:
function getMyWindow() {
var obj = {};
obj.width = window.width;
obj.height = window.height;
return JSON.stringify(obj);
}
If I make this call directly in the code, nothing ever happens - even with the OnInitializeAsync commented out.
var result = SetDimensions().Result;
If I place this method in the OnAfterRendor method:
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
if (!SetTheDivs)
SetTheDivs = SetDimensions().Result;
StateHasChanged();
}
}
protected override void OnInitialized()
{
base.OnInitialized();
this.OnAfterRender(true);
}
everything hangs until I kill the project. There are never any errors but the code never runs when I place breakpoints on the height or width statements.
I even added in the Async version to no avail:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await SetDimensions();
StateHasChanged();
}
}
protected override async Task OnInitializedAsync()
{
await this.OnAfterRenderAsync(true);
}
Same result as everything hangs. I am at a complete loss as to how to proceed and I really could use some help!
As a point of clarity, it is the call to the JS that results in the hang:
string data = await JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { });
I added in some alerts but they never run:
function getMyWindow() {
var obj = {};
alert("hi");
obj.width = screen.width;
obj.height = screen.height;
alert("ho");
return JSON.stringify(obj);
}
Thank you for your time!
BTW - I did change the double await to string data = JSRuntime.InvokeAsync<string>("getMyWindow", new object[] { }).Result;
UPDATE: I moved the JS call outside of the await altogether and I got the error:
InvalidOperationException: JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.
In this case, I am literally calling the method from the OnAfterRenderAsync method:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnInitializedAsync();
if (firstRender)
{
await SetDimensions();
StateHasChanged();
}
}
Not sure what you want... Copy the code below and run it, and tell us if that is what you were trying to get.
Index.razor
#page "/"
<div class=mainScreen id="outerBox" style="width:#($"{TotalWidth}px");height:#($"{TotalHeight}px"); background-color: green; top:60px; position:absolute">
#foreach (Device device in devices)
{
<div class=column style="width:#($"{device.Width}px");height:#($"{device.Height}px");margin-left:#($"{device.Left}px");margin-top:#($"{device.Top}px"); background-color:aliceblue">
#device.Name: Main Content Here...
</div>
}
</div>
#code {
private int TotalWidth = 520;
private int TotalHeight = 530;
private IList<Device> devices = Enumerable.Range(1, 5).Select(i => new Device { Name = $"Name {i}", Width = 520, Height = 100, Left = 0, Top = 5 }).ToList();
public class Device
{
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
}
Note: The OnInitialized{Async} pair of methods are the life-cycle methods of the base class ComponentBase. They are automatically called by the Blazor framework when a Razor component is being created. They are executed only once. You may override them, and add your logics, but you SHOULD never call them manually from your code.
This:
protected override async Task OnInitializedAsync()
{
await this.OnAfterRenderAsync(true);
}
This is wrong and must never be done. You should not call OnAfterRender{Async}. It is the Blazor framework that should call OnAfterRender{Async}, not the developer. Could you try to comprehend what your code is doing...
Try to understand that though the Razor components are defined as C# classes, they are special cases of Classes, that require special handling by the framework...
Update
Ken Tola, the following code I believe does what you're looking for. It reads the width and height of the window object, pass it to the Index component, and relocate your dear divs. Note that before the app relocates the divs, I check the values of the width and height, and determine the dimensions of the divs. This is of course is done for demonstration purposes, and you can manipulate those values as you wish...
Index.razor
#page "/"
#implements IDisposable
#inject IJSRuntime JSRuntime
<div class=mainScreen id="outerBox" style="width:#($" {TotalWidth}px");height:#($"{TotalHeight}px"); background-color: green; top:60px; position:absolute">
#foreach (Device device in devices)
{
<div class=column style="width:#($" {device.Width}px");height:#($"{device.Height}px");margin-left:#($"{device.Left}px");margin-top:#($"{device.Top}px"); background-color:aliceblue">
#device.Name: Main Content Here...
</div>
}
</div>
#code
{
private DotNetObjectReference<BrowserService> objRef;
private BrowserService BSS;
private int TotalWidth;
private int TotalHeight;
private IList<Device> devices = Enumerable.Range(1, 5).Select(i => new Device { Name = $"Name {i}", Width = 520, Height = 100, Left = 0, Top = 5 }).ToList();
public class Device
{
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Left { get; set; }
public int Top { get; set; }
}
protected override void OnInitialized()
{
BSS = new BrowserService();
objRef = DotNetObjectReference.Create(BSS);
BSS.Notify += OnNotify;
}
public void Dispose()
{
BSS.Notify -= OnNotify;
objRef?.Dispose();
}
public async Task OnNotify()
{
// Note that the notifier only notify your component
// that data is ready, and that the dimensions are read
// from a property. You can instead define event handler
// that pass the data in the form of EventArgs...
TotalWidth = BSS.Dimension.Width >= 877 ? 520 : 550;
TotalHeight = BSS.Dimension.Height >= 550 ? 800 : 1200;
await InvokeAsync(() => StateHasChanged());
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// This code is excuted only once, in order to initialize
// the JavaScript objects
if (firstRender)
{
await JSRuntime.InvokeAsync<object>
("myJsFunctions.getDimensions", objRef);
}
}
}
BrowserService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class BrowserService
{
public event Func<Task> Notify;
#nullable enable
public Dimension? Dimension { get; set; }
#nullable disable
[JSInvokableAttribute("GetDimensions")]
public async Task GetDimensions(string dimension)
{
JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
{
WriteIndented = true
};
var _dimension = System.Text.Json.JsonSerializer.Deserialize(dimension, typeof(Dimension), options);
Dimension = (Dimension)_dimension;
if (Notify != null)
{
await Notify?.Invoke();
}
}
}
public class Dimension
{
public int Width { get; set; }
public int Height { get; set; }
}
}
Startup.ConfigureServices
services.AddScoped<BrowserService>();
_Host.cshtml
<script src="_framework/blazor.server.js"></script>
<script type="text/javascript">
window.myJsFunctions = {
getDimensions: function (dotnetHelper) {
var dimension = {
width: window.innerWidth,
height: window.innerHeight
};
var json = JSON.stringify(dimension);
return dotnetHelper.invokeMethodAsync('GetDimensions', json);
}
};
</script>
Note: Consider handling the relocation of the div elements when the window is resized. It should be responsive, right ? Not sure that in your case you can employ media query. Any how, as you can see, I have designed the code in such a way that it takes into account that your div elements may need to be relocate again and again, thus it constantly (when resizing) notifies your Index component of the changing dimensions. I guess this merits a new question.....
I need to have something similar to Facebook Messenger's chat heads in my app, basically a bubble that can be viewed over other apps. I can't find anything online on this topic besides this question. So is there any way to make something like this with RN?
this feature is not support directly from react native and also this is not supported in ios therefore only you could implement it with java native code in android. to do that you should write a service in android which handle this element life cycle.
You could find here simple implementation of that in an android project. it's such a straightforward example and you can use it's service for your react native project and just change it's xml file to customize your view.
And just to start your service you must write a very simple react native module look like this
#ReactMethod
public void startService(Promise promise) {
String result = "Success";
Activity activity = getCurrentActivity();
if (activity != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(getReactApplicationContext())) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getCurrentActivity().getPackageName()));
getCurrentActivity().startActivityForResult(intent, MainActivity.DRAW_OVER_OTHER_APP_PERMISSION_REQUEST_CODE);
}
}
try {
Intent intent = new Intent(FloatingWidgetService.FLOATING_WIDGET_ID);
intent.setClass(this.getReactApplicationContext(), FloatingWidgetService.class);
getReactApplicationContext().startService(intent);
FloatingWidgetService.setUri(uri);
} catch (Exception e) {
promise.reject(e);
return;
}
promise.resolve(result);
}
in Android-8Oreo you must ask for canDrawOverlays and you can wait for result in your MainActivity like this:
private static final int DRAW_OVER_OTHER_APP_PERMISSION_REQUEST_CODE = 1222;
....
private void startFloatingWidgetService() {
if (!mStarted) {
Intent intent = new Intent(this, FloatingWidgetService.class);
ContextCompat.startForegroundService(this, intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.startForegroundService(intent);
}else{
startService(intent);
}
mStarted = true;
finish();
}
}
....
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DRAW_OVER_OTHER_APP_PERMISSION_REQUEST_CODE) {
//Check if the permission is granted or not.
if (resultCode == RESULT_OK)
//If permission granted start floating widget service
startFloatingWidgetService();
else
//Permission is not available then display toast
Toast.makeText(this,
getResources().getString(R.string.draw_other_app_permission_denied),
Toast.LENGTH_SHORT).show();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
And after that to come back again to your app from that service with the same appstate(not to create new activity) first define your activity launchmode as a singleInstance in manifest:
<activity
...
android:launchMode="singleInstance"
...
>
And use this kind of intent(!) in your service:
ReactApplicationContext reactContext = VideoViewModule.getReactContext();
Intent activityIntent = createSingleInstanceIntent();
reactContext.startActivity(activityIntent);
private Intent createSingleInstanceIntent() {
ReactApplicationContext reactContext = VideoViewModule.getReactContext();
String packageName = reactContext.getPackageName();
Intent launchIntent = reactContext.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
Intent activityIntent = null;
try {
Class<?> activityClass = Class.forName(className);
activityIntent = new Intent(reactContext, activityClass);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} catch (Exception e) {
stopCurrentService();
Log.e("POIFOIWEGBF", "Class not found", e);
}
return activityIntent;
}
I hope it helps.
I'm trying to create background service in Android Oreo using react-native-background-job but when I'll start the service is call success but Showing crash dialog service is stopped. please provide a solution for this problem.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(starter);
}
else {
context.startService(starter);
}
You are not allowed to start background service using startForegroundService - if service doesn't elevate itself into the foreground with startForeground and notification within 5 seconds, it will be killed by the system.
Take a look at related question and answers.
I got the solution in for react-native-background-job
android/src/main/java/com/pilloxa/backgroundjob/ReactNativeEventStarter.java
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
public static class MyHeadlessJsTaskService extends HeadlessJsTaskService {
private static final String LOG_TAG = MyHeadlessJsTaskService.class.getSimpleName();
#Override
#SuppressLint("WrongConstant")
public void onCreate() {
super.onCreate();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Context mContext = this.getApplicationContext();
String CHANNEL_ID = "Background job";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification =
new Notification.Builder(mContext, CHANNEL_ID)
.setContentTitle("Running background job")
.setContentText(mContext.getPackageName())
.setSmallIcon(R.drawable.ic_notification)
.build();
startForeground(1, notification);
}
}
#Nullable #Override protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
Bundle extras = intent.getExtras();
boolean allowExecutionInForeground = extras.getBoolean("allowExecutionInForeground", false);
long timeout = extras.getLong("timeout", 2000);
// For task with quick execution period additional check is required
ReactNativeHost reactNativeHost =
((ReactApplication) getApplicationContext()).getReactNativeHost();
boolean appInForeground = Utils.isReactNativeAppInForeground(reactNativeHost);
if (appInForeground && !allowExecutionInForeground) {
return null;
}
return new HeadlessJsTaskConfig(intent.getStringExtra("jobKey"), Arguments.fromBundle(extras),
timeout, allowExecutionInForeground);
}
public static void start(Context context, Bundle jobBundle) {
Intent starter = new Intent(context, MyHeadlessJsTaskService.class);
starter.putExtras(jobBundle);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(starter);
}else{
context.startService(starter);
}
}
}
}
I have designed a splash screen. The Java code is as below. In that screen, I have a button named "Do not show this screen again future". On pressing this button, the splash screen must never been shown in future, no matter how many times the app is started. How can I achieve this? Thanks in advance.
public class Qz1 extends Activity {
MyThread thread;
private class MyThread extends Thread
{
public boolean bRun = true;
#Override
public void run()
{
try
{
sleep(3200);
if (bRun)
{
startActivity(new Intent(getApplicationContext(), Qone.class));
Qz1.this.overridePendingTransition(R.anim.newright,
R.anim.newleft);
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qz1);
thread = new MyThread();
thread.start();
}
public void round1(View v){
Intent i = new Intent(Qz1.this, Qone.class);
startActivity(i);
this.overridePendingTransition(R.anim.newright,
R.anim.newleft);
}
}
Use SharedPreferences for this.
You can save a persisted boolean value by calling
getPreferences(MODE_PRIVATE).edit().putBoolean("no_splash", true).commit();
Then you can check that value by calling
boolean noSplash = getPreferences(MODE_PRIVATE).getBoolean("no_splash", false);
If noSplash is true then launch your main Activity immediately rather than starting the Thread.
User shared Preferences to achieve this, create a class
public class Preference {
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
public Preference(Context context) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
}
public void writePreference(String key, Object value) {
if(value instanceof Boolean) {
editor = sharedPreferences.edit();
editor.putBoolean(key, (Boolean) value);
editor.commit();
}
}
public Object readPreference(String key , Object defValue) {
if(defValue instanceof Boolean)
return sharedPreferences.getBoolean(key, (Boolean) defValue);
else
return null;
}
public Boolean getDisableSplash() {
return (Boolean) readPreference("disable", false);
}
public void disableSplash(Boolean value)) {
writePreference("disable", valve);
}
}
and in your main create an object of Preference to read and write preference
Preference preference = new Preference(YourActivity.this);
Boolean result = preference.getDisableSplash();
if(!result) {
// dissable you splash activity here and move to next one
}
and when you want to disable it simply
Preference preference = new Preference(YourActivity.this);
preference.disableSplash(true);
You can solve this by creating delegate activity, make an empty activity an set it as launcher activity,
On the delegate activity oncreate check your preference if you should show the splash finish the delegate activity and show it else show your home screen.
I want my thread/timer to stop and exit the app if I press the Back Button.
How do I tell it that the timer in timer.stop(); inside onBackPressed() is the same local variable I used for public void run() ?
Code:
Thread timer = new Thread(){
public void run(){
try{
sleep(4400);
} catch (InterruptedException e){
e.printStackTrace();
}finally{
Intent openHome = new Intent(Splash.this, main.class);
startActivity(openHome);
finish();
}
}
};
timer.start();
}
public void onBackPressed(){
timer.stop();
}
When I type this, it says 'timer' in timer.stop(); cannot be resolved.
Thread.stop() is deprecated and you should not use that.
A better Splashscreen implementation would be using Handler-Runnable something like the below.
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
public void run(){
Intent openHome = new Intent(Splash.this, main.class);
startActivity(openHome);
finish();
}
}
public void onCreate(Bundle b) {
super.onCreate(b);
handler.postDelayed(runnable, 4400);
}
public void onBackPressed(){
handler.removeCallbacks(runnable);
super.onBackPressed();
}