📜 ⬆️ ⬇️

Alternative to paid off ads in the free Android application

Good day, Habrahabr!

My name is Alexander, I am a developer under the Android OS. Today I want to share with you the experience of implementing an alternative to a paid method of disabling advertising in an application - disabling advertising for viewing ads (AdMob Rewarded Video Ads). Interesting? Then welcome under cat.


How was everything?


Back in 2013, I decided to start developing applications for Android, started reading thematic books, articles, watched video lessons, etc. Wrote the first underapplication and depressed, because I wanted to do something useful, necessary for society, but there were no ideas. In 2014, a friend of mine asked me to develop for him a mobile reference book on the syntax of the Arduino platform (C language there). With great desire, I took this project and implemented the first version for Android 3.0+. After a while, it was decided to improve it, and so the second version appeared (for Android 4.0+). Both are free with an AdMob banner at the bottom and a paid shutdown. Everything was good, until I began to write that ~ 150-170 rubles of the Russian Federation is expensive to turn off advertising forever in their favorite directory. To which I answered with a "barter" solution of the question - the user can turn off the banner at the bottom for a while for viewing the video ads from AdMob.
[back to content]

Implementation, Part 1: How it works (in words)


When the application is launched, the user will be shown Dialog, with a suggestion to turn off advertising, unless, of course, he has previously disconnected it or if there is no connection to the Internet. In the case of a positive response, the application shows a fragment with buttons with which you can disable advertising in the application in a convenient way.
[back to content]
')

Implementation, Part 2: Appearance


Dialogue with the proposal to disable advertising
Dialogue with the proposal to disable advertising

Screen off advertising
Screen off advertising

1 video advertisement viewed
1 video advertisement viewed

5 video ads viewed
5 video ads viewed

Ads disabled for 1 hour
Ads disabled for 1 hour

Advertising is disabled for 1 day
Advertising is disabled for 1 day

[back to content]

Implementation, part 3: Principle of operation (java code)


Main Activity Code

public class ActivityMain extends AppCompatActivity { private boolean internet; private boolean isAdsDisabled; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // get SharedPreferences prefManager = new PreferencesManager(this); isAdsDisabled = prefManager.isAdsDisabled(); // true - disable | false - enabled // ...     UI internet = CheckURLConnection.isNetworkAvailable(); // true - disable | false - enabled if (internet && !isAdsDisabled && isTimeUp()) { DialogFragment disableAds = new DisableAdsDialog(); if (!disableAds.isResumed()) { disableAds.show(getSupportFragmentManager(), ConstantHolder.DIALOG_DISABLE_ADS); } } private boolean isTimeUp() { return System.currentTimeMillis() > prefManager.getEstimatedAdsTime(); } } 


Class Code CheckURLConnection
 public class CheckURLConnection { public static boolean isNetworkAvailable() { ConnectivityManager connectivityManager = (ConnectivityManager) MyAppClass.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivityManager != null) { NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } else { return false; } } } 


PreferencesManager class code

 public class PreferencesManager { private Context mContext; private static SharedPreferences mSPref; private SharedPreferences.Editor mSPEditor; public PreferencesManager(Context context) { this.mContext = context; mSPref = mContext.getSharedPreferences(ConstantHolder.APP_PREF, Context.MODE_PRIVATE); } //      SharedPreferences public boolean isAdsDisabled() { return mSPref.getBoolean(ConstantHolder.APP_PREF_DISABLE_ADS, false); } //    ,     public long getEstimatedAdsTime() { return mSPref.getLong(ConstantHolder.APP_DISABLE_ADS_PERIOD, 0); } } 

The ConstantHolder class is a class in which I store constants in order not to import them from everywhere, but only from one place to take (analogue of class R)

 public class ConstantHolder { //Preferences Constants public static final String APP_PREF = "app_pref"; public static final String APP_PREF_DISABLE_ADS = "disableAds"; //  public static final String APP_DISABLE_ADS_PERIOD = "disableAdsPeriod"; //    } 

And the most interesting is the class-fragment of the advertisement

java whole class code
 public class SettingsAdsFrag extends Fragment implements View.OnClickListener { private static final String VIEWED_ZERO_VIDEO_ADS = "0"; private static final int VIEWED_ADS_NUMBER_PER_HOUR = 1; private static final int VIEWED_ADS_NUMBER_PER_DAY = 5; //5 private static final long DISABLE_ADS_PERIOD_1_HOUR = 60 * 60 * 1000; private static final long DISABLE_ADS_PERIOD_24_HOURS = 24 * 60 * 60 * 1000; private Context mContext; private PreferencesManager prefManager; private RewardedVideoAd mRewardedVideoAd; private AdRequest mAdRequest; private boolean internet; private boolean readyToPurchase; private boolean bDisableAds; private String ready; private String notReady; private static int adsViewedCounter = 0; private Button btnReadyToViewing; private Button btnDisableAdsPerHour; private Button btnDisableAdsPerDay; private TextView tvViewedAds; private TextView tvEstimatedDate; private ToggleButton tbDisableAds; private BillingProcessor bp; @Override public void onAttach(Context context) { super.onAttach(context); this.mContext = context; //       prefManager = new PreferencesManager(context); //      internet = CheckURLConnection.isNetworkAvailable(); //  " "  readyToPurchase = false; //      " "  ""  .  ,         ready = context.getString(R.string.txt_cat_ads_ready_for_viewing); notReady = context.getString(R.string.txt_cat_ads_not_ready_for_viewing); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //    Toolbar setHasOptionsMenu(false); //       Anjlab - In-App-Billing-v3 bp = new BillingProcessor(getActivity(), InAppBillingResources.getRsaKey(), //  RSA  InAppBillingResources.getMerchantId(), //  ID   Google Play Developer Console bpHandler //    ); //       bDisableAds = prefManager.isAdsDisabled(); // true - disable | false - enabled } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //      Toolbar,  , ..     // [START AdMob Rewarded Video Ads - ] mRewardedVideoAd = MobileAds.getRewardedVideoAdInstance(getActivity()); mRewardedVideoAd.setRewardedVideoAdListener(rewardedVideoAdListener); //     ,   Google     AdMob ( 1      !) if (BuildConfig.DEBUG) { mAdRequest = new AdRequest.Builder() .addTestDevice(DeviceHash.getHtcDeviceHash()) .build(); } else { mAdRequest = new AdRequest.Builder() .build(); } //    loadRewardedVideoAd(); // [END AdMob Rewarded Video Ads] //  View   -   View settAdsView = inflater.inflate(R.layout.frag_sett_ads_screen, container, false); // [START ToggleButton Disable Ads] tbDisableAds = (ToggleButton) settAdsView.findViewById(R.id.tb_disable_ads); //       ,     SharedPrefereces   ""  - // --,       Google   .        ? if (readyToPurchase) { if (bp.isPurchased(InAppBillingResources.getSKU_DisableAds())) { setAdsDisable(); tbDisableAds.setChecked(false); } } else { //     ,    // true - disable | false - enabled tbDisableAds.setChecked(!bDisableAds); } //      tbDisableAds.setOnClickListener(this); // [END ToggleButton Disable Ads] //            .   // [START TextView Rewarded Video Ads Disabling Guide] TextView tvRewardedGuide = (TextView) settAdsView.findViewById(R.id.tv_rewarded_video_disabling_guide); tvRewardedGuide.setText(String.format(getActivity().getString(R.string.txt_cat_ads_disable_tmp_text), VIEWED_ADS_NUMBER_PER_HOUR, VIEWED_ADS_NUMBER_PER_DAY)); // [END TextView Rewarded Video Ads Disabling Guide] // [START TextView Viewed Ads] tvViewedAds = (TextView) settAdsView.findViewById(R.id.tv_viewed_ads); // [END TextView Viewed Ads] // [START Button Ready for Viewing] btnReadyToViewing = (Button) settAdsView.findViewById(R.id.btn_ready_to_viewing); btnReadyToViewing.setText(notReady); btnReadyToViewing.setEnabled(false); btnReadyToViewing.setOnClickListener(this); // [END Button Ready for Viewing] // [START Button Disable Ads Per Hour] btnDisableAdsPerHour = (Button) settAdsView.findViewById(R.id.btn_disable_ads_per_hour); btnDisableAdsPerHour.setEnabled(false); btnDisableAdsPerHour.setOnClickListener(this); // [END Button Disable Ads Per Hour] // [START Button Disable Ads Per Day] btnDisableAdsPerDay = (Button) settAdsView.findViewById(R.id.btn_disable_ads_per_day); btnDisableAdsPerDay.setEnabled(false); btnDisableAdsPerDay.setOnClickListener(this); // [END Button Disable Ads Per Day] // [START TextView Last Viewing Time] tvEstimatedDate = (TextView) settAdsView.findViewById(R.id.tv_estimated_date); // [END TextView Last Viewing Time] //        updateTextView(); updateButtons(); return settAdsView; } @Override public void onResume() { // Rewarded Video Ad -        if (mRewardedVideoAd != null) { mRewardedVideoAd.resume(getActivity()); } super.onResume(); // updateUI -   updateTextView(); updateButtons(); } @Override public void onPause() { // Rewarded Video Ad -        if (mRewardedVideoAd != null) { mRewardedVideoAd.pause(getActivity()); } super.onPause(); } @Override public void onDestroy() { // Rewarded Video Ad -        if (mRewardedVideoAd != null) { mRewardedVideoAd.destroy(getActivity()); } super.onDestroy(); } //     @Override public void onClick(View v) { switch (v.getId()) { case R.id.tb_disable_ads: // disable ads ? setText(..OFF) : setText(..ON) // if state ON (disableAds - false) // true - ads disabled; false - ads enabled if (!bDisableAds && readyToPurchase) { //       ,   "   " bp.purchase(getActivity(), InAppBillingResources.getSKU_DisableAds()); } break; case R.id.btn_ready_to_viewing: //    ,     if (mRewardedVideoAd.isLoaded()) { mRewardedVideoAd.show(); } break; case R.id.btn_disable_ads_per_hour: //   1  disableAdsPerPeriod(DISABLE_ADS_PERIOD_1_HOUR); adsViewedCounter--; updateTextView(); updateButtons(); break; case R.id.btn_disable_ads_per_day: //   1  disableAdsPerPeriod(DISABLE_ADS_PERIOD_24_HOURS); clearAdsCounter(); updateTextView(); updateButtons(); break; default: break; } // true - ads disabled; // false - ads enabled if (bDisableAds) { tbDisableAds.setChecked(false); showSnackbar(); } } // ========================================================== // [START REWARDEDVIDEOAD] private RewardedVideoAdListener rewardedVideoAdListener = new RewardedVideoAdListener() { @Override public void onRewardedVideoAdLoaded() { //      ,    btnReadyToViewing.setText(ready); btnReadyToViewing.setEnabled(true); } @Override public void onRewardedVideoAdOpened() { } @Override public void onRewardedVideoStarted() { //         btnReadyToViewing.setText(notReady); btnReadyToViewing.setEnabled(false); } @Override public void onRewardedVideoAdClosed() { //     loadRewardedVideoAd(); } @Override public void onRewarded(RewardItem rewardItem) { //          ,    if (adsViewedCounter < VIEWED_ADS_NUMBER_PER_DAY) { adsViewedCounter++; } //    updateTextView(); updateButtons(); } @Override public void onRewardedVideoAdLeftApplication() { } @Override public void onRewardedVideoAdFailedToLoad(int i) { //    loadRewardedVideoAd(); } }; private void loadRewardedVideoAd() { //      ,    if (internet) { mRewardedVideoAd.loadAd(mContext.getString(R.string.admob_rewarded_video_id), mAdRequest); } } // [END REWARDEDVIDEOAD] // ========================================================== //          //      ,     private void updateTextView() { // true - disable | false - enabled if (bDisableAds) { tvViewedAds.setText(String.valueOf(adsViewedCounter)); tvEstimatedDate.setText(""); } else { // [START UPDATETEXTVIEW : tvViewedAds] if (adsViewedCounter > 0 && adsViewedCounter <= VIEWED_ADS_NUMBER_PER_DAY) { String strViewedAdsCount = adsViewedCounter + " / " + VIEWED_ADS_NUMBER_PER_DAY; tvViewedAds.setText(strViewedAdsCount); } else { tvViewedAds.setText(VIEWED_ZERO_VIDEO_ADS); } // [END UPDATETEXTVIEW : tvViewedAds] // [START UPDATETEXTVIEW : tvEstimatedDate] long estimatedDate = prefManager.getEstimatedAdsTime(); long currentDate = System.currentTimeMillis(); if (estimatedDate != 0 && estimatedDate > currentDate) { String strEstimatedDate = convertTime(estimatedDate); String strEstimatedDateFinal = "<b>" + mContext.getString(R.string.txt_tv_header_estimated_time).toUpperCase() + "</b>" + "<br>" + strEstimatedDate; tvEstimatedDate.setText(Html.fromHtml(strEstimatedDateFinal)); } // [END UPDATETEXTVIEW : tvEstimatedDate] } } //   private void updateButtons() { // 0 if (adsViewedCounter == 0) { btnDisableAdsPerHour.setEnabled(false); btnDisableAdsPerDay.setEnabled(false); } // 1 - 4 if (adsViewedCounter > 0 && adsViewedCounter < VIEWED_ADS_NUMBER_PER_DAY) { btnDisableAdsPerHour.setEnabled(true); } // 5 if (adsViewedCounter == VIEWED_ADS_NUMBER_PER_DAY) { btnDisableAdsPerHour.setEnabled(true); btnDisableAdsPerDay.setEnabled(true); } } //      private void disableAdsPerPeriod(long disablePeriod) { //     long currentDate = System.currentTimeMillis(); //      long estimatedDate = currentDate + disablePeriod; //      prefManager.setEstimatedDate(estimatedDate); //     AdMobAds.disableBanner(getActivity(), true); } //   private void clearAdsCounter() { adsViewedCounter = 0; } //         public String convertTime(long time) { Date date = new Date(time); Format format = new SimpleDateFormat("dd MMM yyyy @ HH:mm:ss"); return format.format(date); } // ========================================================== // [START IN APP BILLING] BillingProcessor.IBillingHandler bpHandler = new BillingProcessor.IBillingHandler() { @Override public void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details) { // Called when requested PRODUCT ID was successfully purchased // ,   PRODUCT ID    if (bp.isPurchased(productId)) { //     ""   ""  - setAdsDisable(); tbDisableAds.setChecked(false); //   restartDialog(); } else { //   "" tbDisableAds.setChecked(true); } } @Override public void onPurchaseHistoryRestored() { //,     , //          Google Play } @Override public void onBillingError(int errorCode, @Nullable Throwable error) { // ,   . .   //      } @Override public void onBillingInitialized() { // ,  bp       readyToPurchase = true; } }; // [START IN APP BILLING] // ========================================================== //      private void setAdsDisable() { prefManager.setAdsDisabled(); } //    // [START restartDialog] private void restartDialog() { AlertDialog.Builder builder; View alertLayout = View.inflate(mContext, R.layout.dialog_restart, null); if (prefManager.getAppTheme() == 0) { builder = new AlertDialog.Builder(getActivity(), R.style.AppThemeDialogStyleLight); } else { builder = new AlertDialog.Builder(getActivity(), R.style.AppThemeDialogStyleDark); } builder.setTitle(getActivity().getString(R.string.msg_notification_Title)); builder.setView(alertLayout); builder.setPositiveButton(getActivity().getString(R.string.ans_restart), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { restartApp(); } }); builder.show(); } // [END restartDialog] //    // [START restartApp] private void restartApp() { Intent i = getActivity().getPackageManager().getLaunchIntentForPackage(getActivity().getPackageName()); if (i != null) { i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); getActivity().startActivity(i); } } // [END restartApp] // Snackbar  ,    .         private void showSnackbar() { Snackbar.make(getActivity().getWindow().getDecorView().getRootView(), getActivity().getResources().getString(R.string.advertising_is_already_disabled), Snackbar.LENGTH_SHORT).show(); } } 


Class work with the banner AdMob. Nothing complicated, publish for review. Although I’ve posted it on StackOverFlow too

 public class AdMobAds { public static void disableBanner(final Activity activity, boolean disableAds) { final View adsContainer = activity.findViewById(R.id.container); final AdView adView = (AdView) activity.findViewById(R.id.adView); if (disableAds) { adView.setVisibility(View.GONE); adsContainer.setPadding(0, 0, 0, 0); } else { AdRequest adRequest; if (BuildConfig.DEBUG) { adRequest = new AdRequest.Builder() .addTestDevice(DeviceHash.getHtcDeviceHash()) .build(); } else { adRequest = new AdRequest.Builder() .build(); } adView.loadAd(adRequest); adView.setAdListener(new AdListener() { @Override public void onAdFailedToLoad(int errorCode) { MyAppLogs.show("[bottom-banner] >> onAdFailedToLoad:   \terrorCode = " + errorCode + "."); super.onAdFailedToLoad(errorCode); } @Override public void onAdLoaded() { super.onAdLoaded(); MyAppLogs.show("[bottom-banner] >> onAdLoaded"); if (adView.getVisibility() == View.GONE) { adView.setVisibility(View.VISIBLE); } View adsContainer = activity.findViewById(R.id.container); adsContainer.setPadding(adsContainer.getPaddingLeft(), adsContainer.getPaddingTop(), adsContainer.getPaddingRight(), adView.getHeight() + 8); } }); } } } 

[back to content]

Conclusion


That's all. The essence of the article is to share with the community its idea and its implementation. And also get an invitation to Habrahabr, if someone likes what I shared. I will be glad and grateful for your opinion in questions of finalizing the code and / or ideas. If you need additional clarification - write, I will in the shortest possible time make corrections to the article or give an answer in the comments.

For obvious reasons, the link to the application is not published in the public domain. Ethics is ethics! Who needs - I will give in HP.

AdMob statistics is still raw, only 2 weeks have passed since the introduction of this alternative. Not all users updated. But for sure there are those who use this way to turn off the banner below.

UPDATE: Link to the article with the results TOTAL 3 months: Alternative to a paid outage of advertising in the free Android application

I thank everyone who read the article to the end!

Source: https://habr.com/ru/post/349832/


All Articles