1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/screen_orientation/screen_orientation_provider_android.h"
6
7#include "content/browser/android/content_view_core_impl.h"
8#include "content/browser/web_contents/web_contents_impl.h"
9#include "content/public/browser/render_view_host.h"
10#include "content/public/browser/render_widget_host.h"
11#include "content/public/browser/screen_orientation_dispatcher_host.h"
12#include "jni/ScreenOrientationProvider_jni.h"
13#include "third_party/WebKit/public/platform/WebLockOrientationError.h"
14#include "third_party/WebKit/public/platform/WebScreenInfo.h"
15
16namespace content {
17
18ScreenOrientationProviderAndroid::LockInformation::LockInformation(
19    int request_id, blink::WebScreenOrientationLockType lock)
20    : request_id(request_id), lock(lock) {}
21
22ScreenOrientationProviderAndroid::ScreenOrientationProviderAndroid(
23    ScreenOrientationDispatcherHost* dispatcher,
24    WebContents* web_contents)
25    : ScreenOrientationProvider(),
26      WebContentsObserver(web_contents),
27      dispatcher_(dispatcher),
28      lock_applied_(false),
29      pending_lock_(NULL) {
30}
31
32ScreenOrientationProviderAndroid::~ScreenOrientationProviderAndroid() {
33  if (pending_lock_)
34    delete pending_lock_;
35}
36
37WebContentsImpl* ScreenOrientationProviderAndroid::web_contents_impl() {
38  return static_cast<WebContentsImpl*>(web_contents());
39}
40
41// static
42bool ScreenOrientationProviderAndroid::Register(JNIEnv* env) {
43  return RegisterNativesImpl(env);
44}
45
46void ScreenOrientationProviderAndroid::LockOrientation(
47    int request_id,
48    blink::WebScreenOrientationLockType lock_orientation) {
49  ContentViewCoreImpl* cvc =
50      ContentViewCoreImpl::FromWebContents(web_contents());
51  bool fullscreen_required = cvc ? cvc->IsFullscreenRequiredForOrientationLock()
52                                 : true;
53
54  if (fullscreen_required &&
55      !web_contents_impl()->IsFullscreenForCurrentTab()) {
56    dispatcher_->NotifyLockError(
57        request_id,
58        blink::WebLockOrientationErrorFullScreenRequired);
59    return;
60  }
61
62  if (lock_orientation == blink::WebScreenOrientationLockNatural) {
63    lock_orientation = GetNaturalLockType();
64    if (lock_orientation == blink::WebScreenOrientationLockDefault) {
65      // We are in a broken state, let's pretend we got canceled.
66      dispatcher_->NotifyLockError(request_id,
67                                   blink::WebLockOrientationErrorCanceled);
68      return;
69    }
70  }
71
72  lock_applied_ = true;
73  Java_ScreenOrientationProvider_lockOrientation(
74      base::android::AttachCurrentThread(), lock_orientation);
75
76  // If two calls happen close to each other, Android will ignore the first.
77  if (pending_lock_) {
78    delete pending_lock_;
79    pending_lock_ = NULL;
80  }
81
82  // If the orientation we are locking to matches the current orientation, we
83  // should succeed immediately.
84  if (LockMatchesCurrentOrientation(lock_orientation)) {
85    dispatcher_->NotifyLockSuccess(request_id);
86    return;
87  }
88
89  pending_lock_ = new LockInformation(request_id, lock_orientation);
90}
91
92void ScreenOrientationProviderAndroid::UnlockOrientation() {
93  if (!lock_applied_)
94    return;
95
96  Java_ScreenOrientationProvider_unlockOrientation(
97      base::android::AttachCurrentThread());
98  lock_applied_ = false;
99}
100
101void ScreenOrientationProviderAndroid::OnOrientationChange() {
102  if (!pending_lock_)
103    return;
104
105  if (LockMatchesCurrentOrientation(pending_lock_->lock)) {
106    dispatcher_->NotifyLockSuccess(pending_lock_->request_id);
107    delete pending_lock_;
108    pending_lock_ = NULL;
109  }
110}
111
112void ScreenOrientationProviderAndroid::DidToggleFullscreenModeForTab(
113    bool entered_fullscreen) {
114  if (!lock_applied_)
115    return;
116
117  // If fullscreen is not required in order to lock orientation, don't unlock
118  // when fullscreen state changes.
119  ContentViewCoreImpl* cvc =
120      ContentViewCoreImpl::FromWebContents(web_contents());
121  if (cvc && !cvc->IsFullscreenRequiredForOrientationLock())
122    return;
123
124  DCHECK(!entered_fullscreen);
125  UnlockOrientation();
126}
127
128bool ScreenOrientationProviderAndroid::LockMatchesCurrentOrientation(
129    blink::WebScreenOrientationLockType lock_orientation) {
130  if (!web_contents()->GetRenderViewHost())
131    return false;
132
133  RenderWidgetHost* rwh = web_contents()->GetRenderViewHost();
134  blink::WebScreenInfo screen_info;
135  rwh->GetWebScreenInfo(&screen_info);
136
137  switch (lock_orientation) {
138  case blink::WebScreenOrientationLockPortraitPrimary:
139    return screen_info.orientationType ==
140        blink::WebScreenOrientationPortraitPrimary;
141  case blink::WebScreenOrientationLockPortraitSecondary:
142    return screen_info.orientationType ==
143        blink::WebScreenOrientationPortraitSecondary;
144  case blink::WebScreenOrientationLockLandscapePrimary:
145    return screen_info.orientationType ==
146        blink::WebScreenOrientationLandscapePrimary;
147  case blink::WebScreenOrientationLockLandscapeSecondary:
148    return screen_info.orientationType ==
149        blink::WebScreenOrientationLandscapeSecondary;
150  case blink::WebScreenOrientationLockLandscape:
151    return screen_info.orientationType ==
152        blink::WebScreenOrientationLandscapePrimary ||
153        screen_info.orientationType ==
154        blink::WebScreenOrientationLandscapeSecondary;
155  case blink::WebScreenOrientationLockPortrait:
156    return screen_info.orientationType ==
157        blink::WebScreenOrientationPortraitPrimary ||
158        screen_info.orientationType ==
159        blink::WebScreenOrientationPortraitSecondary;
160  case blink::WebScreenOrientationLockAny:
161    return true;
162  case blink::WebScreenOrientationLockNatural:
163  case blink::WebScreenOrientationLockDefault:
164    NOTREACHED();
165    return false;
166  }
167
168  NOTREACHED();
169  return false;
170}
171
172blink::WebScreenOrientationLockType
173ScreenOrientationProviderAndroid::GetNaturalLockType() const {
174  if (!web_contents()->GetRenderViewHost())
175    return blink::WebScreenOrientationLockDefault;
176
177  RenderWidgetHost* rwh = web_contents()->GetRenderViewHost();
178  blink::WebScreenInfo screen_info;
179  rwh->GetWebScreenInfo(&screen_info);
180
181  switch (screen_info.orientationType) {
182  case blink::WebScreenOrientationPortraitPrimary:
183  case blink::WebScreenOrientationPortraitSecondary:
184    if (screen_info.orientationAngle == 0 ||
185        screen_info.orientationAngle == 180) {
186      return blink::WebScreenOrientationLockPortraitPrimary;
187    }
188    return blink::WebScreenOrientationLockLandscapePrimary;
189  case blink::WebScreenOrientationLandscapePrimary:
190  case blink::WebScreenOrientationLandscapeSecondary:
191    if (screen_info.orientationAngle == 0 ||
192        screen_info.orientationAngle == 180) {
193      return blink::WebScreenOrientationLockLandscapePrimary;
194    }
195    return blink::WebScreenOrientationLockPortraitPrimary;
196  case blink::WebScreenOrientationUndefined:
197    NOTREACHED();
198    return blink::WebScreenOrientationLockDefault;
199  }
200
201  NOTREACHED();
202  return blink::WebScreenOrientationLockDefault;
203}
204
205// static
206void ScreenOrientationProviderAndroid::StartAccurateListening() {
207  Java_ScreenOrientationProvider_startAccurateListening(
208      base::android::AttachCurrentThread());
209}
210
211// static
212void ScreenOrientationProviderAndroid::StopAccurateListening() {
213  Java_ScreenOrientationProvider_stopAccurateListening(
214      base::android::AttachCurrentThread());
215}
216
217// static
218ScreenOrientationProvider* ScreenOrientationProvider::Create(
219    ScreenOrientationDispatcherHost* dispatcher,
220    WebContents* web_contents) {
221  return new ScreenOrientationProviderAndroid(dispatcher, web_contents);
222}
223
224} // namespace content
225