File: ambient_managed_photo_controller.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (215 lines) | stat: -rw-r--r-- 7,022 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/ambient/ambient_managed_photo_controller.h"

#include <utility>
#include <vector>

#include "ash/ambient/metrics/managed_screensaver_metrics.h"
#include "ash/ambient/model/ambient_backend_model.h"
#include "ash/public/cpp/image_util.h"
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "ui/gfx/image/image_skia.h"

namespace ash {

namespace {
constexpr size_t kMinImagesRequired = 2u;

}  // namespace

AmbientManagedPhotoController::AmbientManagedPhotoController(
    AmbientViewDelegate& view_delegate,
    AmbientPhotoConfig photo_config)
    : ambient_backend_model_(std::move(photo_config)) {
  scoped_view_delegate_observation_.Observe(&view_delegate);
}

AmbientManagedPhotoController::~AmbientManagedPhotoController() = default;

void AmbientManagedPhotoController::StartScreenUpdate() {
  if (IsScreenUpdateActive()) {
    LOG(ERROR) << "AmbientManagedPhotoController is already active. Ignoring "
                  "StartScreenUpdate().";
    return;
  }

  is_active_ = true;
  image_attempt_no_ = 0;

  LoadImages();
}

void AmbientManagedPhotoController::UpdateImageFilePaths(
    const std::vector<base::FilePath>& images) {
  RecordManagedScreensaverImageCount(images.size());

  // Reset `error_state_` when a sufficient number of new images are received
  if (images.size() < kMinImagesRequired) {
    // TODO(b/269579804): Add Metrics
    SetErrorState(ErrorState::kInsufficientImages);
    return;
  }
  SetErrorState(ErrorState::kNone);

  images_file_paths_ = images;
  image_attempt_no_ = 0;

  if (IsScreenUpdateActive()) {
    // Invalidate the weak pointers to make sure that any in-flight decoding
    // operations become no-ops.
    weak_factory_.InvalidateWeakPtrs();
    current_image_index_ = 0;

    // Note: We do not clear the backend model here but rather just load
    // the next topic buffer size images from disk, this will automatically
    // fill the backend model with only the latest images.
    LoadImages();
  }
}

bool AmbientManagedPhotoController::HasScreenUpdateErrors() const {
  return error_state_ != ErrorState::kNone;
}

void AmbientManagedPhotoController::SetErrorState(ErrorState error_state) {
  if (error_state == error_state_) {
    return;
  }
  error_state_ = error_state;
  if (observer_) {
    observer_->OnErrorStateChanged();
  }
}

void AmbientManagedPhotoController::StopScreenUpdate() {
  is_active_ = false;
  images_file_paths_.clear();
  ambient_backend_model_.Clear();
  weak_factory_.InvalidateWeakPtrs();
  current_image_index_ = 0;
  image_attempt_no_ = 0;
}

bool AmbientManagedPhotoController::IsScreenUpdateActive() const {
  return is_active_;
}

void AmbientManagedPhotoController::OnMarkerHit(
    AmbientPhotoConfig::Marker marker) {
  if (!ambient_backend_model_.photo_config().refresh_topic_markers.contains(
          marker)) {
    DVLOG(3) << "UI event " << marker
             << " does not trigger a image refresh. Ignoring...";
    return;
  }
  if (error_state_ == ErrorState::kPhotoLoadFailure) {
    LOG(WARNING) << "Not loading the next image for the UI marker " << marker
                 << " as maximum photo loading attempts reached";
    return;
  }

  DVLOG(3) << "UI event " << marker << " triggering image load";
  if (!is_active_) {
    LOG(DFATAL) << "Received unexpected UI marker " << marker
                << " while inactive";
    return;
  }

  LoadImagesInternal(/*images_to_load=*/1, /*success=*/true);
}

void AmbientManagedPhotoController::LoadImages() {
  if (images_file_paths_.size() < kMinImagesRequired) {
    // TODO(b/269579804): Log metrics to detect if the images are not enough.
    LOG(WARNING)
        << "AmbientManagedPhotoController does not have enough images.";
    return;
  }

  // Initially load a total of backend model buffer size images in the backend
  // model. Note: This should not be lower than 2 because the photo view code
  // right now loads the current and the next image simultaneously, and in case
  // of a buffer size equal to 1 it never triggers the images ready callback.
  LoadImagesInternal(
      ambient_backend_model_.photo_config().GetNumDecodedTopicsToBuffer(),
      true);
}

void AmbientManagedPhotoController::LoadImagesInternal(size_t images_to_load,
                                                       bool success) {
  if (images_to_load == 0 || !success) {
    return;
  }
  // Use a partial function application to store the no of remaining images to
  // load.
  base::OnceCallback<void(bool)> done_callback = base::BindOnce(
      &AmbientManagedPhotoController::LoadImagesInternal,
      weak_factory_.GetWeakPtr(), /*images_to_load=*/images_to_load - 1);
  LoadNextImage(std::move(done_callback));
}

void AmbientManagedPhotoController::LoadNextImage(
    base::OnceCallback<void(bool success)> done_callback) {
  CHECK(images_file_paths_.size() >= kMinImagesRequired);
  image_attempt_no_++;
  current_image_index_ = (current_image_index_ + 1) % images_file_paths_.size();
  image_util::DecodeImageFile(
      base::BindOnce(&AmbientManagedPhotoController::OnPhotoDecoded,
                     weak_factory_.GetWeakPtr(), std::move(done_callback)),
      images_file_paths_.at(current_image_index_),
      data_decoder::mojom::ImageCodec::kDefault);
}

void AmbientManagedPhotoController::HandlePhotoDecodingFailure(
    base::OnceCallback<void(bool success)> done_callback) {
  if (image_attempt_no_ >= GetMaxImageAttempts()) {
    LOG(ERROR) << "Image decoding failed, no valid image was decoded";
    SetErrorState(ErrorState::kPhotoLoadFailure);
    std::move(done_callback).Run(false);
    return;
  }
  LOG(WARNING) << "Image decoding failed, attempting to load next image";
  LoadNextImage(std::move(done_callback));
}

void AmbientManagedPhotoController::OnPhotoDecoded(
    base::OnceCallback<void(bool success)> done_callback,
    const gfx::ImageSkia& image) {
  DVLOG(3) << __func__;
  if (image.isNull()) {
    HandlePhotoDecodingFailure(std::move(done_callback));
    return;
  }

  image_attempt_no_ = 0;
  PhotoWithDetails detailed_photo;
  detailed_photo.photo = image;
  // Note: There is no surefire way of determining the orientation of the image
  // so for now we assume and document that all admin provided images are
  // landscape.
  detailed_photo.is_portrait = false;

  ambient_backend_model_.AddNextImage(std::move(detailed_photo));

  // Notify the caller that photo loading was successful.
  std::move(done_callback).Run(true);
}

size_t AmbientManagedPhotoController::GetMaxImageAttempts() const {
  CHECK_GE(images_file_paths_.size(), kMinImagesRequired);
  return images_file_paths_.size() - 1;
}

void AmbientManagedPhotoController::SetObserver(Observer* observer) {
  CHECK(!observer_);
  observer_ = observer;
}

}  // namespace ash