Skip to content

Commit ff38f3c

Browse files
hosymneuwert
andauthored
[feature/whats new screen] In-App changelog "What´s new screen" (#572)
* first draft of what´s new screen * Autoplay Media / Show album artwork in the media player view (#566) * [fix/fp-offline-browsing] Allow offline browsing of folders in the File Provider (#547) * - Fix Swift and SwiftLint warnings - Remove unused UploadsSettingsSection (was replaced by MediaUploadSettings) * - address libzip Xcode project upgrade warning - add research note to FileProviderExtension - update SDK * - Allow offline browsing of folders in the File Provider * Revert "[fix/fp-offline-browsing] Allow offline browsing of folders in the File Provider (#547)" (#553) This reverts commit 9a0bc93. * Autoplay media files implemented as described in issue #59 * Added album artwork as overlay in the player * Fixed playing next media item in BG and lock screen - Now multiple items can be played contignuously in the background - Now playing info in the lock screen contains artwork, title, artist info and displays correct playback timeline - Audio can be paused / resumed from the lock screen - Added skip controls allowing to jump 10s backwards and forwards from the play-head position in the lock screen * Small fixes * [fix/open-in-on-ipad] Share sheet not visible on iPad (#570) * [fix/fp-offline-browsing] Allow offline browsing of folders in the File Provider (#547) * - Fix Swift and SwiftLint warnings - Remove unused UploadsSettingsSection (was replaced by MediaUploadSettings) * - address libzip Xcode project upgrade warning - add research note to FileProviderExtension - update SDK * - Allow offline browsing of folders in the File Provider * Revert "[fix/fp-offline-browsing] Allow offline browsing of folders in the File Provider (#547)" (#553) This reverts commit 9a0bc93. * Fix for issue #568: Share sheet was now visible on iPad, if tableview was scrolled down, after first visible page rect * Changed implementation for ReleaseNotes controller * - moved release notes to new folder - added release notes for version 1.2 - added a datasource class and a check, if release notes should appear and which items * Added task to update In-App changelog * No longer needed, because code was moved * No longer needed, decided for other solution * code review fixes * fixed typo * Keyboard commands are available below iOS 13 removed version 13 from release notes * - no longer showing release notes after app install - fix showing release notes, after was displayed - added app review request - changed release notes * set correct short app version * new check, if app was previously installed, with a key already exists with previous app versions * - removed unneeded code - added code comment * if clause is needed, otherwise next else statement would be always true * changed fallback check, because .isBetaBuild always exists * added support for customize app name Co-authored-by: Michael Neuwert <[email protected]>
1 parent 39ee826 commit ff38f3c

File tree

10 files changed

+471
-29
lines changed

10 files changed

+471
-29
lines changed

.github/release_template.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Release a new version
1111
- [ ] [DEV] Update `APP_SHORT_VERSION` `[major].[minor].[patch]` in [ownCloud.xcodeproj/project.pbxproj](https://github.com/owncloud/ios-app/blob/master/ownCloud.xcodeproj/project.pbxproj)
1212
- [ ] [TRFX] Update translations from transifex branch.
1313
- [ ] [DIS] Update [changelog](https://github.com/owncloud/ios-app/blob/master/CHANGELOG.md)
14+
- [ ] [DEV] Update In-App Release Notes (changelog) in ownCloud/Release Notes/ReleaseNotes.plist
1415
- [ ] [QA] Design Test plan
1516
- [ ] [QA] Regression Test plan
1617
- [ ] [DOC] Update user manual with the new functionalities
@@ -21,7 +22,7 @@ Release a new version
2122

2223
If it is required to update the iOS-SDK version:
2324

24-
- [ ] [GIT] Create branch library `release/[major].[minor].[patch]`(freeze the code)
25+
- [ ] [GIT] Create branch library `release/[major].[minor].[patch]`(freeze the code)
2526
- [ ] [mail] inform #marketing about the new release.
2627
- [ ] [DIS] Update README.md (version number, third party, supported versions of iOS, Xcode)
2728
- [ ] [DIS] Update [changelog](https://github.com/owncloud/ios-sdk/blob/master/CHANGELOG.md)

ownCloud.xcodeproj/project.pbxproj

Lines changed: 38 additions & 26 deletions
Large diffs are not rendered by default.

ownCloud/Client/Viewer/Media/MediaDisplayViewController.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ class MediaDisplayViewController : DisplayViewController {
225225
}
226226

227227
// Add handler for skip forward command
228+
commandCenter.skipForwardCommand.isEnabled = true
228229
commandCenter.skipForwardCommand.addTarget { [weak self] (_) -> MPRemoteCommandHandlerStatus in
229230
if let player = self?.player {
230231
let time = player.currentTime() + CMTime(seconds: 10.0, preferredTimescale: 1)
@@ -239,6 +240,7 @@ class MediaDisplayViewController : DisplayViewController {
239240
}
240241

241242
// Add handler for skip backward command
243+
commandCenter.skipBackwardCommand.isEnabled = true
242244
commandCenter.skipBackwardCommand.addTarget { [weak self] (_) -> MPRemoteCommandHandlerStatus in
243245
if let player = self?.player {
244246
let time = player.currentTime() - CMTime(seconds: 10.0, preferredTimescale: 1)
@@ -314,6 +316,7 @@ class MediaDisplayViewController : DisplayViewController {
314316

315317
nowPlayingInfo[MPMediaItemPropertyTitle] = mediaItemTitle
316318
nowPlayingInfo[MPMediaItemPropertyArtist] = mediaItemArtist
319+
nowPlayingInfo[MPNowPlayingInfoPropertyCurrentPlaybackDate] = self.playerItem?.currentDate()
317320
nowPlayingInfo[MPNowPlayingInfoPropertyAssetURL] = source
318321
nowPlayingInfo[MPNowPlayingInfoPropertyCurrentPlaybackDate] = playerItem.currentDate()
319322
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = playerItem.currentTime().seconds

ownCloud/Release Notes/ReleaseNotes.plist

Lines changed: 133 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
//
2+
// ReleaseNotesHostViewController.swift
3+
// ownCloud
4+
//
5+
// Created by Matthias Hühne on 04.12.19.
6+
// Copyright © 2019 ownCloud GmbH. All rights reserved.
7+
//
8+
9+
/*
10+
* Copyright (C) 2019, ownCloud GmbH.
11+
*
12+
* This code is covered by the GNU Public License Version 3.
13+
*
14+
* For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/
15+
* You should have received a copy of this license along with this program. If not, see <http://www.gnu.org/licenses/gpl-3.0.en.html>.
16+
*
17+
*/
18+
19+
import UIKit
20+
import ownCloudSDK
21+
import StoreKit
22+
23+
class ReleaseNotesHostViewController: UIViewController {
24+
25+
// MARK: - Constants
26+
private let cornerRadius : CGFloat = 8.0
27+
private let padding : CGFloat = 20.0
28+
private let smallPadding : CGFloat = 10.0
29+
private let buttonHeight : CGFloat = 44.0
30+
private let headerHeight : CGFloat = 60.0
31+
32+
// MARK: - Instance Variables
33+
var titleLabel = UILabel()
34+
var proceedButton = ThemeButton()
35+
var footerButton = UIButton()
36+
37+
override func viewDidLoad() {
38+
super.viewDidLoad()
39+
40+
Theme.shared.register(client: self)
41+
42+
ReleaseNotesDatasource.setUserPreferenceValue(NSString(utf8String: VendorServices.shared.appVersion), forClassSettingsKey: .lastSeenReleaseNotesVersion)
43+
44+
let headerView = UIView()
45+
headerView.backgroundColor = .clear
46+
headerView.translatesAutoresizingMaskIntoConstraints = false
47+
view.addSubview(headerView)
48+
NSLayoutConstraint.activate([
49+
headerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
50+
headerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
51+
headerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
52+
headerView.heightAnchor.constraint(equalToConstant: headerHeight)
53+
])
54+
55+
titleLabel.translatesAutoresizingMaskIntoConstraints = false
56+
titleLabel.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal)
57+
58+
titleLabel.text = "New in ownCloud".localized
59+
titleLabel.textAlignment = .center
60+
titleLabel.numberOfLines = 0
61+
titleLabel.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.headline)
62+
titleLabel.adjustsFontForContentSizeCategory = true
63+
headerView.addSubview(titleLabel)
64+
65+
NSLayoutConstraint.activate([
66+
titleLabel.leftAnchor.constraint(greaterThanOrEqualTo: headerView.safeAreaLayoutGuide.leftAnchor, constant: padding),
67+
titleLabel.rightAnchor.constraint(lessThanOrEqualTo: headerView.safeAreaLayoutGuide.rightAnchor, constant: padding * -1),
68+
titleLabel.centerXAnchor.constraint(equalTo: headerView.safeAreaLayoutGuide.centerXAnchor),
69+
70+
titleLabel.topAnchor.constraint(equalTo: headerView.safeAreaLayoutGuide.topAnchor, constant: padding)
71+
])
72+
73+
let releaseNotesController = ReleaseNotesTableViewController(style: .plain)
74+
if let containerView = releaseNotesController.view {
75+
containerView.backgroundColor = .clear
76+
containerView.translatesAutoresizingMaskIntoConstraints = false
77+
view.addSubview(containerView)
78+
79+
let bottomView = UIView()
80+
bottomView.backgroundColor = .clear
81+
bottomView.translatesAutoresizingMaskIntoConstraints = false
82+
view.addSubview(bottomView)
83+
NSLayoutConstraint.activate([
84+
bottomView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
85+
bottomView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
86+
bottomView.topAnchor.constraint(equalTo: containerView.bottomAnchor),
87+
bottomView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
88+
])
89+
90+
proceedButton.setTitle("Proceed".localized, for: .normal)
91+
proceedButton.translatesAutoresizingMaskIntoConstraints = false
92+
proceedButton.addTarget(self, action: #selector(dismissView), for: .touchUpInside)
93+
bottomView.addSubview(proceedButton)
94+
95+
let appName = OCAppIdentity.shared.appName ?? "ownCloud"
96+
footerButton.setTitle(String(format:"Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️".localized, appName), for: .normal)
97+
footerButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.footnote)
98+
footerButton.titleLabel?.adjustsFontForContentSizeCategory = true
99+
footerButton.titleLabel?.numberOfLines = 0
100+
footerButton.titleLabel?.textAlignment = .center
101+
footerButton.translatesAutoresizingMaskIntoConstraints = false
102+
footerButton.addTarget(self, action: #selector(rateApp), for: .touchUpInside)
103+
bottomView.addSubview(footerButton)
104+
105+
NSLayoutConstraint.activate([
106+
footerButton.leadingAnchor.constraint(equalTo: bottomView.leadingAnchor, constant: padding),
107+
footerButton.trailingAnchor.constraint(equalTo: bottomView.trailingAnchor, constant: padding * -1),
108+
footerButton.topAnchor.constraint(equalTo: bottomView.topAnchor, constant: smallPadding),
109+
footerButton.bottomAnchor.constraint(equalTo: proceedButton.topAnchor, constant: padding * -1)
110+
])
111+
112+
NSLayoutConstraint.activate([
113+
proceedButton.leadingAnchor.constraint(equalTo: bottomView.leadingAnchor, constant: padding),
114+
proceedButton.trailingAnchor.constraint(equalTo: bottomView.trailingAnchor, constant: padding * -1),
115+
proceedButton.heightAnchor.constraint(equalToConstant: buttonHeight),
116+
proceedButton.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor, constant: smallPadding * -1)
117+
])
118+
119+
NSLayoutConstraint.activate([
120+
containerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
121+
containerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
122+
containerView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
123+
containerView.bottomAnchor.constraint(equalTo: bottomView.topAnchor)
124+
])
125+
}
126+
}
127+
128+
deinit {
129+
Theme.shared.unregister(client: self)
130+
}
131+
132+
@objc func dismissView() {
133+
self.dismiss(animated: true, completion: nil)
134+
}
135+
136+
@objc func rateApp() {
137+
SKStoreReviewController.requestReview()
138+
}
139+
}
140+
141+
// MARK: - Themeable implementation
142+
extension ReleaseNotesHostViewController : Themeable {
143+
func applyThemeCollection(theme: Theme, collection: ThemeCollection, event: ThemeEvent) {
144+
145+
self.view.backgroundColor = collection.tableBackgroundColor
146+
titleLabel.applyThemeCollection(collection, itemStyle: .logo)
147+
proceedButton.backgroundColor = collection.neutralColors.normal.background
148+
proceedButton.setTitleColor(collection.neutralColors.normal.foreground, for: .normal)
149+
footerButton.titleLabel?.textColor = collection.tableRowColors.labelColor
150+
}
151+
}
152+
153+
class ReleaseNotesDatasource : NSObject, OCClassSettingsUserPreferencesSupport {
154+
155+
var shouldShowReleaseNotes: Bool {
156+
if let lastSeenReleaseNotesVersion = self.classSetting(forOCClassSettingsKey: .lastSeenReleaseNotesVersion) as? String {
157+
158+
if lastSeenReleaseNotesVersion.compare(VendorServices.shared.appVersion, options: .numeric) == .orderedDescending || lastSeenReleaseNotesVersion.compare(VendorServices.shared.appVersion, options: .numeric) == .orderedSame {
159+
return false
160+
}
161+
162+
if let path = Bundle.main.path(forResource: "ReleaseNotes", ofType: "plist"), let releaseNotesValues = NSDictionary(contentsOfFile: path), let versionsValues = releaseNotesValues["Versions"] as? NSArray {
163+
164+
let relevantReleaseNotes = versionsValues.filter {
165+
if let version = ($0 as AnyObject)["Version"] as? String, version.compare(VendorServices.shared.appVersion, options: .numeric) == .orderedDescending {
166+
return false
167+
}
168+
169+
return true
170+
}
171+
172+
if relevantReleaseNotes.count > 0 {
173+
return true
174+
}
175+
}
176+
177+
return false
178+
} else if self.classSetting(forOCClassSettingsKey: .lastSeenAppVersion) != nil {
179+
if self.classSetting(forOCClassSettingsKey: .lastSeenAppVersion) as? String != VendorServices.shared.appVersion {
180+
return true
181+
}
182+
return false
183+
} else if OCBookmarkManager.shared.bookmarks.count > 0 {
184+
// Fallback, if app was previously installed, because we cannot check for an user defaults key, we have to check if accounts was previously configured
185+
return true
186+
}
187+
188+
return false
189+
}
190+
191+
func releaseNotes(for version: String) -> [[String:Any]]? {
192+
if let path = Bundle.main.path(forResource: "ReleaseNotes", ofType: "plist") {
193+
if let releaseNotesValues = NSDictionary(contentsOfFile: path), let versionsValues = releaseNotesValues["Versions"] as? NSArray {
194+
195+
let relevantReleaseNotes = versionsValues.filter {
196+
if let version = ($0 as AnyObject)["Version"] as? String, version.compare(VendorServices.shared.appVersion, options: .numeric) == .orderedAscending {
197+
return false
198+
}
199+
200+
return true
201+
}
202+
203+
return relevantReleaseNotes as? [[String:Any]]
204+
}
205+
}
206+
207+
return nil
208+
}
209+
}
210+
211+
extension OCClassSettingsKey {
212+
// Available since version 1.3.0
213+
static let lastSeenReleaseNotesVersion = OCClassSettingsKey("lastSeenReleaseNotesVersion")
214+
static let lastSeenAppVersion = OCClassSettingsKey("lastSeenAppVersion")
215+
}
216+
217+
extension ReleaseNotesDatasource : OCClassSettingsSupport {
218+
static let classSettingsIdentifier : OCClassSettingsIdentifier = .app
219+
220+
static func defaultSettings(forIdentifier identifier: OCClassSettingsIdentifier) -> [OCClassSettingsKey : Any]? {
221+
if identifier == .app {
222+
return nil
223+
}
224+
225+
return nil
226+
}
227+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// ReleaseNotesTableViewController.swift
3+
// ownCloud
4+
//
5+
// Created by Matthias Hühne on 09.10.19.
6+
// Copyright © 2019 ownCloud GmbH. All rights reserved.
7+
//
8+
9+
/*
10+
* Copyright (C) 2019, ownCloud GmbH.
11+
*
12+
* This code is covered by the GNU Public License Version 3.
13+
*
14+
* For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/
15+
* You should have received a copy of this license along with this program. If not, see <http://www.gnu.org/licenses/gpl-3.0.en.html>.
16+
*
17+
*/
18+
19+
import UIKit
20+
21+
class ReleaseNotesTableViewController: StaticTableViewController {
22+
23+
override func viewDidLoad() {
24+
super.viewDidLoad()
25+
26+
tableView.separatorColor = .clear
27+
prepareReleaseNotes()
28+
}
29+
30+
func prepareReleaseNotes() {
31+
if let relevantReleaseNotes = ReleaseNotesDatasource().releaseNotes(for: VendorServices.shared.appVersion) {
32+
let section = StaticTableViewSection()
33+
34+
for aDict in relevantReleaseNotes {
35+
if let notes = aDict["ReleaseNotes"] as? NSArray {
36+
for releaseNote in (notes as? [[String:String]])! {
37+
if let title = releaseNote["Title"], let subtitle = releaseNote["Subtitle"], let strBase64 = releaseNote["ImageData"] {
38+
let dataDecoded : Data = Data(base64Encoded: strBase64, options: .ignoreUnknownCharacters)!
39+
if let decodedimage = UIImage(data: dataDecoded)?.tinted(with: .white)?.scaledImageFitting(in: CGSize(width: 50.0, height: 44.0)) {
40+
let row = StaticTableViewRow(rowWithAction: { (_, _) in
41+
self.dismissAnimated()
42+
}, title: title, subtitle: subtitle, image: decodedimage, imageWidth:50.0, alignment: .left, accessoryType: .none)
43+
section.add(row: row)
44+
}
45+
}
46+
}
47+
}
48+
}
49+
self.addSection(section)
50+
}
51+
}
52+
53+
}

ownCloud/Resources/en.lproj/Localizable.strings

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,11 @@
489489
"Save File" = "Save File";
490490
"Choose an account and folder to import the file into.\n\nOnly one file can be imported at once." = "Choose an account and folder to import the file into.\n\nOnly one file can be imported at once.";
491491

492+
/* Release Notes */
493+
"Proceed" = "Proceed";
494+
"New in ownCloud" = "New in ownCloud";
495+
"Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️" = "Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️";
496+
492497
/* Key Commands */
493498
"Select Next" = "Select Next";
494499
"Select Previous" = "Select Previous";

ownCloud/Server List/ServerListTableViewController.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ class ServerListTableViewController: UITableViewController, Themeable {
9494
self.navigationItem.title = OCAppIdentity.shared.appName
9595

9696
NotificationCenter.default.addObserver(self, selector: #selector(considerAutoLogin), name: UIApplication.didBecomeActiveNotification, object: nil)
97+
98+
if ReleaseNotesDatasource().shouldShowReleaseNotes {
99+
let releaseNotesHostController = ReleaseNotesHostViewController()
100+
releaseNotesHostController.modalPresentationStyle = .formSheet
101+
self.present(releaseNotesHostController, animated: true, completion: nil)
102+
}
103+
104+
ReleaseNotesDatasource.setUserPreferenceValue(NSString(utf8String: VendorServices.shared.appVersion), forClassSettingsKey: .lastSeenAppVersion)
97105
}
98106

99107
override func viewWillAppear(_ animated: Bool) {

ownCloud/Theming/UI/ThemeButton.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class ThemeButton : UIButton {
8585
}
8686

8787
private func styleButton() {
88-
self.layer.cornerRadius = 5
88+
self.layer.cornerRadius = 8
8989
self.titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline)
9090
self.titleLabel?.adjustsFontForContentSizeCategory = true
9191
}

0 commit comments

Comments
 (0)