Unity 8
indicatorsmanager.cpp
1 /*
2  * Copyright (C) 2013-2016 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include "indicatorsmanager.h"
18 
19 #include <QSettings>
20 #include <QDebug>
21 
22 #include <paths.h>
23 
24 
25 class IndicatorsManager::IndicatorData
26 {
27 public:
28  IndicatorData(const QString& name, const QFileInfo& fileInfo)
29  : m_name(name)
30  , m_fileInfo(fileInfo)
31  , m_verified (true)
32  {
33  }
34 
35  QString m_name;
36  QFileInfo m_fileInfo;
37 
38  bool m_verified;
39  Indicator::Ptr m_indicator;
40 };
41 
42 IndicatorsManager::IndicatorsManager(QObject* parent)
43  : QObject(parent)
44  , m_loaded(false)
45  , m_profile(QStringLiteral("phone"))
46 {
47 }
48 
49 IndicatorsManager::~IndicatorsManager()
50 {
51  unload();
52 }
53 
54 void IndicatorsManager::load()
55 {
56  unload();
57 
58  const QStringList xdgLocations = shellDataDirs();
59 
60  m_fsWatcher.reset(new QFileSystemWatcher(this));
61 
62  Q_FOREACH(const QString& xdgLocation, xdgLocations)
63  {
64  const QString indicator_path = QDir::cleanPath(xdgLocation + "/unity/indicators");
65  QDir indicator_dir(indicator_path);
66  if (indicator_dir.exists())
67  {
68  // watch folder for changes.
69  m_fsWatcher->addPath(indicator_path);
70 
71  loadDir(indicator_dir);
72  }
73  }
74 
75  QObject::connect(m_fsWatcher.data(), &QFileSystemWatcher::directoryChanged, this, &IndicatorsManager::onDirectoryChanged);
76  QObject::connect(m_fsWatcher.data(), &QFileSystemWatcher::fileChanged, this, &IndicatorsManager::onFileChanged);
77  setLoaded(true);
78 }
79 
80 void IndicatorsManager::onDirectoryChanged(const QString& directory)
81 {
82  loadDir(QDir(directory));
83 }
84 
85 void IndicatorsManager::onFileChanged(const QString& file)
86 {
87  QFileInfo file_info(file);
88  if (!file_info.exists())
89  {
90  unloadFile(file_info);
91  return;
92  }
93  else
94  {
95  loadFile(QFileInfo(file));
96  }
97 }
98 
99 void IndicatorsManager::loadDir(const QDir& dir)
100 {
101  startVerify(dir.canonicalPath());
102 
103  const QFileInfoList indicator_files = dir.entryInfoList(QStringList(), QDir::Files|QDir::NoDotAndDotDot);
104  Q_FOREACH(const QFileInfo& indicator_file, indicator_files)
105  {
106  loadFile(indicator_file);
107  }
108 
109  endVerify(dir.canonicalPath());
110 }
111 
112 void IndicatorsManager::loadFile(const QFileInfo& file_info)
113 {
114  QSettings indicator_settings(file_info.absoluteFilePath(), QSettings::IniFormat, this);
115  const QString name = indicator_settings.value(QStringLiteral("Indicator Service/Name")).toString();
116 
117  if (m_platform.isPC() && name == QLatin1String("indicator-keyboard")) {
118  return; // convergence: skip this indicator until it works in Mir
119  }
120 
121  auto iter = m_indicatorsData.constFind(name);
122  if (iter != m_indicatorsData.constEnd())
123  {
124  const QString newFileInfoDir = QDir::cleanPath(file_info.canonicalPath());
125  IndicatorData* currentData = (*iter);
126  currentData->m_verified = true;
127 
128  int file_info_location = -1;
129  int current_data_location = -1;
130 
131  const QString currentDataDir = QDir::cleanPath(currentData->m_fileInfo.canonicalPath());
132 
133  // if we've already got this indicator, we need to make sure we're not overwriting data which is
134  // from a lower priority standard path
135  QStringList xdgLocations = shellDataDirs();
136  for (int i = 0; i < xdgLocations.size(); i++)
137  {
138  const QString indicatorDir = QDir::cleanPath(xdgLocations[i] + "/unity/indicators");
139 
140  if (newFileInfoDir == indicatorDir)
141  {
142  file_info_location = i;
143  }
144  if (currentDataDir == indicatorDir)
145  {
146  current_data_location = i;
147  }
148 
149  if (file_info_location != -1 && current_data_location != -1)
150  {
151  break;
152  }
153  }
154 
155  // file location is higher (or of equal) priority. overwrite.
156  if (file_info_location <= current_data_location &&
157  file_info != currentData->m_fileInfo)
158  {
159  currentData->m_fileInfo = file_info;
160  Q_EMIT indicatorLoaded(name);
161  }
162  }
163  else
164  {
165  IndicatorData* data = new IndicatorData(name, file_info);
166  data->m_verified = true;
167  m_indicatorsData[name]= data;
168  Q_EMIT indicatorLoaded(name);
169  }
170 }
171 
172 void IndicatorsManager::unload()
173 {
174  QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
175  while(iter.hasNext())
176  {
177  iter.next();
178  Q_EMIT indicatorAboutToBeUnloaded(iter.key());
179  }
180 
181  qDeleteAll(m_indicatorsData);
182  m_indicatorsData.clear();
183 
184  setLoaded(false);
185 }
186 
187 void IndicatorsManager::unloadFile(const QFileInfo& file)
188 {
189  QMutableHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
190  while(iter.hasNext())
191  {
192  iter.next();
193  IndicatorData* data = iter.value();
194  if (data->m_fileInfo.absoluteFilePath() == file.absoluteFilePath())
195  {
196  if (!data->m_verified)
197  {
198  const QString name = data->m_name;
199  Q_EMIT indicatorAboutToBeUnloaded(name);
200 
201  delete data;
202  iter.remove();
203  }
204  }
205  }
206 
207  setLoaded(m_indicatorsData.size() > 0);
208 }
209 
210 void IndicatorsManager::setLoaded(bool loaded)
211 {
212  if (loaded != m_loaded)
213  {
214  m_loaded = loaded;
215  Q_EMIT loadedChanged(m_loaded);
216  }
217 }
218 
219 QString IndicatorsManager::profile() const
220 {
221  return m_profile;
222 }
223 
224 void IndicatorsManager::setProfile(const QString& profile)
225 {
226  if (m_profile != profile) {
227  m_profile = profile;
228  Q_EMIT profileChanged(m_profile);
229  }
230 }
231 
232 void IndicatorsManager::startVerify(const QString& path)
233 {
234  QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
235  while(iter.hasNext())
236  {
237  iter.next();
238  IndicatorData* data = iter.value();
239  if (data->m_fileInfo.canonicalPath() == path)
240  {
241  data->m_verified = false;
242  }
243  }
244 }
245 
246 void IndicatorsManager::endVerify(const QString& path)
247 {
248  QMutableHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
249  while(iter.hasNext())
250  {
251  iter.next();
252  IndicatorData* data = iter.value();
253  if (data->m_fileInfo.canonicalPath() == path)
254  {
255  if (!data->m_verified)
256  {
257  const QString name = data->m_name;
258  Q_EMIT indicatorAboutToBeUnloaded(name);
259 
260  delete data;
261  iter.remove();
262  }
263  }
264  }
265 }
266 
267 Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
268 {
269  if (!m_indicatorsData.contains(indicator_name))
270  {
271  qWarning() << Q_FUNC_INFO << "Invalid indicator name: " << indicator_name;
272  return Indicator::Ptr();
273  }
274 
275  IndicatorData *data = m_indicatorsData.value(indicator_name);
276  if (data->m_indicator)
277  {
278  return data->m_indicator;
279  }
280 
281  Indicator::Ptr new_indicator(new Indicator(this));
282  data->m_indicator = new_indicator;
283  QSettings settings(data->m_fileInfo.absoluteFilePath(), QSettings::IniFormat, this);
284  new_indicator->init(data->m_fileInfo.fileName(), settings);
285 
286  // convergence:
287  // 1) enable session indicator
288  // 2) on a PC, switch the battery/power indicator to desktop mode,
289  // can't control brightness for now and phone-on-desktop broken (FIXME)
290  //
291  // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
292  if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
293  || (new_indicator->identifier() == QStringLiteral("indicator-power") && m_platform.isPC())) {
294  new_indicator->setProfile(QStringLiteral("desktop"));
295  } else {
296  new_indicator->setProfile(m_profile);
297  }
298 
299  QObject::connect(this, &IndicatorsManager::profileChanged, new_indicator.data(), &Indicator::setProfile);
300  return new_indicator;
301 }
302 
303 QVector<Indicator::Ptr> IndicatorsManager::indicators()
304 {
305  QVector<Indicator::Ptr> list;
306  Q_FOREACH(IndicatorData* data, m_indicatorsData)
307  {
308  Indicator::Ptr ret = indicator(data->m_name);
309  if (ret)
310  list.append(ret);
311  }
312  return list;
313 }
314 
315 bool IndicatorsManager::isLoaded() const
316 {
317  return m_loaded;
318 }