Commit 0cf9aa09 authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[remoting][Android] Basic notification UI implement for Android

This CL implements a basic dialog-based notification UI for Android
client.

Bug: 1001291
Change-Id: If7df03179ad7a143a6c805c66595dbf7815eb074
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1846048
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Reviewed-by: default avatarJamie Walch <jamiewalch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703926}
parent 0b2f16bf
......@@ -17,6 +17,7 @@ generate_jni("jni_headers") {
"java/src/org/chromium/chromoting/jni/GlDisplay.java",
"java/src/org/chromium/chromoting/jni/JniInterface.java",
"java/src/org/chromium/chromoting/jni/JniOAuthTokenGetter.java",
"java/src/org/chromium/chromoting/jni/NotificationPresenter.java",
"java/src/org/chromium/chromoting/jni/TouchEventData.java",
]
}
......
......@@ -75,6 +75,7 @@ template("remoting_android_client_java_tmpl") {
"jni/DirectoryService.java",
"jni/JniInterface.java",
"jni/JniOAuthTokenGetter.java",
"jni/NotificationPresenter.java",
"jni/TouchEventData.java",
]
......@@ -97,7 +98,10 @@ template("remoting_android_client_java_tmpl") {
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
srcjar_deps = [ "//remoting/client/jni:jni_enums" ]
srcjar_deps = [
"//remoting/client/jni:jni_enums",
"//remoting/client/notification:notification_enums",
]
if (defined(invoker.play_services_package)) {
deps += [
......
......@@ -43,6 +43,7 @@ import org.chromium.chromoting.jni.ConnectionListener;
import org.chromium.chromoting.jni.DirectoryService;
import org.chromium.chromoting.jni.DirectoryServiceRequestError;
import org.chromium.chromoting.jni.JniOAuthTokenGetter;
import org.chromium.chromoting.jni.NotificationPresenter;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -97,6 +98,9 @@ public class Chromoting extends AppCompatActivity
/** Dialog for reporting connection progress. */
private ProgressDialog mProgressIndicator;
/** Helper for fetching notification and presenting it. */
private NotificationPresenter mNotificationPresenter;
/**
* Helper used by SessionConnection for session authentication. Receives onNewIntent()
* notifications to handle third-party authentication.
......@@ -236,6 +240,7 @@ public class Chromoting extends AppCompatActivity
setSupportActionBar(toolbar);
mDirectoryService = new DirectoryService();
mNotificationPresenter = new NotificationPresenter(this);
// Get ahold of our view widgets.
mHostListView = (ListView) findViewById(R.id.hostList_chooser);
......@@ -626,6 +631,7 @@ public class Chromoting extends AppCompatActivity
}
mAccount = accountName;
JniOAuthTokenGetter.setAccount(accountName);
mNotificationPresenter.presentIfNecessary(accountName);
// The current host list is no longer valid for the new account, so clear the list.
mHosts = new HostInfo[0];
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromoting.jni;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AlertDialog;
import androidx.annotation.IntDef;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chromoting.NotificationAppearance;
import org.chromium.chromoting.Preconditions;
import org.chromium.chromoting.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Helper for fetching and presenting a notification */
@JNINamespace("remoting")
public final class NotificationPresenter {
@IntDef({State.NOT_FETCHED, State.FETCHING, State.FETCHED})
@Retention(RetentionPolicy.SOURCE)
private @interface State {
int NOT_FETCHED = 0;
int FETCHING = 1;
int FETCHED = 2;
}
private final long mNativeJniNotificationPresenter;
private final Activity mActivity;
private @State int mState;
/**
* @param activity The activity on which the notification will be shown.
*/
public NotificationPresenter(Activity activity) {
mNativeJniNotificationPresenter = NotificationPresenterJni.get().init(this);
mActivity = activity;
mState = State.NOT_FETCHED;
}
@Override
public void finalize() {
NotificationPresenterJni.get().destroy(mNativeJniNotificationPresenter);
}
/**
* Presents notification for the given |username| if no previous notification has been shown,
* and the user is selected for the notification.
* @param username String that can uniquely identify a user
*/
public void presentIfNecessary(String username) {
if (mState != State.NOT_FETCHED) {
return;
}
mState = State.FETCHING;
NotificationPresenterJni.get().fetchNotification(mNativeJniNotificationPresenter, username);
}
@CalledByNative
void onNotificationFetched(@NotificationAppearance int appearance, String messageText,
String linkText, String linkUrl) {
Preconditions.isTrue(mState == State.FETCHING);
mState = State.FETCHED;
// TODO(yuweih): Add Don't Show Again support.
final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setMessage(messageText);
builder.setPositiveButton(linkText, (DialogInterface dialog, int id) -> {
Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl));
mActivity.startActivity(openLink);
});
builder.setNegativeButton(R.string.dismiss,
(DialogInterface dialog, int id)
-> {
// Do nothing
});
final AlertDialog dialog = builder.create();
dialog.show();
}
@CalledByNative
void onNoNotification() {
Preconditions.isTrue(mState == State.FETCHING);
mState = State.NOT_FETCHED;
}
@NativeMethods
interface Natives {
long init(NotificationPresenter javaPresenter);
void fetchNotification(long nativeJniNotificationPresenter, String username);
void destroy(long nativeJniNotificationPresenter);
}
}
......@@ -22,6 +22,7 @@ shared_library("remoting_client_jni") {
"//remoting/base",
"//remoting/client",
"//remoting/client/display",
"//remoting/client/notification",
"//remoting/proto/remoting/v1:directory_grpc_library",
"//remoting/protocol",
"//ui/gfx",
......@@ -35,6 +36,8 @@ shared_library("remoting_client_jni") {
"jni_directory_service.h",
"jni_gl_display_handler.cc",
"jni_gl_display_handler.h",
"jni_notification_presenter.cc",
"jni_notification_presenter.h",
"jni_oauth_token_getter.cc",
"jni_oauth_token_getter.h",
"jni_runtime_delegate.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/client/jni/jni_notification_presenter.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "remoting/android/jni_headers/NotificationPresenter_jni.h"
#include "remoting/client/chromoting_client_runtime.h"
#include "remoting/client/notification/notification_message.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
namespace remoting {
JniNotificationPresenter::JniNotificationPresenter(
const JavaObjectWeakGlobalRef& java_presenter)
: java_presenter_(java_presenter),
notification_client_(
ChromotingClientRuntime::GetInstance()->network_task_runner()),
sequence_(base::SequencedTaskRunnerHandle::Get()) {}
JniNotificationPresenter::~JniNotificationPresenter() {
DCHECK(sequence_->RunsTasksInCurrentSequence());
}
void JniNotificationPresenter::FetchNotification(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& username) {
DCHECK(sequence_->RunsTasksInCurrentSequence());
std::string username_str = ConvertJavaStringToUTF8(env, username);
// Safe to use unretained: callback is dropped once client is deleted.
notification_client_.GetNotification(
username_str,
base::BindOnce(&JniNotificationPresenter::OnNotificationFetched,
base::Unretained(this)));
}
void JniNotificationPresenter::Destroy(JNIEnv* env) {
if (sequence_->RunsTasksInCurrentSequence()) {
delete this;
} else {
sequence_->DeleteSoon(FROM_HERE, this);
}
}
void JniNotificationPresenter::OnNotificationFetched(
base::Optional<NotificationMessage> notification) {
DCHECK(sequence_->RunsTasksInCurrentSequence());
JNIEnv* env = base::android::AttachCurrentThread();
auto java_presenter = java_presenter_.get(env);
if (!notification) {
Java_NotificationPresenter_onNoNotification(env, java_presenter);
return;
}
jint j_appearance = static_cast<jint>(notification->appearance);
auto j_message_text =
ConvertUTF8ToJavaString(env, notification->message_text);
auto j_link_text = ConvertUTF8ToJavaString(env, notification->link_text);
auto j_link_url = ConvertUTF8ToJavaString(env, notification->link_url);
Java_NotificationPresenter_onNotificationFetched(env, java_presenter,
j_appearance, j_message_text,
j_link_text, j_link_url);
}
static jlong JNI_NotificationPresenter_Init(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& java_presenter) {
return reinterpret_cast<intptr_t>(new JniNotificationPresenter(
JavaObjectWeakGlobalRef(env, java_presenter)));
}
} // namespace remoting
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_CLIENT_JNI_JNI_NOTIFICATION_PRESENTER_H_
#define REMOTING_CLIENT_JNI_JNI_NOTIFICATION_PRESENTER_H_
#include <jni.h>
#include "base/android/jni_weak_ref.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequenced_task_runner.h"
#include "remoting/client/notification/notification_client.h"
namespace remoting {
// C++ counterpart for org.chromium.chromoting.jni.NotificationPresenter.
class JniNotificationPresenter final {
public:
explicit JniNotificationPresenter(
const JavaObjectWeakGlobalRef& java_presenter);
~JniNotificationPresenter();
void FetchNotification(JNIEnv* env,
const base::android::JavaParamRef<jstring>& username);
void Destroy(JNIEnv* env);
private:
void OnNotificationFetched(base::Optional<NotificationMessage> notification);
JavaObjectWeakGlobalRef java_presenter_;
NotificationClient notification_client_;
scoped_refptr<base::SequencedTaskRunner> sequence_;
DISALLOW_COPY_AND_ASSIGN(JniNotificationPresenter);
};
} // namespace remoting
#endif // REMOTING_CLIENT_JNI_JNI_NOTIFICATION_PRESENTER_H_
\ No newline at end of file
......@@ -2,6 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
}
source_set("notification") {
sources = [
"gstatic_json_fetcher.cc",
......@@ -25,6 +30,14 @@ source_set("notification") {
]
}
if (is_android) {
java_cpp_enum("notification_enums") {
sources = [
"notification_message.h",
]
}
}
source_set("unit_tests") {
testonly = true
......
......@@ -10,6 +10,8 @@
namespace remoting {
struct NotificationMessage final {
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chromoting
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: NotificationAppearance
enum class Appearance {
TOAST,
DIALOG,
......
......@@ -659,7 +659,7 @@
<message name="IDS_PRIVACY_POLICY" desc="The label to access the privacy policy, displayed in navigation menu." formatter_data="android_java">
Privacy Policy
</message>
<message name="IDS_DISMISS" desc="Label for button to dismiss a dialog">
<message name="IDS_DISMISS" desc="Label for button to dismiss a dialog" formatter_data="android_java">
Dismiss
</message>
<message name="IDS_DONT_SHOW_AGAIN" desc="Label for checkbox to make a dialog not showing again.">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment