|
| 1 | +// Copyright 2019 The Flutter 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 | +/// A regular expression that matches against the "size directive" path |
| 6 | +/// segment of Google profile image URLs. |
| 7 | +/// |
| 8 | +/// The format is is "`/sNN-c/`", where `NN` is the max width/height of the |
| 9 | +/// image, and "`c`" indicates we want the image cropped. |
| 10 | +final RegExp sizeDirective = RegExp(r'^s[0-9]{1,5}(-c)?$'); |
| 11 | + |
| 12 | +/// Adds [size] (and crop) directive to [photoUrl]. |
| 13 | +/// |
| 14 | +/// There are two formats for photoUrls coming from the Sign In backend. |
| 15 | +/// |
| 16 | +/// The two formats can be told apart by the number of path segments in the |
| 17 | +/// URL (path segments: parts of the URL separated by slashes "/"): |
| 18 | +/// |
| 19 | +/// * If the URL has 2 or less path segments, it is a *new* style URL. |
| 20 | +/// * If the URL has more than 2 path segments, it is an old style URL. |
| 21 | +/// |
| 22 | +/// Old style URLs encode the image transformation directives as the last |
| 23 | +/// path segment. Look at the [sizeDirective] Regular Expression for more |
| 24 | +/// information about these URLs. |
| 25 | +/// |
| 26 | +/// New style URLs carry the same directives at the end of the URL, |
| 27 | +/// after an = sign, like: "`=s120-c-fSoften=1,50,0`". |
| 28 | +/// |
| 29 | +/// Directives may contain the "=" sign (`fSoften=1,50,0`), but it seems the |
| 30 | +/// base URL of the images don't. "Everything after the first = sign" is a |
| 31 | +/// good heuristic to split new style URLs. |
| 32 | +/// |
| 33 | +/// Each directive is separated from others by dashes. Directives are the same |
| 34 | +/// as described in the [sizeDirective] RegExp. |
| 35 | +/// |
| 36 | +/// Modified image URLs are recomposed by performing the parsing steps in reverse. |
| 37 | +String addSizeDirectiveToUrl(String photoUrl, double size) { |
| 38 | + final Uri profileUri = Uri.parse(photoUrl); |
| 39 | + final List<String> pathSegments = List<String>.from(profileUri.pathSegments); |
| 40 | + if (pathSegments.length <= 2) { |
| 41 | + final String imagePath = pathSegments.last; |
| 42 | + // Does this have any existing transformation directives? |
| 43 | + final int directiveSeparator = imagePath.indexOf('='); |
| 44 | + if (directiveSeparator >= 0) { |
| 45 | + // Split the baseUrl from the sizing directive by the first "=" |
| 46 | + final String baseUrl = imagePath.substring(0, directiveSeparator); |
| 47 | + final String directive = imagePath.substring(directiveSeparator + 1); |
| 48 | + // Split the directive by "-" |
| 49 | + final Set<String> directives = Set<String>.from(directive.split('-')) |
| 50 | + // Remove the size directive, if present, and any empty values |
| 51 | + ..removeWhere((String s) => s.isEmpty || sizeDirective.hasMatch(s)) |
| 52 | + // Add the size and crop directives |
| 53 | + ..addAll(<String>['c', 's${size.round()}']); |
| 54 | + // Recompose the URL by performing the reverse of the parsing |
| 55 | + pathSegments.last = '$baseUrl=${directives.join("-")}'; |
| 56 | + } else { |
| 57 | + pathSegments.last = '${pathSegments.last}=c-s${size.round()}'; |
| 58 | + } |
| 59 | + } else { |
| 60 | + // Old style URLs |
| 61 | + pathSegments |
| 62 | + ..removeWhere(sizeDirective.hasMatch) |
| 63 | + ..insert(pathSegments.length - 1, 's${size.round()}-c'); |
| 64 | + } |
| 65 | + return Uri( |
| 66 | + scheme: profileUri.scheme, |
| 67 | + host: profileUri.host, |
| 68 | + pathSegments: pathSegments, |
| 69 | + ).toString(); |
| 70 | +} |
0 commit comments