33import android .app .AlertDialog ;
44import android .content .Context ;
55import android .content .DialogInterface ;
6+ import android .content .Intent ;
7+ import android .content .res .Configuration ;
8+ import android .content .res .Resources ;
69import android .os .Handler ;
710import android .os .Looper ;
11+ import android .util .DisplayMetrics ;
812import android .webkit .WebSettings ;
9- import android .webkit .WebView ;
1013import androidx .annotation .NonNull ;
1114import androidx .annotation .Nullable ;
15+ import java .io .Serializable ;
1216import java .util .ArrayList ;
1317import java .util .HashMap ;
1418import java .util .Iterator ;
1519import java .util .List ;
1620import java .util .Map ;
21+ import java .util .concurrent .ConcurrentHashMap ;
1722import org .json .JSONArray ;
1823import org .json .JSONException ;
1924import org .json .JSONObject ;
25+ // import android.webkit.WebView;
2026
2127public class ModuleFeedback extends ModuleBase {
2228
2329 public enum FeedbackWidgetType {survey , nps , rating }
2430
25- public static class CountlyFeedbackWidget {
31+ public static class CountlyFeedbackWidget implements Serializable {
2632 public String widgetId ;
2733 public FeedbackWidgetType type ;
2834 public String name ;
2935 public String [] tags ;
36+ public String widgetVersion ;
3037 }
3138
3239 final static String NPS_EVENT_KEY = "[CLY]_nps" ;
@@ -123,6 +130,7 @@ static List<CountlyFeedbackWidget> parseFeedbackList(JSONObject requestResponse)
123130 String valId = jObj .optString ("_id" , "" );
124131 String valType = jObj .optString ("type" , "" );
125132 String valName = jObj .optString ("name" , "" );
133+ String widgetVersion = jObj .isNull ("wv" ) ? null : jObj .optString ("wv" , null );
126134 List <String > valTagsArr = new ArrayList <String >();
127135
128136 JSONArray jTagArr = jObj .optJSONArray ("tg" );
@@ -161,6 +169,7 @@ static List<CountlyFeedbackWidget> parseFeedbackList(JSONObject requestResponse)
161169 se .widgetId = valId ;
162170 se .name = valName ;
163171 se .tags = valTagsArr .toArray (new String [0 ]);
172+ se .widgetVersion = widgetVersion ;
164173
165174 parsedRes .add (se );
166175 } catch (Exception ex ) {
@@ -247,90 +256,132 @@ void presentFeedbackWidgetInternal(@Nullable final CountlyFeedbackWidget widgetI
247256 JSONObject customObjectToSendWithTheWidget = new JSONObject ();
248257 try {
249258 customObjectToSendWithTheWidget .put ("tc" , 1 );
259+ // these are used only in case of a widget with a version
260+ if (!Utils .isNullOrEmpty (widgetInfo .widgetVersion )) {
261+ customObjectToSendWithTheWidget .put ("rw" , 1 );
262+ customObjectToSendWithTheWidget .put ("xb" , 1 );
263+ }
250264 } catch (JSONException e ) {
251265 throw new RuntimeException (e );
252266 }
253267 widgetListUrl .append ("&custom=" );
254- widgetListUrl .append (customObjectToSendWithTheWidget .toString ());
255-
256- final String preparedWidgetUrl = widgetListUrl .toString ();
257-
258- L .d ("[ModuleFeedback] Using following url for widget:[" + widgetListUrl + "]" );
259-
260- //enable for chrome debugging
261- //WebView.setWebContentsDebuggingEnabled(true);
262-
263- final boolean useAlertDialog = true ;
264- Handler handler = new Handler (Looper .getMainLooper ());
265- handler .post (new Runnable () {
266- public void run () {
267- L .d ("[ModuleFeedback] Calling on main thread" );
268+ widgetListUrl .append (customObjectToSendWithTheWidget );
268269
269- try {
270+ String preparedWidgetUrl = widgetListUrl . toString ();
270271
271- ModuleRatings .RatingDialogWebView webView = new ModuleRatings .RatingDialogWebView (context );
272- webView .getSettings ().setJavaScriptEnabled (true );
273- webView .clearCache (true );
274- webView .clearHistory ();
275- webView .getSettings ().setCacheMode (WebSettings .LOAD_NO_CACHE );
276- webView .setWebViewClient (new ModuleRatings .FeedbackDialogWebViewClient ());
277- webView .loadUrl (preparedWidgetUrl );
278- webView .requestFocus ();
279-
280- AlertDialog .Builder builder = prepareAlertDialog (context , webView , closeButtonText , widgetInfo , devCallback );
281-
282- if (useAlertDialog ) {
283- // use alert dialog to host the webView
284- L .d ("[ModuleFeedback] Creating standalone Alert dialog" );
285- builder .show ();
286- } else {
287- // use dialog fragment to host the webView
288- L .d ("[ModuleFeedback] Creating Alert dialog in dialogFragment" );
272+ L .d ("[ModuleFeedback] Using following url for widget:[" + preparedWidgetUrl + "]" );
273+ if (!Utils .isNullOrEmpty (widgetInfo .widgetVersion )) {
274+ L .d ("[ModuleFeedback] Will use transparent activity for displaying the widget" );
275+ showFeedbackWidget_newActivity (context , preparedWidgetUrl , widgetInfo , devCallback );
276+ } else {
277+ L .d ("[ModuleFeedback] Will use dialog for displaying the widget" );
278+ //enable for chrome debugging
279+ // WebView.setWebContentsDebuggingEnabled(true);
280+ Handler handler = new Handler (Looper .getMainLooper ());
281+ handler .post (new Runnable () {
282+ public void run () {
283+ L .d ("[ModuleFeedback] Calling on main thread" );
289284
290- //CountlyDialogFragment newFragment = CountlyDialogFragment.newInstance(builder);
291- //newFragment.show(fragmentManager, "CountlyFragmentDialog");
292- }
285+ try {
286+ showFeedbackWidget (context , widgetInfo , closeButtonText , devCallback , preparedWidgetUrl );
293287
294- if (devCallback != null ) {
295- devCallback .onFinished (null );
296- }
297- } catch (Exception ex ) {
298- L .e ("[ModuleFeedback] Failed at displaying feedback widget dialog, [" + ex .toString () + "]" );
299- if (devCallback != null ) {
300- devCallback .onFinished ("Failed at displaying feedback widget dialog, [" + ex .toString () + "]" );
288+ if (devCallback != null ) {
289+ devCallback .onFinished (null );
290+ }
291+ } catch (Exception ex ) {
292+ L .e ("[ModuleFeedback] Failed at displaying feedback widget dialog, [" + ex .toString () + "]" );
293+ if (devCallback != null ) {
294+ devCallback .onFinished ("Failed at displaying feedback widget dialog, [" + ex .toString () + "]" );
295+ }
301296 }
302297 }
303- }
304- });
298+ });
299+ }
305300 }
306301
307- AlertDialog .Builder prepareAlertDialog (@ NonNull final Context context , @ NonNull WebView webView , @ Nullable String closeButtonText , @ NonNull final CountlyFeedbackWidget widgetInfo , @ Nullable final FeedbackCallback devCallback ) {
308- AlertDialog .Builder builder = new AlertDialog .Builder (context );
309- builder .setView (webView );
310- builder .setCancelable (false );
302+ private void showFeedbackWidget (Context context , CountlyFeedbackWidget widgetInfo , String closeButtonText , FeedbackCallback devCallback , String url ) {
303+ ModuleRatings .RatingDialogWebView webView = new ModuleRatings .RatingDialogWebView (context );
304+ webView .getSettings ().setJavaScriptEnabled (true );
305+ webView .clearCache (true );
306+ webView .clearHistory ();
307+ webView .getSettings ().setCacheMode (WebSettings .LOAD_NO_CACHE );
308+ ModuleRatings .FeedbackDialogWebViewClient webViewClient = new ModuleRatings .FeedbackDialogWebViewClient ();
309+ webView .setWebViewClient (webViewClient );
310+ webView .loadUrl (url );
311+ webView .requestFocus ();
312+
313+ AlertDialog .Builder builder = new AlertDialog .Builder (context ).setView (webView ).setCancelable (false );
311314 String usedCloseButtonText = closeButtonText ;
312315 if (closeButtonText == null || closeButtonText .isEmpty ()) {
313316 usedCloseButtonText = "Close" ;
314317 }
315318 builder .setNeutralButton (usedCloseButtonText , new DialogInterface .OnClickListener () {
316319 @ Override public void onClick (DialogInterface dialogInterface , int i ) {
317320 L .d ("[ModuleFeedback] Cancel button clicked for the feedback widget" );
318- reportFeedbackWidgetCancelButton (widgetInfo , deviceInfo . mp . getAppVersion ( context ) );
321+ reportFeedbackWidgetCancelButton (widgetInfo );
319322
320323 if (devCallback != null ) {
321324 devCallback .onClosed ();
322325 }
323326 }
324327 });
325- return builder ;
328+ builder . show () ;
326329 }
327330
328- void reportFeedbackWidgetCancelButton (@ NonNull CountlyFeedbackWidget widgetInfo , @ NonNull String appVersion ) {
331+ private void showFeedbackWidget_newActivity (@ NonNull Context context , String url , CountlyFeedbackWidget widgetInfo , FeedbackCallback devCallback ) {
332+ DisplayMetrics displayMetrics = deviceInfo .mp .getDisplayMetrics (context );
333+ Resources resources = context .getResources ();
334+ int currentOrientation = resources .getConfiguration ().orientation ;
335+ boolean portrait = currentOrientation == Configuration .ORIENTATION_PORTRAIT ;
336+
337+ int width = displayMetrics .widthPixels ;
338+ int height = displayMetrics .heightPixels ;
339+
340+ // this calculation needs improvement for status bar and navigation bar
341+ int portraitWidth = portrait ? width : height ;
342+ int portraitHeight = portrait ? height : width ;
343+ int landscapeWidth = portrait ? height : width ;
344+ int landscapeHeight = portrait ? width : height ;
345+
346+ Map <Integer , TransparentActivityConfig > placementCoordinates = new ConcurrentHashMap <>();
347+ TransparentActivityConfig pConfig = new TransparentActivityConfig (0 , 0 , portraitWidth , portraitHeight );
348+ TransparentActivityConfig lConfig = new TransparentActivityConfig (0 , 0 , landscapeWidth , landscapeHeight );
349+ pConfig .url = url ;
350+ lConfig .url = url ;
351+ placementCoordinates .put (Configuration .ORIENTATION_PORTRAIT , pConfig );
352+ placementCoordinates .put (Configuration .ORIENTATION_LANDSCAPE , lConfig );
353+
354+ Intent intent = new Intent (context , TransparentActivity .class );
355+ intent .putExtra (TransparentActivity .CONFIGURATION_LANDSCAPE , placementCoordinates .get (Configuration .ORIENTATION_LANDSCAPE ));
356+ intent .putExtra (TransparentActivity .CONFIGURATION_PORTRAIT , placementCoordinates .get (Configuration .ORIENTATION_PORTRAIT ));
357+ intent .putExtra (TransparentActivity .ORIENTATION , context .getResources ().getConfiguration ().orientation );
358+ intent .putExtra (TransparentActivity .WIDGET_INFO , widgetInfo );
359+
360+ Long id = System .currentTimeMillis ();
361+ intent .putExtra (TransparentActivity .ID_CALLBACK , id );
362+ if (devCallback != null ) {
363+ ContentCallback feedbackCallback = new ContentCallback () {
364+ @ Override public void onContentCallback (ContentStatus contentStatus , Map <String , Object > contentData ) {
365+ if (contentStatus .equals (ContentStatus .CLOSED )) {
366+ devCallback .onClosed ();
367+ } else {
368+ devCallback .onFinished (null );
369+ }
370+ }
371+ };
372+ TransparentActivity .contentCallbacks .put (id , feedbackCallback );
373+ }
374+
375+ intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
376+ _cly .context_ .startActivity (intent );
377+ }
378+
379+ void reportFeedbackWidgetCancelButton (@ NonNull CountlyFeedbackWidget widgetInfo ) {
329380 L .d ("[reportFeedbackWidgetCancelButton] Cancel button event" );
330381 if (consentProvider .getConsent (Countly .CountlyFeatureNames .feedback )) {
331382 final Map <String , Object > segm = new HashMap <>();
332383 segm .put ("platform" , "android" );
333- segm .put ("app_version" , appVersion );
384+ segm .put ("app_version" , cachedAppVersion );
334385 segm .put ("widget_id" , "" + widgetInfo .widgetId );
335386 segm .put ("closed" , "1" );
336387 final String key ;
@@ -675,21 +726,37 @@ public void getAvailableFeedbackWidgets(@Nullable RetrieveFeedbackWidgets callba
675726 }
676727
677728 /**
678- * Present a chosen feedback widget in an alert dialog
729+ * Present a chosen feedback widget
679730 *
680731 * @param widgetInfo
681732 * @param context
682733 * @param closeButtonText if this is null, no "close" button will be shown
683734 * @param devCallback
735+ * @deprecated use {@link #presentFeedbackWidget(CountlyFeedbackWidget, Context, FeedbackCallback)} instead
684736 */
685737 public void presentFeedbackWidget (@ Nullable CountlyFeedbackWidget widgetInfo , @ Nullable Context context , @ Nullable String closeButtonText , @ Nullable FeedbackCallback devCallback ) {
686738 synchronized (_cly ) {
687- L .i ("[Feedback] Trying to present feedback widget in an alert dialog " );
739+ L .i ("[Feedback] Trying to present feedback widget" );
688740
689741 presentFeedbackWidgetInternal (widgetInfo , context , closeButtonText , devCallback );
690742 }
691743 }
692744
745+ /**
746+ * Present a chosen feedback widget
747+ *
748+ * @param widgetInfo the widget to present
749+ * @param context the context to use for displaying the feedback widget
750+ * @param devCallback callback to be called when the feedback widget is closed
751+ */
752+ public void presentFeedbackWidget (@ Nullable CountlyFeedbackWidget widgetInfo , @ Nullable Context context , @ Nullable FeedbackCallback devCallback ) {
753+ synchronized (_cly ) {
754+ L .i ("[Feedback] Trying to present feedback widget" );
755+
756+ presentFeedbackWidgetInternal (widgetInfo , context , null , devCallback );
757+ }
758+ }
759+
693760 /**
694761 * Download data for a specific widget so that it can be displayed with a custom UI
695762 * When requesting this data, it will count as a shown widget (will increment that "shown" count in the dashboard)
@@ -820,4 +887,4 @@ public void presentRating(@NonNull Context context, @NonNull String nameIDorTag,
820887 }
821888 }
822889 }
823- }
890+ }
0 commit comments