Как реализовать функцию Rate It в приложении для Android


95

Я разрабатываю приложение для Android. В котором все работает нормально. Мое приложение готово к запуску. Но здесь мне нужно реализовать еще одну функцию. Мне нужно отобразить всплывающее окно, содержащее

Rate It и Remind me later

Здесь, если какой-либо пользователь оценит приложение на рынке, всплывающее окно не исчезнет. Я поискал в Google и нашел одну ссылку . Я понимаю, что это невозможно узнать. Так что мне нужно предложение по этому поводу.

Кто-нибудь сталкивался с такой ситуацией раньше? Если да, то есть ли какое-либо решение или альтернатива для этого?


Так вы просите просто Оценить / напомнить мне позже или вы спрашиваете, как узнать, оценил ли конкретный пользователь приложение для Android?
wtsang02

1
Я реализовал всплывающее окно. но как узнать, оценил ли пользователь приложение или нет
Naveen

-1 Я не вижу разницы между этим вопросом и тем, что в ссылке.
wtsang02

2
@ wtsang02, Может и правда. Но вот вопрос. его спросили Mar 15 2011. так прошло почти 20 месяцев. Я думаю, что у кого-то есть решение или альтернатива для моего требования. это yi размещено здесь.
Naveen

Вы можете использовать библиотеку github.com/Vorlonsoft/AndroidRate ( implementation 'com.vorlonsoft:androidrate:1.0.3')
Александр Савин

Ответы:


182

В некоторой степени я реализовал это некоторое время назад. Невозможно узнать, оценил ли пользователь приложение, чтобы рейтинги не превратились в валюту (некоторые разработчики могут добавить опцию типа «Оценить это приложение и получить то-то и то-то в приложении бесплатно»).

Написанный мною класс предоставляет три кнопки и настраивает диалоговое окно так, чтобы оно отображалось только после того, как приложение было запущено n(у пользователей больше шансов оценить приложение, если они использовали его немного раньше. Большинство из них маловероятно чтобы даже знать, что он делает при первом запуске):

public class AppRater {
    private final static String APP_TITLE = "App Name";// App Name
    private final static String APP_PNAME = "com.example.name";// Package Name

    private final static int DAYS_UNTIL_PROMPT = 3;//Min number of days
    private final static int LAUNCHES_UNTIL_PROMPT = 3;//Min number of launches

    public static void app_launched(Context mContext) {
        SharedPreferences prefs = mContext.getSharedPreferences("apprater", 0);
        if (prefs.getBoolean("dontshowagain", false)) { return ; }

        SharedPreferences.Editor editor = prefs.edit();

        // Increment launch counter
        long launch_count = prefs.getLong("launch_count", 0) + 1;
        editor.putLong("launch_count", launch_count);

        // Get date of first launch
        Long date_firstLaunch = prefs.getLong("date_firstlaunch", 0);
        if (date_firstLaunch == 0) {
            date_firstLaunch = System.currentTimeMillis();
            editor.putLong("date_firstlaunch", date_firstLaunch);
        }

        // Wait at least n days before opening
        if (launch_count >= LAUNCHES_UNTIL_PROMPT) {
            if (System.currentTimeMillis() >= date_firstLaunch + 
                    (DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000)) {
                showRateDialog(mContext, editor);
            }
        }

        editor.commit();
    }   

    public static void showRateDialog(final Context mContext, final SharedPreferences.Editor editor) {
        final Dialog dialog = new Dialog(mContext);
        dialog.setTitle("Rate " + APP_TITLE);

        LinearLayout ll = new LinearLayout(mContext);
        ll.setOrientation(LinearLayout.VERTICAL);

        TextView tv = new TextView(mContext);
        tv.setText("If you enjoy using " + APP_TITLE + ", please take a moment to rate it. Thanks for your support!");
        tv.setWidth(240);
        tv.setPadding(4, 0, 4, 10);
        ll.addView(tv);

        Button b1 = new Button(mContext);
        b1.setText("Rate " + APP_TITLE);
        b1.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + APP_PNAME)));
                dialog.dismiss();
            }
        });        
        ll.addView(b1);

        Button b2 = new Button(mContext);
        b2.setText("Remind me later");
        b2.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
        ll.addView(b2);

        Button b3 = new Button(mContext);
        b3.setText("No, thanks");
        b3.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                if (editor != null) {
                    editor.putBoolean("dontshowagain", true);
                    editor.commit();
                }
                dialog.dismiss();
            }
        });
        ll.addView(b3);

        dialog.setContentView(ll);        
        dialog.show();        
    }
}

Интегрировать класс так же просто, как добавить:

AppRater.app_launched(this);

К вашей деятельности. Его нужно добавить только в одно действие во всем приложении.


1
Это не поддерживает несколько пользователей, использующих одно и то же устройство.
AsafK

1
@AsafK Да, но с несколькими пользователями, использующими одно и то же устройство, можно работать, только показывая appraterдиалоговое окно после аутентификации и изменяя его shared preferenceдля включения адреса электронной почты Google в файл key.
Стивен

1
Привет, у меня только один вопрос. Почему вы сделали все статичным? Спасибо, Рагхав!
Ручир Барония 01

2
Привет, я пробую ваш код выше. Я вложил AppRater.app_launched(this);в свой onCreate()файл MainActivity. Я также изменил минимальное количество запусков на 2. Но я не вижу диалогового окна после запуска двух приложений. Можете ли вы помочь мне? Спасибо!
Исключение

2
Лучше используйте enum Context.MODE_PRIVATE-context.getSharedPreferences("apprater", Context.MODE_PRIVATE);
Vivek

18

Мой с использованием DialogFragment:

public class RateItDialogFragment extends DialogFragment {
    private static final int LAUNCHES_UNTIL_PROMPT = 10;
    private static final int DAYS_UNTIL_PROMPT = 3;
    private static final int MILLIS_UNTIL_PROMPT = DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000;
    private static final String PREF_NAME = "APP_RATER";
    private static final String LAST_PROMPT = "LAST_PROMPT";
    private static final String LAUNCHES = "LAUNCHES";
    private static final String DISABLED = "DISABLED";

    public static void show(Context context, FragmentManager fragmentManager) {
        boolean shouldShow = false;
        SharedPreferences sharedPreferences = getSharedPreferences(context);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        long currentTime = System.currentTimeMillis();
        long lastPromptTime = sharedPreferences.getLong(LAST_PROMPT, 0);
        if (lastPromptTime == 0) {
            lastPromptTime = currentTime;
            editor.putLong(LAST_PROMPT, lastPromptTime);
        }

        if (!sharedPreferences.getBoolean(DISABLED, false)) {
            int launches = sharedPreferences.getInt(LAUNCHES, 0) + 1;
            if (launches > LAUNCHES_UNTIL_PROMPT) {
                if (currentTime > lastPromptTime + MILLIS_UNTIL_PROMPT) {
                    shouldShow = true;
                }
            }
            editor.putInt(LAUNCHES, launches);
        }

        if (shouldShow) {
            editor.putInt(LAUNCHES, 0).putLong(LAST_PROMPT, System.currentTimeMillis()).commit();
            new RateItDialogFragment().show(fragmentManager, null);
        } else {
            editor.commit();
        }
    }

    private static SharedPreferences getSharedPreferences(Context context) {
        return context.getSharedPreferences(PREF_NAME, 0);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.rate_title)
                .setMessage(R.string.rate_message)
                .setPositiveButton(R.string.rate_positive, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + getActivity().getPackageName())));
                        getSharedPreferences(getActivity()).edit().putBoolean(DISABLED, true).commit();
                        dismiss();
                    }
                })
                .setNeutralButton(R.string.rate_remind_later, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dismiss();
                    }
                })
                .setNegativeButton(R.string.rate_never, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        getSharedPreferences(getActivity()).edit().putBoolean(DISABLED, true).commit();
                        dismiss();
                    }
                }).create();
    }
}

Затем используйте его в onCreate()своем основном FragmentActivity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...

    RateItDialogFragment.show(this, getFragmentManager());

}

Хороший! Я бы просто поместил editor.commit () перед показом DialogFragment на случай, если что-то пойдет не так при загрузке Dialog.
нарко

@narko Спасибо. Обновлено.
миксель

Примечание: это может привести к утечке памяти, если вы используете для сохранения общих предпочтений. Если вы внимательно заметили в setPositiveButtonи setNegativeButton, он записывает в общие настройки с помощью фиксации, но если вы используете apply, который является асинхронным и будет сохранять ссылку на действие, пока оно не завершится, и сразу после этого он вызывает отклонение. Команда Dismiss попытается уничтожить фрагмент, но не может, потому что действие удерживается / используется процессом применения общих предпочтений (я носил это, потому что AndroidStudio предложит пользователю изменить фиксацию для применения, не делайте этого, если вы используйте другую логику)
Сай

@mixel Как изменить код, чтобы его можно было использовать в Activity и без фрагментов?
user1090751

7

Я думаю, что то, что вы пытаетесь сделать, вероятно, контрпродуктивно.

Как правило, неплохо было бы упростить людям оценку приложений, поскольку большинство людей делают это, потому что им нравится приложение. Ходят слухи, что количество оценок влияет на ваш рыночный рейтинг (хотя я не вижу этому подтверждений). Путаница в рейтинге пользователей - через экраны с клячками - скорее всего, заставит людей избавиться от них, оставив плохой рейтинг.

Добавление возможности напрямую оценивать приложение привело к небольшому снижению числовых оценок для моей бесплатной версии и небольшому увеличению в моем платном приложении. Для бесплатного приложения мои 4-звездочные рейтинги выросли больше, чем мои 5-звездочные рейтинги, поскольку люди, которые считали мое приложение хорошим, но не отличным, также начали его оценивать. Изменение было около -0,2. Для платных сдача составила около +0,1. Я должен удалить его из бесплатной версии, но я люблю получать много комментариев.

Я помещаю свою кнопку оценки в экран настроек (предпочтений), где она не влияет на нормальную работу. Это все равно увеличило мою оценку в 4 или 5 раз. Я не сомневаюсь, что если я попытаюсь уговорить своих пользователей выставить рейтинг, я получу множество пользователей, которые поставят мне плохие оценки в качестве протеста.


100% правда. То же самое произошло и с моим бесплатным приложением.
Акаш Варлани

6

AndroidRate - это библиотека, которая поможет вам продвигать ваше приложение для Android, предлагая пользователям оценить приложение после его использования в течение нескольких дней.

Модуль Gradle:

dependencies {
  implementation 'com.vorlonsoft:androidrate:1.0.8'
}

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  AppRate.with(this)
      .setStoreType(StoreType.GOOGLEPLAY) //default is GOOGLEPLAY (Google Play), other options are
                                          //           AMAZON (Amazon Appstore) and
                                          //           SAMSUNG (Samsung Galaxy Apps)
      .setInstallDays((byte) 0) // default 10, 0 means install day
      .setLaunchTimes((byte) 3) // default 10
      .setRemindInterval((byte) 2) // default 1
      .setRemindLaunchTimes((byte) 2) // default 1 (each launch)
      .setShowLaterButton(true) // default true
      .setDebug(false) // default false
      //Java 8+: .setOnClickButtonListener(which -> Log.d(MainActivity.class.getName(), Byte.toString(which)))
      .setOnClickButtonListener(new OnClickButtonListener() { // callback listener.
          @Override
          public void onClickButton(byte which) {
              Log.d(MainActivity.class.getName(), Byte.toString(which));
          }
      })
      .monitor();

  if (AppRate.with(this).getStoreType() == StoreType.GOOGLEPLAY) {
      //Check that Google Play is available
      if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) != ConnectionResult.SERVICE_MISSING) {
          // Show a dialog if meets conditions
          AppRate.showRateDialogIfMeetsConditions(this);
      }
  } else {
      // Show a dialog if meets conditions
      AppRate.showRateDialogIfMeetsConditions(this);
  }
}

Условия по умолчанию для отображения диалогового окна скорости следующие:

  1. Приложение запускается более чем на 10 дней позже установки. Изменить через AppRate#setInstallDays(byte).
  2. Приложение запускалось более 10 раз. Изменить через AppRate#setLaunchTimes(byte).
  3. Приложение запускается более чем через 1 день после нажатия нейтральной кнопки. Изменить через AppRate#setRemindInterval(byte).
  4. Приложение запускается X раз и X% 1 = 0. Изменить через AppRate#setRemindLaunchTimes(byte).
  5. По умолчанию приложение показывает нейтральный диалог (Напомнить позже). Изменить через setShowLaterButton(boolean).
  6. Чтобы указать обратный вызов при нажатии кнопки. То же значение, что и второй аргумент, DialogInterface.OnClickListener#onClickбудет передано в аргумент onClickButton.
  7. Настройка AppRate#setDebug(boolean)гарантирует, что запрос рейтинга будет отображаться каждый раз при запуске приложения. Эта функция предназначена только для разработки! .

Дополнительные требования к настраиваемому событию для отображения диалога

Вы можете добавить дополнительные необязательные требования для отображения диалога. Каждое требование может быть добавлено / обозначено как уникальная строка. Вы можете установить минимальное количество для каждого такого события (например, «action_performed» 3 раза, «button_clicked» 5 раз и т. Д.)

AppRate.with(this).setMinimumEventCount(String, short);
AppRate.with(this).incrementEventCount(String);
AppRate.with(this).setEventCountValue(String, short);

Очистить флаг диалога отображения

Если вы хотите снова показать диалог, позвоните AppRate#clearAgreeShowDialog().

AppRate.with(this).clearAgreeShowDialog();

Когда кнопка нажимается

звоните AppRate#showRateDialog(Activity).

AppRate.with(this).showRateDialog(this);

Настроить индивидуальный вид

звоните AppRate#setView(View).

LayoutInflater inflater = (LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.custom_dialog, (ViewGroup)findViewById(R.id.layout_root));
AppRate.with(this).setView(view).monitor();

Конкретная тема

Вы можете использовать определенную тему для расширения диалогового окна.

AppRate.with(this).setThemeResId(int);

Пользовательский диалог

Если вы хотите использовать собственные метки диалоговых окон, переопределите строковые xml-ресурсы в своем приложении.

<resources>
    <string name="rate_dialog_title">Rate this app</string>
    <string name="rate_dialog_message">If you enjoy playing this app, would you mind taking a moment to rate it? It won\'t take more than a minute. Thanks for your support!</string>
    <string name="rate_dialog_ok">Rate It Now</string>
    <string name="rate_dialog_cancel">Remind Me Later</string>
    <string name="rate_dialog_no">No, Thanks</string>
</resources>

Убедитесь, что Google Play доступен

if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) != ConnectionResult.SERVICE_MISSING) {

}


3

Это решение очень похоже на представленные выше. Единственное отличие состоит в том, что вы сможете задерживать запрос диалогового окна рейтинга по запускам и дням. Если нажать кнопку «Напомнить позже», то всплывающее окно откладывается на 3 дня и 10 запусков. То же самое делается для тех, кто выбрал его для оценки, однако задержки больше (чтобы не беспокоить пользователя так скоро, если он действительно оценил приложение. Это можно изменить, чтобы не показывать снова, тогда вам придется измените код на свой вкус). Надеюсь, это кому-то поможет!

public class AppRater {
    private final static String APP_TITLE = "your_app_name";
    private static String PACKAGE_NAME = "your_package_name";
    private static int DAYS_UNTIL_PROMPT = 5;
    private static int LAUNCHES_UNTIL_PROMPT = 10;
    private static long EXTRA_DAYS;
    private static long EXTRA_LAUCHES;
    private static SharedPreferences prefs;
    private static SharedPreferences.Editor editor;
    private static Activity activity;

    public static void app_launched(Activity activity1) {
        activity = activity1;

        Configs.sendScreenView("Avaliando App", activity);

        PACKAGE_NAME = activity.getPackageName();

        prefs = activity.getSharedPreferences("apprater", Context.MODE_PRIVATE);
        if (prefs.getBoolean("dontshowagain", false)) 
            return;

        editor = prefs.edit();

        EXTRA_DAYS = prefs.getLong("extra_days", 0);
        EXTRA_LAUCHES = prefs.getLong("extra_launches", 0);

        // Increment launch counter
        long launch_count = prefs.getLong("launch_count", 0) + 1;
        editor.putLong("launch_count", launch_count);

        // Get date of first launch
        Long date_firstLaunch = prefs.getLong("date_firstlaunch", 0);
        if (date_firstLaunch == 0) {
            date_firstLaunch = System.currentTimeMillis();
            editor.putLong("date_firstlaunch", date_firstLaunch);
        }

        // Wait at least n days before opening
        if (launch_count >= (LAUNCHES_UNTIL_PROMPT + EXTRA_LAUCHES))
            if (System.currentTimeMillis() >= date_firstLaunch + (DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000) + EXTRA_DAYS)
                showRateDialog();

        editor.commit();
    }   

    public static void showRateDialog() {
        final Dialog dialog = new Dialog(activity);
        dialog.setTitle("Deseja avaliar o aplicativo " + APP_TITLE + "?");

        LinearLayout ll = new LinearLayout(activity);
        ll.setOrientation(LinearLayout.VERTICAL);
        ll.setPadding(5, 5, 5, 5);

        TextView tv = new TextView(activity);
        tv.setTextColor(activity.getResources().getColor(R.color.default_text));
        tv.setText("Ajude-nos a melhorar o aplicativo com sua avaliação no Google Play!");
        tv.setWidth(240);
        tv.setGravity(Gravity.CENTER);
        tv.setPadding(5, 5, 5, 5);
        ll.addView(tv);

        Button b1 = new Button(activity);
        b1.setTextColor(activity.getResources().getColor(R.color.default_text));
        b1.setBackground(activity.getResources().getDrawable(R.drawable.rounded_blue_box));
        b1.setTextColor(Color.WHITE);
        b1.setText("Avaliar aplicativo " + APP_TITLE + "!");
        b1.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Configs.sendHitEvents(Configs.APP_RATER, Configs.CATEGORIA_ANALYTICS, "Clique", "Avaliar", activity);

                activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + PACKAGE_NAME)));
                delayDays(60);
                delayLaunches(30);
                dialog.dismiss();
            }
        });        
        ll.addView(b1);
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) b1.getLayoutParams();
        params.setMargins(5, 3, 5, 3);
        b1.setLayoutParams(params);

        Button b2 = new Button(activity);
        b2.setTextColor(activity.getResources().getColor(R.color.default_text));
        b2.setBackground(activity.getResources().getDrawable(R.drawable.rounded_blue_box));
        b2.setTextColor(Color.WHITE);
        b2.setText("Lembre-me mais tarde!");
        b2.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Configs.sendHitEvents(Configs.APP_RATER, Configs.CATEGORIA_ANALYTICS, "Clique", "Avaliar Mais Tarde", activity);
                delayDays(3);
                delayLaunches(10);
                dialog.dismiss();
            }
        });
        ll.addView(b2);
        params = (LinearLayout.LayoutParams) b2.getLayoutParams();
        params.setMargins(5, 3, 5, 3);
        b2.setLayoutParams(params);

        Button b3 = new Button(activity);
        b3.setTextColor(activity.getResources().getColor(R.color.default_text));
        b3.setBackground(activity.getResources().getDrawable(R.drawable.rounded_blue_box));
        b3.setTextColor(Color.WHITE);
        b3.setText("Não, obrigado!");
        b3.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Configs.sendHitEvents(Configs.APP_RATER, Configs.CATEGORIA_ANALYTICS, "Clique", "Não Avaliar", activity);

                if (editor != null) {
                    editor.putBoolean("dontshowagain", true);
                    editor.commit();
                }
                dialog.dismiss();
            }
        });
        ll.addView(b3);
        params = (LinearLayout.LayoutParams) b3.getLayoutParams();
        params.setMargins(5, 3, 5, 0);
        b3.setLayoutParams(params);

        dialog.setContentView(ll);        
        dialog.show();        
    }

    private static void delayLaunches(int numberOfLaunches) {
        long extra_launches = prefs.getLong("extra_launches", 0) + numberOfLaunches;
        editor.putLong("extra_launches", extra_launches);
        editor.commit();
    }

    private static void delayDays(int numberOfDays) {
        Long extra_days = prefs.getLong("extra_days", 0) + (numberOfDays * 1000 * 60 * 60 * 24);
        editor.putLong("extra_days", extra_days);
        editor.commit();
    }
}

Кнопки имеют определенный цвет и фон. Фон такой, как показано в этом XML-файле:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="10dp"
    android:shape="rectangle" >

    <solid android:color="#2E78B9" />

    <corners
        android:bottomLeftRadius="6dp"
        android:bottomRightRadius="6dp"
        android:topLeftRadius="6dp"
        android:topRightRadius="6dp" />

</shape>

источник: Android подход к "Оценить мое приложение"


Что такое "Конфиги" при попытке не найти.
Md Imran Choudhury

1
@ Md.ImranChoudhury Извините за поздний ответ. Конфиги - это мой частный класс, который я использую для аналитики Google. Вы можете просто удалить это утверждение без проблем!
Густаво Байокки Коста

вы должны либо сослаться на исходный ответ, либо отдать ему должное. stackoverflow.com/a/6920848/563735
Рохит Мандивал


2

Решение для Java и Kotlin (API для проверки приложений от Google в 2020 г.):

введите описание изображения здесь

Сначала в свой build.gradle(app)файл добавьте следующие зависимости (полная настройка здесь )

dependencies {
    // This dependency is downloaded from the Google’s Maven repository.
    // So, make sure you also include that repository in your project's build.gradle file.
    implementation 'com.google.android.play:core:1.8.0'
}

Добавьте этот метод в свой Activity:

void askRatings() {
    ReviewManager manager = ReviewManagerFactory.create(this);
    Task<ReviewInfo> request = manager.requestReviewFlow();
    request.addOnCompleteListener(task -> {
        if (task.isSuccessful()) {
            // We can get the ReviewInfo object
            ReviewInfo reviewInfo = task.getResult();
            Task<Void> flow = manager.launchReviewFlow(this, reviewInfo);
            flow.addOnCompleteListener(task2 -> {
                // The flow has finished. The API does not indicate whether the user
                // reviewed or not, or even whether the review dialog was shown. Thus, no
                // matter the result, we continue our app flow.
            });
        } else {
            // There was some problem, continue regardless of the result.
        }
    });
}

Назовите это как любой другой метод:

askRatings();

Код Kotlin можно найти здесь


1

Как видно из другого сообщения, на которое вы указали ссылку, у приложения нет способа узнать, оставил ли пользователь отзыв или нет. И не зря.

Подумайте об этом: если бы приложение могло определить, оставил ли пользователь отзыв или нет, разработчик мог бы ограничить определенные функции, которые будут разблокированы только в том случае, если пользователь оставит оценку 5/5. Это приведет к тому, что другие пользователи Google Play не будут доверять обзорам и подорвут систему оценок.

Альтернативные решения, которые я видел, заключаются в том, что приложение напоминает пользователю о необходимости выставлять оценку всякий раз, когда приложение открывается определенное количество раз или через заданный интервал. Например, каждый 10-й раз, когда приложение открывается, попросите пользователя оставить оценку и предоставить кнопку «уже сделано» и «напомнить мне позже». Продолжайте показывать это сообщение, если пользователь решил напомнить ему / ей позже. Некоторые другие разработчики приложений показывают это сообщение с возрастающим интервалом (например, 5, 10, 15 раз при открытии приложения), потому что, если пользователь не оставил отзыв, например, при 100-м открытии приложения, он вероятно, он / она не покинет его.

Это решение не идеально, но я думаю, что это лучшее, что у вас есть на данный момент. Это заставляет вас доверять пользователю, но вы понимаете, что альтернатива может означать потенциально худший опыт для всех на рынке приложений.


0

Котлинская версия ответа Рагхава Суда

Rater.kt

    class Rater {
      companion object {
        private const val APP_TITLE = "App Name"
        private const val APP_NAME = "com.example.name"

        private const val RATER_KEY = "rater_key"
        private const val LAUNCH_COUNTER_KEY = "launch_counter_key"
        private const val DO_NOT_SHOW_AGAIN_KEY = "do_not_show_again_key"
        private const val FIRST_LAUNCH_KEY = "first_launch_key"

        private const val DAYS_UNTIL_PROMPT: Int = 3
        private const val LAUNCHES_UNTIL_PROMPT: Int = 3

        fun start(mContext: Context) {
            val prefs: SharedPreferences = mContext.getSharedPreferences(RATER_KEY, 0)
            if (prefs.getBoolean(DO_NOT_SHOW_AGAIN_KEY, false)) {
                return
            }

            val editor: Editor = prefs.edit()

            val launchesCounter: Long = prefs.getLong(LAUNCH_COUNTER_KEY, 0) + 1;
            editor.putLong(LAUNCH_COUNTER_KEY, launchesCounter)

            var firstLaunch: Long = prefs.getLong(FIRST_LAUNCH_KEY, 0)
            if (firstLaunch == 0L) {
                firstLaunch = System.currentTimeMillis()
                editor.putLong(FIRST_LAUNCH_KEY, firstLaunch)
            }

            if (launchesCounter >= LAUNCHES_UNTIL_PROMPT) {
                if (System.currentTimeMillis() >= firstLaunch +
                    (DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000)
                ) {
                    showRateDialog(mContext, editor)
                }
            }

            editor.apply()
        }

        fun showRateDialog(mContext: Context, editor: Editor) {
            Dialog(mContext).apply {
                setTitle("Rate $APP_TITLE")

                val ll = LinearLayout(mContext)
                ll.orientation = LinearLayout.VERTICAL

                TextView(mContext).apply {
                    text =
                        "If you enjoy using $APP_TITLE, please take a moment to rate it. Thanks for your support!"

                    width = 240
                    setPadding(4, 0, 4, 10)
                    ll.addView(this)
                }

                Button(mContext).apply {
                    text = "Rate $APP_TITLE"
                    setOnClickListener {
                        mContext.startActivity(
                            Intent(
                                Intent.ACTION_VIEW,
                                Uri.parse("market://details?id=$APP_NAME")
                            )
                        );
                        dismiss()
                    }
                    ll.addView(this)
                }

                Button(mContext).apply {
                    text = "Remind me later"
                    setOnClickListener {
                        dismiss()
                    };
                    ll.addView(this)
                }

                Button(mContext).apply {
                    text = "No, thanks"
                    setOnClickListener {
                        editor.putBoolean(DO_NOT_SHOW_AGAIN_KEY, true);
                        editor.commit()
                        dismiss()
                    };
                    ll.addView(this)
                }

                setContentView(ll)
                show()
            }
        }
    }
}

Оптимизированный ответ

Rater.kt

class Rater {
    companion object {
        fun start(context: Context) {
            val prefs: SharedPreferences = context.getSharedPreferences(RATER_KEY, 0)
            if (prefs.getBoolean(DO_NOT_SHOW_AGAIN_KEY, false)) {
                return
            }

            val editor: Editor = prefs.edit()

            val launchesCounter: Long = prefs.getLong(LAUNCH_COUNTER_KEY, 0) + 1;
            editor.putLong(LAUNCH_COUNTER_KEY, launchesCounter)

            var firstLaunch: Long = prefs.getLong(FIRST_LAUNCH_KEY, 0)
            if (firstLaunch == 0L) {
                firstLaunch = System.currentTimeMillis()
                editor.putLong(FIRST_LAUNCH_KEY, firstLaunch)
            }

            if (launchesCounter >= LAUNCHES_UNTIL_PROMPT) {
                if (System.currentTimeMillis() >= firstLaunch +
                    (DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000)
                ) {
                    showRateDialog(context, editor)
                }
            }

            editor.apply()
        }

        fun showRateDialog(context: Context, editor: Editor) {
            Dialog(context).apply {
                setTitle("Rate $APP_TITLE")
                LinearLayout(context).let { layout ->
                    layout.orientation = LinearLayout.VERTICAL
                    setDescription(context, layout)
                    setPositiveAnswer(context, layout)
                    setNeutralAnswer(context, layout)
                    setNegativeAnswer(context, editor, layout)
                    setContentView(layout)
                    show()       
                }
            }
        }

        private fun setDescription(context: Context, layout: LinearLayout) {
            TextView(context).apply {
                text = context.getString(R.string.rate_description, APP_TITLE)
                width = 240
                setPadding(4, 0, 4, 10)
                layout.addView(this)
            }
        }

        private fun Dialog.setPositiveAnswer(
            context: Context,
            layout: LinearLayout
        ) {
            Button(context).apply {
                text = context.getString(R.string.rate_now)
                setOnClickListener {
                    context.startActivity(
                        Intent(
                            Intent.ACTION_VIEW,
                            Uri.parse(context.getString(R.string.market_uri, APP_NAME))
                        )
                    );
                    dismiss()
                }
                layout.addView(this)
            }
        }

        private fun Dialog.setNeutralAnswer(
            context: Context,
            layout: LinearLayout
        ) {
            Button(context).apply {
                text = context.getString(R.string.remind_later)
                setOnClickListener {
                    dismiss()
                };
                layout.addView(this)
            }
        }

        private fun Dialog.setNegativeAnswer(
            context: Context,
            editor: Editor,
            layout: LinearLayout
        ) {
            Button(context).apply {
                text = context.getString(R.string.no_thanks)
                setOnClickListener {
                    editor.putBoolean(DO_NOT_SHOW_AGAIN_KEY, true);
                    editor.commit()
                    dismiss()
                };
                layout.addView(this)
            }
        }
    }
}

Constants.kt

object Constants {

    const val APP_TITLE = "App Name"
    const val APP_NAME = "com.example.name"

    const val RATER_KEY = "rater_key"
    const val LAUNCH_COUNTER_KEY = "launch_counter_key"
    const val DO_NOT_SHOW_AGAIN_KEY = "do_not_show_again_key"
    const val FIRST_LAUNCH_KEY = "first_launch_key"

    const val DAYS_UNTIL_PROMPT: Int = 3
    const val LAUNCHES_UNTIL_PROMPT: Int = 3

}

strings.xml

<resources>
    <string name="rate_description">If you enjoy using %1$s, please take a moment to rate it. Thanks for your support!</string>
    <string name="rate_now">Rate now</string>
    <string name="no_thanks">No, thanks</string>
    <string name="remind_later">Remind me later</string>
    <string name="market_uri">market://details?id=%1$s</string>
</resources>

0

Запущена новая система Android In-App Review, которая позволяет разработчикам запрашивать отзывы в магазине Play, не выходя из приложения.

Чтобы проверить рекомендации по дизайну и когда отображать карточку обзора, обратитесь к официальному документу

https://developer.android.com/guide/playcore/in-app-review

Для реализации:

  • Добавьте библиотеку play-core в качестве зависимости в файл build.gradle.
implementation 'com.google.android.play:core:1.8.0'
  • Создайте экземпляр ReviewManager и запросите объект ReviewInfo . Объект ReviewInfo должен быть предварительно кэширован, а затем может инициировать «launchReviewFlow», чтобы представить карточку обзора пользователю.

     private var reviewInfo: ReviewInfo? = null
    
     val manager = ReviewManagerFactory.create(context)
    
     val request = manager.requestReviewFlow()
    
     requestFlow.addOnCompleteListener { request ->
         if (request.isSuccessful) {
             //Received ReviewInfo object
             reviewInfo = request.result
         } else {
             //Problem in receiving object
             reviewInfo = null
         }
    
     reviewInfo?.let {
         val flow = reviewManager.launchReviewFlow(this@MainActivity, it)
         flow.addOnCompleteListener {
             //Irrespective of the result, the app flow should continue
         }
     }

Примечание. Рекомендуется отображать процесс проверки после того, как пользователь в достаточной мере ознакомится с вашим приложением или игрой.

Когда запрашивать проверку в приложении:

  • Запускайте процесс проверки в приложении после того, как пользователь ознакомится с вашим приложением или игрой в достаточной степени, чтобы дать полезный отзыв.
  • Не запрашивайте у пользователя чрезмерный обзор. Такой подход помогает минимизировать разочарование пользователей и ограничить использование API (см. Раздел о квотах).
  • Ваше приложение не должно задавать пользователю какие-либо вопросы до или во время представления кнопки или карточки оценки, включая вопросы об их мнении (например, «Нравится ли вам приложение?») Или прогнозные вопросы (например, «Вы бы оценили это приложение на 5 звезд »).

Несколько моментов перед тестированием:

  • При тестировании новых функций в основном мы создаем новый проект, который будет иметь новый ApplicationId. Убедитесь, что вы указали ApplicationId, который уже выпущен и доступен в магазине воспроизведения.

  • Если в прошлом вы давали отзыв о своем приложении, launchReviewFlow API проверки в приложении не представит никаких карточек обзора . Он просто запускает событие успеха.

  • Из-за ограничений квоты вызов метода launchReviewFlow не всегда может отображать диалоговое окно. Он не должен быть связан с каким-либо событием щелчка.


0

Убедитесь, что реализовано следующее. Для обзоров в приложении:

implementation 'com.google.android.play:core:1.8.0'

OnCreate

public void RateApp(Context mContext) {
    try {
        ReviewManager manager = ReviewManagerFactory.create(mContext);
        manager.requestReviewFlow().addOnCompleteListener(new OnCompleteListener<ReviewInfo>() {
            @Override
            public void onComplete(@NonNull Task<ReviewInfo> task) {
                if(task.isSuccessful()){
                    ReviewInfo reviewInfo = task.getResult();
                    manager.launchReviewFlow((Activity) mContext, reviewInfo).addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(Exception e) {
                            Toast.makeText(mContext, "Rating Failed", Toast.LENGTH_SHORT).show();
                        }
                    }).addOnCompleteListener(new OnCompleteListener<Void>() {
                        @Override
                        public void onComplete(@NonNull Task<Void> task) {
                            Toast.makeText(mContext, "Review Completed, Thank You!", Toast.LENGTH_SHORT).show();
                        }
                    });
                }

            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(Exception e) {
                Toast.makeText(mContext, "In-App Request Failed", Toast.LENGTH_SHORT).show();
            }
        });
    } catch (ActivityNotFoundException e) {
        e.printStackTrace();
    }
}

0

Все эти библиотеки не являются решением проблемы в этом посте. Эти библиотеки просто открывают веб-страницу приложения в Google Play. Вместо этого эта основная библиотека Play имеет более последовательный интерфейс.

Итак, я думаю, что это проблема, ProGuard: он достаточно затемняет некоторые классы https://stackoverflow.com/a/63650212/10117882


0

По состоянию на август 2020 года доступен API обзора приложений Google Play, и его прямая реализация верна в соответствии с этим ответом .

Но если вы хотите добавить к нему некоторую логику отображения, используйте библиотеку Five-Star-Me .

Задайте время запуска и дни установки в onCreateметоде MainActivity для настройки библиотеки.

  FiveStarMe.with(this)
      .setInstallDays(0) // default 10, 0 means install day.
      .setLaunchTimes(3) // default 10
      .setDebug(false) // default false
      .monitor();

Затем поместите приведенный ниже вызов метода в любой метод onCreate / onViewCreated действия / фрагмента, чтобы отображать подсказку всякий раз, когда выполняются условия.

FiveStarMe.showRateDialogIfMeetsConditions(this); //Where *this* is the current activity.

введите описание изображения здесь

Инструкция по установке:

Вы можете скачать из jitpack.

Шаг 1: Добавьте это в проект (root) build.gradle.

allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}

Шаг 2. Добавьте следующую зависимость на уровень вашего модуля (приложения) build.gradle.

dependencies {
  implementation 'com.github.numerative:Five-Star-Me:2.0.0'
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.