Jak zaimplementować funkcję Rate It w aplikacji na Androida


95

Rozwijam aplikację na Androida. W którym wszystko działa dobrze. Moja aplikacja jest gotowa do uruchomienia. Ale muszę zaimplementować jeszcze jedną funkcję. Muszę wyświetlić wyskakujące okienko, które zawiera

Rate It i Remind me later

Tutaj, jeśli jakikolwiek użytkownik oceni aplikację na rynku, wyskakujące okienko nie zniknie. Szukałem w Google i znalazłem jeden link . Dzięki temu rozumiem, że nie można tego wiedzieć. Potrzebuję więc sugestii.

Czy ktoś spotkał się wcześniej z tą sytuacją? Jeśli tak, czy jest na to jakieś rozwiązanie lub alternatywa?


Czy pytasz tylko o ocenę / przypomnienie mi później, czy też pytasz, jak dowiedzieć się, czy określony użytkownik ocenił aplikację na Androida?
wtsang02

1
wdrożyłem wyskakujące okienko. ale jak sprawdzić, czy użytkownik ocenia aplikację, czy nie
Naveen

-1 Nie widzę różnicy między tym pytaniem a tym w linku.
wtsang02

2
@ wtsang02, Może to prawda. Ale spójrz na pytanie. jego zapytał Mar 15 2011. więc prawie 20 miesięcy minęło. Myślę, że ktoś ma rozwiązanie lub alternatywę dla moich wymagań. to jest yi opublikowane tutaj.
Naveen

Możesz użyć biblioteki github.com/Vorlonsoft/AndroidRate ( implementation 'com.vorlonsoft:androidrate:1.0.3')
Alexander Savin,

Odpowiedzi:


182

Do pewnego stopnia wdrożyłem to jakiś czas temu. Niemożliwe jest ustalenie, czy użytkownik ocenił aplikację, aby zapobiec sytuacji, w której oceny staną się walutą (niektórzy programiści mogą dodać opcję „Oceń tę aplikację i uzyskaj to a tak w aplikacji za darmo”).

Klasa, którą napisałem, zawiera trzy przyciski i konfiguruje okno dialogowe tak, aby było wyświetlane dopiero po uruchomieniu aplikacji n(użytkownicy mają większą szansę na ocenę aplikacji, jeśli używali jej trochę wcześniej. Większość z nich jest mało prawdopodobna) nawet wiedzieć, co robi przy pierwszym uruchomieniu):

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();        
    }
}

Integracja klasy jest tak prosta, jak dodanie:

AppRater.app_launched(this);

Do Twojej aktywności. Wystarczy dodać je do jednego działania w całej aplikacji.


1
To nie obsługuje wielu użytkowników korzystających z tego samego urządzenia.
AsafK

1
@AsafK Tak, ale wielu użytkowników korzystających z tego samego urządzenia można obsłużyć, wyświetlając appraterokno dialogowe tylko po uwierzytelnieniu i zmianie w shared preferencecelu uwzględnienia adresu e-mail google w pliku key.
stephen

1
Cześć, mam tylko jedno pytanie. Dlaczego wszystko stało się statyczne? Dzięki Raghav!
Ruchir Baronia

2
Cześć, próbuję twojego powyższego kodu. Umieściłem AppRater.app_launched(this);w mojej onCreate()MainActivity. Zmieniłem również minimalną wymaganą liczbę uruchomień na 2. Ale nie widzę okna dialogowego po 2 uruchomieniach aplikacji. Czy możesz mi pomóc? Dzięki!
Wyjątek

1
Lepiej użyj wyliczenia Context.MODE_PRIVATE-context.getSharedPreferences("apprater", Context.MODE_PRIVATE);
Vivek

18

Mój przy użyciu 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();
    }
}

Następnie użyj go w onCreate()swojej głównej FragmentActivity:

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

    RateItDialogFragment.show(this, getFragmentManager());

}

Dobry! Po prostu umieściłbym edytor.commit () przed wyświetleniem DialogFragment, na wypadek gdyby coś poszło nie tak podczas ładowania okna dialogowego.
narko

@narko Thanks. Zaktualizowano.
mixel

Uwaga: użycie polecenia do zapisania preferencji współdzielonych może prowadzić do wycieku pamięci. Jeśli zauważysz uważnie w setPositiveButtoni setNegativeButton, zapisuje się do wspólnych preferencji przy użyciu zatwierdzenia, ale jeśli używasz zastosuj, który jest asynchroniczny i zachowa odniesienie do działania do czasu jego ukończenia, a zaraz po tym wywoła odrzucenie. Dismiss spróbuje zniszczyć fragment, ale nie może, ponieważ działanie jest utrzymywane / używane przez proces stosowania wspólnych preferencji (nosiłem to, ponieważ AndroidStudio poprosi użytkownika o zmianę zatwierdzenia do zastosowania, nie rób tego, chyba że ty użyj innej logiki)
Sai

@mixel Jak zmodyfikować kod, aby móc go używać w działaniu i bez fragmentów?
user1090751

7

Myślę, że to, co próbujesz zrobić, prawdopodobnie przynosi efekt przeciwny do zamierzonego.

Ułatwienie ludziom oceniania aplikacji jest ogólnie dobrym pomysłem, ponieważ większość ludzi, którzy się tym przejmują, bo im się podoba. Krążą pogłoski, że liczba ocen wpływa na ocenę rynkową (chociaż widzę niewiele dowodów na to). Namawianie użytkowników do oceniania - za pomocą ekranów nag - może spowodować, że ludzie będą usuwać nagę przez wystawienie złej oceny.

Dodanie możliwości bezpośredniego oceniania aplikacji spowodowało niewielki spadek liczbowych ocen mojej bezpłatnej wersji i niewielki wzrost płatnej aplikacji. W przypadku bezpłatnej aplikacji moje 4 gwiazdki wzrosły bardziej niż moje 5 gwiazdek, ponieważ ludzie, którzy uważali moją aplikację za dobrą, ale nie świetną, również ją oceniali. Zmiana wyniosła około -0,2. W przypadku opłaconych reszta wynosiła około +0,1. Powinienem usunąć go z darmowej wersji, z wyjątkiem tego, że lubię otrzymywać wiele komentarzy.

Umieściłem przycisk oceny na ekranie ustawień (preferencji), gdzie nie wpływa to na normalne działanie. Wciąż podniosło to mój wskaźnik oceny o współczynnik 4 lub 5. Nie mam wątpliwości, że gdybym próbował nakłonić użytkowników do wystawienia oceny, wielu użytkowników wystawiało mi złe oceny w proteście.


100% prawda. To samo stało się z moją bezpłatną aplikacją.
akash varlani

6

AndroidRate to biblioteka, która pomaga promować aplikację na Androida, zachęcając użytkowników do oceny aplikacji po kilku dniach jej używania.

Moduł 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);
  }
}

Domyślne warunki wyświetlania okna dialogowego stawki są następujące:

  1. Aplikacja jest uruchamiana ponad 10 dni później niż instalacja. Zmień przez AppRate#setInstallDays(byte).
  2. Aplikacja jest uruchamiana ponad 10 razy. Zmień przez AppRate#setLaunchTimes(byte).
  3. Aplikacja jest uruchamiana ponad 1 dzień po kliknięciu neutralnego przycisku. Zmień przez AppRate#setRemindInterval(byte).
  4. Aplikacja jest uruchamiana X razy i X% 1 = 0. Zmień za pomocą AppRate#setRemindLaunchTimes(byte).
  5. Aplikacja domyślnie wyświetla neutralne okno dialogowe (Przypomnij mi później). Zmień przez setShowLaterButton(boolean).
  6. Określanie wywołania zwrotnego po naciśnięciu przycisku. Ta sama wartość co drugi argument DialogInterface.OnClickListener#onClickzostanie przekazana w argumencie funkcji onClickButton.
  7. Ustawienie AppRate#setDebug(boolean)zapewni, że żądanie oceny będzie wyświetlane przy każdym uruchomieniu aplikacji. Ta funkcja jest przeznaczona tylko do programowania! .

Opcjonalne wymagania dotyczące zdarzeń niestandardowych do wyświetlania okna dialogowego

Możesz dodać dodatkowe opcjonalne wymagania dotyczące wyświetlania okna dialogowego. Każde wymaganie można dodać / przywołać jako niepowtarzalny ciąg. Możesz ustawić minimalną liczbę dla każdego takiego zdarzenia (np. „Action_performed” 3 razy, „button_clicked” 5 razy itd.)

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

Wyczyść flagę pokazującą okno dialogowe

Jeśli chcesz ponownie wyświetlić okno dialogowe, zadzwoń AppRate#clearAgreeShowDialog().

AppRate.with(this).clearAgreeShowDialog();

Po naciśnięciu przycisku

zadzwoń AppRate#showRateDialog(Activity).

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

Ustaw widok niestandardowy

zadzwoń 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();

Konkretny temat

Możesz użyć określonego motywu, aby rozwinąć okno dialogowe.

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

Niestandardowe okno dialogowe

Jeśli chcesz użyć własnych etykiet okien dialogowych, zastąp zasoby ciągów xml w aplikacji.

<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>

Sprawdź, czy Google Play jest dostępny

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

}


3

To rozwiązanie jest bardzo podobne do przedstawionych powyżej. Jedyna różnica polega na tym, że będziesz mógł opóźnić wyświetlenie monitu w oknie dialogowym oceny o uruchomienie i dni. Jeśli przycisk przypomnij mi później zostanie naciśnięty, opóźnię wyskakujące okienko o 3 dni i 10 uruchomień. To samo dotyczy tych, którzy wybrali ocenę, ale opóźnienia są dłuższe (aby nie przeszkadzać użytkownikowi tak szybko, gdyby faktycznie ocenił aplikację. Można to zmienić, aby nie była ponownie wyświetlana, wtedy będziesz musiał zmień kod na swój własny). Mam nadzieję, że to komuś pomoże!

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();
    }
}

Przyciski mają określony kolor i tło. Tło jest takie, jak pokazano w tym pliku 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>

źródło: metoda „Oceń moją aplikację” w systemie Android


Co to jest „konfiguracja”, nie można jej znaleźć, gdy próbuję.
Md Imran Choudhury

1
@ Md.ImranChoudhury Przepraszamy za spóźnioną odpowiedź. Konfiguracja to moja prywatna klasa, której używam do Google Analytics. Możesz po prostu usunąć to stwierdzenie bez problemu!
Gustavo Baiocchi Costa

powinieneś albo zamieścić link do oryginalnej odpowiedzi, albo podać mu kredyt. stackoverflow.com/a/6920848/563735
Rohit Mandiwal


1

Jak widać z innego wpisu, do którego masz link, aplikacja nie może dowiedzieć się, czy użytkownik zostawił recenzję, czy nie. I nie bez powodu.

Pomyśl o tym, jeśli aplikacja może stwierdzić, czy użytkownik zostawił recenzję, czy nie, programista może ograniczyć niektóre funkcje, które zostaną odblokowane tylko wtedy, gdy użytkownik wystawi ocenę 5/5. To spowodowałoby, że inni użytkownicy Google Play nie ufaliby recenzjom i podważyłoby system ocen.

Alternatywne rozwiązania, które widziałem, polegają na tym, że aplikacja przypomina użytkownikowi o przesłaniu oceny za każdym razem, gdy aplikacja jest otwierana określoną liczbę razy lub w określonym interwale. Na przykład przy każdym dziesiątym otwarciu aplikacji poproś użytkownika o wystawienie oceny i podanie przycisku „już zrobione” i „przypomnij mi później”. Kontynuuj wyświetlanie tej wiadomości, jeśli użytkownik wybrał przypomnienie mu / jej później. Niektórzy inni programiści aplikacji wyświetlają ten komunikat z rosnącym interwałem (np. 5, 10, 15 godzin po otwarciu aplikacji), ponieważ jeśli użytkownik nie zostawił recenzji, na przykład setnego otwarcia aplikacji, jest to prawdopodobnie prawdopodobnie nie opuści żadnego.

To rozwiązanie nie jest idealne, ale myślę, że jest najlepsze, jakie masz na razie. Prowadzi to do zaufania użytkownika, ale zdaj sobie sprawę, że alternatywa oznaczałaby potencjalnie gorsze wrażenia dla wszystkich na rynku aplikacji.


1

Rozwiązanie Java i Kotlin (interfejs API przeglądu w aplikacji od Google w 2020 r.):

wprowadź opis obrazu tutaj

Najpierw w swoim build.gradle(app)pliku dodaj następujące zależności (pełna konfiguracja tutaj )

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'
}

Dodaj tę metodę do 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.
        }
    });
}

Nazwij to jak każdą inną metodę:

askRatings();

Kod Kotlin można znaleźć tutaj


0

Wersja Kotlin odpowiedzi Raghava Sooda

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()
            }
        }
    }
}

Zoptymalizowana odpowiedź

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

Uruchomiono nowy system recenzji w aplikacji na Androida, który pozwala programistom prosić o recenzje w Sklepie Play bez opuszczania aplikacji.

Aby sprawdzić wytyczne projektowe i kiedy wyświetlić kartę przeglądu, zapoznaj się z oficjalnym dokumentem

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

Aby wdrożyć:

  • Dodaj bibliotekę Play-Core jako zależność w pliku build.gradle.
implementation 'com.google.android.play:core:1.8.0'
  • Utwórz instancję ReviewManager i zażądaj obiektu ReviewInfo . Obiekt ReviewInfo, który ma zostać wstępnie zapisany w pamięci podręcznej, a następnie może wyzwolić „launchReviewFlow”, aby przedstawić użytkownikowi kartę recenzji.

     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
         }
     }

Uwaga: sugerowane jest pokazanie przepływu recenzji po tym, jak użytkownik doświadczył wystarczająco dużo Twojej aplikacji lub gry.

Kiedy poprosić o sprawdzenie w aplikacji:

  • Uruchom przepływ recenzji w aplikacji, gdy użytkownik doświadczy wystarczająco dużo Twojej aplikacji lub gry, aby przekazać przydatne opinie.
  • Nie zachęcaj nadmiernie użytkownika do recenzji. Takie podejście pomaga zminimalizować frustrację użytkowników i ograniczyć użycie interfejsu API (zobacz sekcję dotyczącą przydziałów).
  • Twoja aplikacja nie powinna zadawać użytkownikowi żadnych pytań przed lub podczas prezentowania przycisku lub karty oceny, w tym pytań o jego opinię (np. „Czy podoba Ci się ta aplikacja?”) Lub pytań predykcyjnych (np. „Czy oceniasz tę aplikację na 5 gwiazdek?”) ”).

Kilka punktów przed przetestowaniem tego:

  • Podczas testowania nowych funkcjonalności, najczęściej tworzymy nowy projekt, który będzie miał nowy ApplicationId, upewnij się, że podajesz ApplicationId, który jest już wydany i dostępny w sklepie Play.

  • Jeśli w przeszłości przekazałeś opinię na temat swojej aplikacji, funkcja launchReviewFlow interfejsu API oceny w aplikacji nie wyświetli żadnej karty recenzji . Po prostu wywołuje zdarzenie sukcesu.

  • Ze względu na ograniczenia przydziału wywołanie metody launchReviewFlow może nie zawsze powodować wyświetlenie okna dialogowego. Nie powinno być powiązane z żadnym zdarzeniem kliknięcia.


0

Upewnij się, że zaimplementowano poniższe elementy W przypadku recenzji w aplikacji:

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

Wszystkie te biblioteki nie są rozwiązaniem problemu opisanego w tym poście. Te biblioteki po prostu otwierają stronę internetową z aplikacją w Google Play. Zamiast tego ta podstawowa biblioteka Play ma bardziej spójny interfejs.

Więc myślę, że to jest problem, ProGuard: obfscats wystarcza na niektóre klasy https://stackoverflow.com/a/63650212/10117882

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.