Bug 1246238 - Pre: Implement SimpleHelperUI r?sebastian
This provides a basic helper UI that can be customised with images/text.
We need a very similar helper for both reader-view offline bookmarking related
helpers (
Bug 1236328 and
Bug 1247689), hence it's useful to have a common
class implementing most of the required functionality.
Most of the new helper is borrowed from the existing HomeScreenPrompt. I will
extract the common functionality in a followup Bug.
MozReview-Commit-ID: Byc5VnVFffj
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -154,16 +154,20 @@
</activity-alias>
<service android:name="org.mozilla.gecko.GeckoService" />
<activity android:name="org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt"
android:launchMode="singleTop"
android:theme="@style/OverlayActivity" />
+ <activity android:name="org.mozilla.gecko.promotion.SimpleHelperUI"
+ android:launchMode="singleTop"
+ android:theme="@style/OverlayActivity" />
+
<activity android:name="org.mozilla.gecko.promotion.HomeScreenPrompt"
android:launchMode="singleTop"
android:theme="@style/OverlayActivity" />
<!-- The main reason for the Tab Queue build flag is to not mess with the VIEW intent filter
before the rest of the plumbing is in place -->
<service android:name="org.mozilla.gecko.tabqueue.TabQueueService" />
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/promotion/SimpleHelperUI.java
@@ -0,0 +1,191 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.promotion;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.mozilla.gecko.Locales;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+
+/**
+ * Generic HelperUI (prompt) that can be populated with an image, title, message and action button.
+ * See show() for usage. This is run as an Activity, results must be handled in the parent Activities
+ * onActivityResult().
+ */
+public class SimpleHelperUI extends Locales.LocaleAwareActivity {
+ private View containerView;
+
+ private boolean isAnimating;
+
+ private String mTelemetryExtra;
+
+ private static final String EXTRA_TELEMETRYEXTRA = "telemetryextra";
+ private static final String EXTRA_TITLE = "title";
+ private static final String EXTRA_MESSAGE = "message";
+ private static final String EXTRA_IMAGE = "image";
+ private static final String EXTRA_BUTTON = "button";
+ private static final String EXTRA_RESULTCODE_POSITIVE = "positive";
+ private static final String EXTRA_RESULTCODE_NEGATIVE = "negative";
+
+
+ /**
+ * Show a generic helper UI/prompt.
+ *
+ * @param owner The owning Activity, the result of this prompt will be delivered to its
+ * onActivityResult().
+ * @param requestCode The request code for the Activity that will be created, this is passed to
+ * onActivityResult() to identify the prompt.
+ *
+ * @param positiveResultCode The result code passed to onActivityResult() when the button has
+ * been pressed.
+ * @param negativeResultCode The result code passed to onActivityResult() when the prompt was
+ * dismissed, either by pressing outside the prompt or by pressing the
+ * device back button.
+ */
+ public static void show(Activity owner, String telemetryExtra,
+ int requestCode,
+ @StringRes int title, @StringRes int message,
+ @DrawableRes int image, @StringRes int buttonText,
+ int positiveResultCode, int negativeResultCode) {
+ Intent intent = new Intent(owner, SimpleHelperUI.class);
+
+ intent.putExtra(EXTRA_TELEMETRYEXTRA, telemetryExtra);
+
+ intent.putExtra(EXTRA_TITLE, title);
+ intent.putExtra(EXTRA_MESSAGE, message);
+
+ intent.putExtra(EXTRA_IMAGE, image);
+ intent.putExtra(EXTRA_BUTTON, buttonText);
+
+ intent.putExtra(EXTRA_RESULTCODE_POSITIVE, positiveResultCode);
+ intent.putExtra(EXTRA_RESULTCODE_NEGATIVE, negativeResultCode);
+
+ owner.startActivityForResult(intent, requestCode);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mTelemetryExtra = getIntent().getStringExtra(EXTRA_TELEMETRYEXTRA);
+
+ setupViews();
+
+ slideIn();
+ }
+
+ private void setupViews() {
+ final Intent i = getIntent();
+
+ setContentView(R.layout.simple_helper_ui);
+
+ ((ImageView) findViewById(R.id.image)).setImageResource(i.getIntExtra(EXTRA_IMAGE, -1));
+
+ ((TextView) findViewById(R.id.title)).setText(i.getIntExtra(EXTRA_TITLE, -1));
+
+ ((TextView) findViewById(R.id.message)).setText(i.getIntExtra(EXTRA_MESSAGE, -1));
+
+ ((Button) findViewById(R.id.button)).setText(i.getIntExtra(EXTRA_BUTTON, -1));
+
+ containerView = findViewById(R.id.container);
+
+ findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ slideOut();
+
+ Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BUTTON, mTelemetryExtra);
+
+ setResult(i.getIntExtra(EXTRA_RESULTCODE_POSITIVE, -1));
+ }
+ });
+ }
+
+ private void slideIn() {
+ containerView.setTranslationY(500);
+ containerView.setAlpha(0);
+
+ final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
+ translateAnimator.setDuration(400);
+
+ final Animator alphaAnimator = ObjectAnimator.ofFloat(containerView, "alpha", 1);
+ alphaAnimator.setStartDelay(200);
+ alphaAnimator.setDuration(600);
+
+ final AnimatorSet set = new AnimatorSet();
+ set.playTogether(alphaAnimator, translateAnimator);
+ set.setStartDelay(400);
+
+ set.start();
+ }
+
+ private void slideOut() {
+ if (isAnimating) {
+ return;
+ }
+
+ isAnimating = true;
+
+ ObjectAnimator animator = ObjectAnimator.ofFloat(containerView, "translationY", containerView.getHeight());
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finish();
+ }
+
+ });
+ animator.start();
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+
+ // Don't perform an activity-dismiss animation.
+ overridePendingTransition(0, 0);
+ }
+
+ @Override
+ public void onBackPressed() {
+ slideOut();
+
+ Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, mTelemetryExtra);
+
+ setResult(getIntent().getIntExtra(EXTRA_RESULTCODE_NEGATIVE, -1));
+
+ }
+
+ /**
+ * User clicked outside of the prompt.
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ slideOut();
+
+ // Not really an action triggered by the "back" button but with the same effect: Finishing this
+ // activity and going back to the previous one.
+ Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, mTelemetryExtra);
+
+ setResult(getIntent().getIntExtra(EXTRA_RESULTCODE_NEGATIVE, -1));
+
+ return true;
+ }
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -510,16 +510,17 @@ gbjar.sources += ['java/org/mozilla/geck
'preferences/SearchPreferenceCategory.java',
'preferences/SetHomepagePreference.java',
'preferences/SyncPreference.java',
'PrefsHelper.java',
'PrintHelper.java',
'PrivateTab.java',
'promotion/AddToHomeScreenPromotion.java',
'promotion/HomeScreenPrompt.java',
+ 'promotion/SimpleHelperUI.java',
'prompts/ColorPickerInput.java',
'prompts/IconGridInput.java',
'prompts/IntentChooserPrompt.java',
'prompts/IntentHandler.java',
'prompts/Prompt.java',
'prompts/PromptInput.java',
'prompts/PromptListAdapter.java',
'prompts/PromptListItem.java',
new file mode 100644
--- /dev/null
+++ b/mobile/android/services/src/main/res/layout/simple_helper_ui.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/white"
+ android:layout_gravity="bottom|center"
+ android:clickable="true"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="40dp"
+ android:layout_marginBottom="40dp"
+ android:scaleType="fitCenter"
+ android:layout_gravity="center"
+ android:adjustViewBounds="true"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="@dimen/firstrun_content_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.FirstrunLight.Main"/>
+
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="@dimen/firstrun_content_width"
+ android:layout_height="wrap_content"
+ android:paddingTop="20dp"
+ android:paddingBottom="30dp"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.FirstrunRegular.Body"
+ android:singleLine="false"/>
+
+ <Button
+ android:id="@+id/button"
+ style="@style/Widget.Firstrun.Button"
+ android:background="@drawable/button_background_action_orange_round"
+ android:layout_gravity="center"
+ android:layout_marginBottom="30dp"/>
+
+ </LinearLayout>
+</merge>