sAleksovski / react-native-android-widget

Build Android Widgets with React Native
https://sAleksovski.github.io/react-native-android-widget/
MIT License
570 stars 22 forks source link

Create custom Intent #97

Open OPavliuk-delhaize opened 1 month ago

OPavliuk-delhaize commented 1 month ago

Thanks to the great library, it has greatly simplified and increased the development speed of many applications!

But because we support many applications, we had a small problem with the click on the widget. Widgets have the same basis and differ due to the provision of Flavors. However, they all have the same package name.

When we configure a widget in Manifest we use it

 <intent-filter>
     <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
     <action android:name="com.myPackageName.WIDGET_CLICK" />
 </intent-filter>

And then myPackageName is used in your library when we make click

Intent intent = new Intent(mContext.getPackageName() + ".WIDGET_CLICK");

Therefore, when we have several applications when clicking on a widget, a pop-up appears and asks us to select an application to launch.

I have an idea.

import com.reactnativeandroidwidget.RNWidgetProvider

class MyWidgetProvider : RNWidgetProvider() {
}

Could we set our variable in the constructor or some other way, which will be used instead of WIDGET_CLICK if it exists?

sAleksovski commented 1 month ago

Why does android show the app chooser if the applications have different package name? Is there a way to force the intent to be handled by a given application?

You mentioned multiple applications, what pattern do you have for the package names of the applications?

Also, what is your clickAction when you get the chooser?

OPavliuk-delhaize commented 2 weeks ago

Hello, sorry for the long answer. Yes, you turned out to be right in android/app/build.gradle we set different names for different customers in productFlavors -> flavorName -> applicationId.

Several applications were triggered by clicking because there were problems with deep-link settings. However, there were still problems when several applications of the same customer were assembled for different environments. When updating applications, the widgets were triggered by incorrect updates. In one application, the user was logged in, but in the other not, and the widgets had a conflict.

The problem was completely solved due to the following actions

1) Using the library patch-package were made changes

diff --git a/node_modules/react-native-android-widget/android/src/main/java/com/reactnativeandroidwidget/RNWidgetProvider.java b/node_modules/react-native-android-widget/android/src/main/java/com/reactnativeandroidwidget/RNWidgetProvider.java
index 889a8b4..bb4a7df 100644
--- a/node_modules/react-native-android-widget/android/src/main/java/com/reactnativeandroidwidget/RNWidgetProvider.java
+++ b/node_modules/react-native-android-widget/android/src/main/java/com/reactnativeandroidwidget/RNWidgetProvider.java
@@ -20,6 +20,7 @@ import org.json.JSONObject;
 import java.util.concurrent.TimeUnit;

 public class RNWidgetProvider extends AppWidgetProvider {
+    protected String click_widget = "WIDGET_CLICK";
     @Override
     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
         super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
@@ -210,7 +211,7 @@ public class RNWidgetProvider extends AppWidgetProvider {
                 .build();
         }

-        Data data = buildData(context, widgetId, "WIDGET_CLICK", additionalData);
+        Data data = buildData(context, widgetId, this.click_widget, additionalData);
         startBackgroundTask(context, data);
     }
 }

After all the following operations, this may be redundant code, but others may find it useful.

  1. A script has been created that generates different classes for the widget and different manifests for all flavors in the project
    
    const fs = require('fs')
    const path = require('path')
    const ejs = require('ejs')

const flavors = ['flavor1', 'flavor2', 'flavor3', 'flavor4'] const envs = ['qa1', 'qa2', 'qa3'] const build_types = ['release', 'debug']

const templatePathManifest = path.join(dirname, 'templates', 'Manifest.ejs') const templatePathMyWidgetClass = path.join(dirname, 'templates', 'MyWidget.ejs')

const templateManifest = fs.readFileSync(templatePathManifest, 'utf8') const templateMyWidgetClass = fs.readFileSync(templatePathMyWidgetClass, 'utf8')

flavors.forEach(flavor => { envs.forEach(env => { // eslint-disable-next-line complexity build_types.forEach(build_type => { try { const envName = env[0].toUpperCase() + env.slice(1)

    const folderName = flavor + envName + build_type

    const className = `MyWidget${flavor[0].toUpperCase()}${flavor.slice(
      1,
    )}${env[0].toUpperCase()}${env.slice(1)}${build_type}`

    const intentName = `WIDGET_CLICK_${flavor.toUpperCase()}_${env.toUpperCase()}_${build_type.toUpperCase()}`

    const outputFlavoursDir = path.join(
      __dirname,
      '..',
      '..',
      'android',
      'app',
      'src',
      folderName,
    )

    const outputManifestPath = path.join(outputFlavoursDir, `AndroidManifest.xml`)

    const outputWidgetClassDir = path.join(
      outputFlavoursDir,
      'java',
      'com',
      'project',
      'widget',
    )

    const outputWidgetClassPath = path.join(outputWidgetClassDir, `${className}.kt`)

    const renderedManifest = ejs.render(templateManifest, { className, intentName })
    const renderedWidgetClass = ejs.render(templateMyWidgetClass, { className, intentName })

    if (!fs.existsSync(outputFlavoursDir)) {
      fs.mkdirSync(outputFlavoursDir, { recursive: true })
    }
    if (!fs.existsSync(outputWidgetClassDir)) {
      fs.mkdirSync(outputWidgetClassDir, { recursive: true })
    }

    fs.writeFileSync(outputManifestPath, renderedManifest)
    fs.writeFileSync(outputWidgetClassPath, renderedWidgetClass)
  } catch (e) {
    console.log('🚀 ~ file: index.js:78 ~ e:', e)
  }
})

}) })

**templates/Manifest.ejs**
Be careful with **packagename**

**templates/MyWidget.ejs**

package com.myprogect.widget

import com.reactnativeandroidwidget.RNWidgetProvider

class <%= className %> : RNWidgetProvider() { init { this.click_widget = "<%= intentName %>" } }


These changes fixed my problem. A little later I will check whether it is possible to work correctly without using the _patch package_
sAleksovski commented 1 week ago

I'm not sure I completely understand. You are updating the click_action, but there are also problems with the update?

What is in your clickAction prop when you face this issue?

Can you create an example repo with two apps with conflicting click/update handlers?