Записи с пометкой ·

Delphi

·...

Delphi Cookbook — Second Edition

Где-то ближе к концу весны запланирован выход второго издания книги Delphi Cookbook (уже доступен предзаказ). Мне посчастливилось эту книгу рецензировать. Это был очень интересный процесс, удалось заглянуть одним глазком за кулисы книгоиздания.

В общем, я с большим удовольствием ее прочитал.

Как любая поваренная книга («cookbook» все-таки), эта книга представляет собой сборник рецептов. Рецепты совершенно разнообразные — от использования стилей в VCL, до рисования нестандартных вещей в FMX ListView, от примеров функционального программирования в Delphi до импорта Java-библиотек на андроиде. Более 60 рецептов, если верить обложке :)

Лично меня эта книга заинтересовала в первую очередь не столько сутью самих рецептов, сколько тем фактом, что автор для решения поставленных задач использует все нововведения Delphi, какие только возможно. За этим очень интересно наблюдать. Delphi в последние годы развивается быстро и не всегда отдаешь себе отчет в том, сколько же всего нового появилось в свежих версиях.

Поэтому я рекомендую Delphi Cookbook в первую очередь тем, кто все еще работает со старыми версиям Delphi и хочет поближе приглядеться к нововведениям. Хотя и те, кто работает с новыми версиями, тоже найдут, чем расширить свой кругозор.

Хороших книг о Delphi в последнее время довольно мало издается. Так что новость довольно позитивная, на мой взгляд.

Сокращение в московском офисе Embarcadero

Сегодня в московском офисе Embarcadero произошло сокращение. Офис покинули Денис Васильев (менеджер по продуктам RAD Studio), Сергей Терлецкий (менеджер по образованию), Алексей Голопяткин (менеджер по СНГ), Екатерина Макарова (маркетинг). То есть 50% работавших в московском офисе.

Можно считать это первыми действиями нового менеджмента из Idera. К лучшему оно или нет, мы узнаем потом. Но что будет с презентациями Delphi, вебинарами и т.п. на русском языке теперь совсем непонятно.

Пожелаем Денису, Сергею, Алексею и Екатерине удачи в дальнейшей карьере!

FixInsight & Parnassus Navigator

Хочу анонсировать новогоднее предложение.

В декабре два плагина для Delphi — FixInsight и Parnassus Navigator — можно получить по цене одного. Про Parnassus Navigator я уже писал. Очень интересная штука, действительно упрощает повседневную жизнь. Ускоряет навигацию в коде.

С учетом того, что у FixInsight для России специальная цена, все это вместе делает новогоднее предложение особенно выгодным. Вряд ли такое можно встретить где-то еще. Ссылка вот: http://sourceoddity.com/fixinsight/order.html

С наступающим поздравлять пока рано! НО. Но вы можете поздравить меня с днем рожденья :)

Вебинар об FixInsight

Сегодня состоялся вебинар о статическом анализе и об FixInsight в частности. У меня не большой опыт подобных выступлений, но вроде бы получилось неплохо. Хотя звук получился так себе.

Посмотреть запись можно на ютубе:

А сама презентация вот:

Parnassus Navigator

Товарищ Дэвид Миллингтон написал крутой новый плагин для Delphi. Скачать его можно тут, а в блоге автора есть подробное описание. Я не буду его пересказывать, я буду краток.

Во-первых, Navigator позволяет быстро перемещаться по модулю с помощью нового поискового окна. Раньше я постоянно нажимал CTRL+F, вводил текст, а потом F3 F3 F3… А кто так не делает? С навигатором это делать намного проще.

То есть можно просто ввести фрагменты слов и получить полный список подходящих идентификаторов в текущем модуле. Это могут быть не только методы и процедуры, но и сами классы, свойства классов, секции uses и т.п. Вся структура модуля как на ладони.

Во-вторых, там есть крутая миникарта, которая тоже помогает перемещаться по коду чуть быстрее.

В-третьих, лично мне особенно приятно то, что это еще один интересный проект использующий мой DelphiAST. Оказывается, многим не хватало хорошего парсера.

Как вам? Интересно, чего вам не достает в Delphi IDE из того, что уже есть в других средах разработки?

FixInsight vs FMX

В этом посте я продолжу анализ библиотек поставляемых с Delphi. В предыдущих сериях я рассмотрел Delphi VCL и RTL. Как обычно я постараюсь найти все самое интересное и пропущу разнообразные мелочи, иначе этот пост получится слишком длинным. Все примеры кода в этом посте касаются Delphi XE8 (версия 22.0.19027.8951).

W517 Variable hides a class field, method or property

procedure TLight.ReadDiffuse(Reader: TReader);
var
  Color: Integer;
begin
  IdentToAlphaColor(Reader.ReadIdent, Color);
  {$R-}
  Color := TAlphaColor(Color);
  {$R+}
end;

И то же самое в другом модуле.

procedure TStrokeCube.ReadDiffuse(Reader: TReader);
var
  Color: Integer;
begin
  IdentToAlphaColor(Reader.ReadIdent, Color);
  {$R-}
  Color := TAlphaColor(Color);
  {$R+}
end;

Это одно из тех многочисленных предупреждений «переменная скрывает поле класса», о которых я говорил в предыдущем посте. В этом случае мы действительно видим баг. Классы TLight и TStrokeCube имеют свойство Color (типа TAlphaColor, конечно). Так что я предполагаю, что на самом деле имелось ввиду что-то вроде «Self.Color := TAlphaColor(Color)», но лучше просто переименовать переменную. Использовать одинаковые имена для переменных и полей класса — это очень плохой стиль. И Embarcadero, к сожалению, им злоупотребляет. Часто это совершенно не вредит, но иногда ведет к трудноотлавливаемым багам вроде этого. Просто посмотрите на этот код, запомните и никогда так не делайте. Никогда.

W511 Object created in TRY block

begin
  if ([csDesigning, csDestroying, csLoading, csUpdating] * ComponentState <> []) or
     (FUpdating > 0) then Exit;
  { Update objects in form }
  try
    Comparer := TComparerTFmxObject.Create;
    ClientList := TTObjInfoList.Create(Comparer);
    Bucket := TDictionary<TObject, TObject>.Create;
    for InitiatedCount := 0 to 7 do
    begin
      if CollectActionClients(ClientList) = 0 then
        Break;
      ClientList.Sort;
      for I := 0 to ClientList.Count - 1 do
        ClientList[I].ActionClient.InitiateAction;
      ClientList.Clear;
    end;
  finally
    FreeAndNil(ClientList);
    FreeAndNil(Bucket);
  end;
end;

Приложение может вызвать исключение до того как ссылка на экземпляр класса будет присвоена переменным ClientList и/или Bucket. Это значит, что в блоке finally может быть попытка освободить объекты, которые не создавались.

W523 Interface declared without a GUID

  IModelImporter = interface
    function GetDescription: string;
    function GetExt: string;

    function LoadFromFile(const AFileName: string; out AMesh: TMeshDynArray;
      const AOwner: TComponent): Boolean;
  end;
  IFMXUISwitch = interface(UISwitch)
    { Touches }
    procedure touchesBegan(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure touchesCancelled(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure touchesEnded(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure touchesMoved(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure ValueChanged; cdecl;
  end;

Не баг на самом деле, но так как интерфейсам не присвоен GUID, то они не могут быть использованы с функцией Supports или оператором As. Может быть стоит добавить GUID, почему нет?

W510 Values on both sides of the operator are equal

function SamePosition(const APosition1, APosition2: TPosition): Boolean; overload;
begin
  Result := (APosition1.X = APosition2.X) and (APosition1.Y = APosition1.Y);
end;

Предположу, что имелось ввиду «APosition1.Y = APosition2.Y».

W508 Variable is assigned twice successively

  FPixelShader := TShaderManager.RegisterShaderFromData('gouraud.fps', TContextShaderKind.PixelShader, '', [
    TContextShaderSource.Create(TContextShaderArch.DX9, [
      $00, $02, $FF, $FF, $FE, $FF, $32, $00, $43, $54, $41, $42, $1C, $00, $00, $00, $9F, $00, $00, $00, $00, $02, $FF, $FF, $03, $00, $00, $00, $1C, $00, $00, $00, $00, $01, $00, $20, $98, $00, $00, $00,
// skipped
  FPixelShader := TShaderManager.RegisterShaderFromData('gouraud.fps', TContextShaderKind.PixelShader, '', [
    TContextShaderSource.Create(TContextShaderArch.DX9, [
      $00, $02, $FF, $FF, $FE, $FF, $32, $00, $43, $54, $41, $42, $1C, $00, $00, $00, $9F, $00, $00, $00, $00, $02, $FF, $FF, $03, $00, $00, $00, $1C, $00, $00, $00, $00, $01, $00, $20, $98, $00, $00, $00,
// skipped

Два одинаковых присвоения одной переменной подряд. Вероятно не баг, просто неаккуратная копипаста.

W503 Assignment right hand side is equal to its left hand side

  if X1 > X2 then
    X1 := X1;
  if Y1 > Y2 then
    Y1 := Y2;

Предположу, что имелось ввиду «X1 := X2».

      if (Self.Form <> nil) and (Self.Form.Handle <> nil) then
        Self.Form := Self.Form;

Не знаю что здесь должно было быть (Self.Form здесь — это поле record’а).

И еще одно похожее место.

      if FNew.FFrequency <> 0 then
        FNew.FValue := Round(FNew.FValue / FNew.FFrequency) * FNew.FFrequency
      else
        FNew.FValue := FNew.FValue;

W510 Values on both sides of the operator are equal

            if RegionSize = RegionSize then
            begin
              SetLength(UpdateRects, RegionData.rdh.nCount);
              for i := 0 to RegionData.rdh.nCount - 1 do
              begin
                R := PRgnRects(@RegionData.buffer[0])[i];
                UpdateRects[i] := RectF(R.Left, R.Top, R.Right, R.Bottom);
              end;
            end;

Что здесь имелось ввиду?

W513 Format parameter count mismatch

function TCustomValueRange.GetNamePath: string;
begin
  Result := Format( 'Value: %0:*.*f (%1:*.*f..%2:*.*f)', [Value, Min, Max]);
end;

Строка форматирования неправильная. При выполнении этого кода будет сообщение об ошибке.

W505 Empty THEN block

      if FoundValue.Count > 1 then
      else if FoundValue.Count > 0 then
        PropValues[Name] := FoundValue[0];

Этот код очень странно выглядит. Вероятно его можно заменить на что-то такое: «If FoundValue.Count = 1 then PropValues[Name] := FoundValue[0]».

Ну вот и все на сегодня. Используйте FixInsight, чтобы найти баги до того как ваши пользователи их найдут :)

FixInsight vs RTL

В этом посте я продолжу анализ библиотек поставляемых с Delphi. В прошлый раз мы заглянули внутрь Delphi VCL, а в этот раз будем разглядывать RTL. Я снова пропущу всякие мелочи и постараюсь сосредоточиться на самом интересном. Все примеры кода в этом посте касаются Delphi XE7 (версия 21.0.17017.3725).

W503 Assignment right hand side is equal to its left hand side

  constructor TNativeDownloader.Create(PublicKey: string; Salt: array of Byte; MainFile, PatchFile: TApkFileInfo);
  begin
    FMainFile := MainFile;
    FPatchFile := FPatchFile;
    FDownloader := TJNativeDownloaderLauncher.JavaClass.init(StringToJString(PublicKey), ByteArrayToJArray(Salt),
      CreateApkList(MainFile, PatchFile));
    Create;
  end;

Я думаю, тут должно было быть «FPatchFile := PatchFile».

W501 Empty EXCEPT block

  try
    Assert(FConnections.Count = 0); // All connections should have been removed
  except
  end;

Это не ошибка, просто непонятная какая-то ерунда :)

W517 Variable hides a class field, method or property

procedure TTask.ExecuteReplicates(Root: TTask);
var
                                     
//  ReplicasQuitting: Boolean;
  ReplicaProc: TProc;
begin
//  ReplicasQuitting := False;
  ReplicaProc := procedure
    var
      CurrentTask, ChildTask: ITask;
    begin
      CurrentTask := CurrentTask;
      if not Root.ShouldCreateReplica {or ReplicasQuitting} then
        Exit;
      ChildTask := Root.CreateReplicaTask(ReplicaProc, Self, [TCreateFlag.Replicating, TCreateFlag.Replica]);
      ChildTask.Start;
      try
        Root.CallUserCode;
      except
        Root.HandleException(TTask(ChildTask), TObject(AcquireExceptionObject));
      end;
    end;
  ReplicaProc;
end;

Сообщение W517 довольно часто встречается при анализе и VCL, и RTL. Но это место, по-моему, самое интересное. В классе TTask есть метод под названием CurrentTask и переменная CurrentTask перекрывает этот метод. Кстати, тут на строке 1865 еще одно предупреждение: «W503 Assignment right hand side is equal to its left hand side».

То есть непонятно что имелось ввиду «CurrentTask := Self.CurrentTask» или просто эта переменная там без какого-то смысла? В любом случае код довольно странно выглядит и достоин того, чтобы на него внимательно взглянуть.

Я не могу процитировать все сообщений W517, их слишком много. Но давайте вернемся к VCL и взглянем на еще один фрагмент.

procedure TCustomTaskDialog.ShowHelpException(E: Exception);
var
  Msg: string;
  Flags: Integer;
  SubE: Exception;
begin
  Flags := MB_OK or MB_ICONSTOP;
  if Application.UseRightToLeftReading then
    Flags := Flags or MB_RTLREADING;
  // ... [skipped]

Это не баг. Но это запросто сможет стать багом, если код изменится. Например, этот код будет компилироваться даже если переменную Flags просто удалить (ведь в классе TCustomTaskDialog есть свойство с таким же именем). Или, например, можно легко ошибиться и использовать в этом методе переменную вместо одноименного свойства, либо наоборот.

W504 Missing INHERITED call in destructor

destructor TJavaImport.Destroy;
begin
  TJNIResolver.DeleteGlobalRef(GetObjectID);
end;

Снова немало сообщений о пропущенном «inherited» в конструкторе. Процитирую только один пример.

W508 Variable is assigned twice successively

      // Another thread could start traversing the list between when we set the
      // head to P and when we assign to P.Next.  Initializing P.Next to point
      // to itself will make others spin until we assign the tail to P.Next.
      P.Next := P;
      P.Next := PThreadInfo(AtomicExchange(Pointer(FHashTable[H]), Pointer(P)));

Насколько я понимаю, это не баг. Но это место достойно цитирования. Как-то я не задумывался, что с многопоточностью можно работать вот так. Очень интересно.

На сегодня у меня все. Если пост вам понравился, то дайте мне знать на какую библиотеку или фреймворк мне стоит взглянуть с помощью FixInsight в следующий раз. FMX? JEDI? Что-то еще?

FixInsight vs VCL

Я конечно не мог пройти мимо и не проанализировать помощью FixInsight родной код Delphi. Все написанное будет касаться Delphi XE7 (точный номер версии 21.0.17017.3725). Я попытаюсь выделить что-нибудь интересное и проигнорирую мелкие проблемы вроде слишком длинных методов или вложенных операторов with (Вы когда-нибудь задумывались, сколько раз в коде VCL встречаются вложенные with? Вот лучше и не думайте никогда).

[FixInsight Warning] Vcl.RibbonStyleActnCtrls.pas(809): W512 Odd ELSE-IF condition (review lines 809 and 811)

    if Element = sbGroupStartInactive then
      LSrcRect := System.Types.Rect(0, 110, 22 - 2, 132)
    else if Element = sbGroupEndInactive then
      LSrcRect := System.Types.Rect(22, 110, 46 - 2, 132)
    else if Element = sbGroupEndInactive then
      LSrcRect := System.Types.Rect(47, 110, 70 - 2, 132)
    else
      LSrcRect := System.Types.Rect(71, 110, 95 - 2, 132);
    DrawButton(RibbonSkin.Bitmap, LSrcRect, Canvas, Rect, 2, 2, 255);

Обратите внимание на строки 809 и 811. Условия в if одинаковые. Здесь явно что-то не так. Попробуем исправить?

Возьмем кусочек побольше, посмотрим как выглядит код вокруг.

  else if Element in [sbGroupStartInactive, sbGroupMiddleInactive,
    sbGroupEndInactive, sbGroupSingleInactive] then
  begin
    if Element = sbGroupStartInactive then
      LSrcRect := System.Types.Rect(0, 110, 22, 132)
    else if Element = sbGroupMiddleInactive then
      LSrcRect := System.Types.Rect(22, 110, 46, 132)
    else if Element = sbGroupEndInactive then
      LSrcRect := System.Types.Rect(47, 110, 70, 132)
    else
      LSrcRect := System.Types.Rect(71, 110, 95, 132);
    DrawButton(RibbonSkin.Bitmap, LSrcRect, Canvas, Rect, 2, 2, 255);
  end
  else if Element in [sbGroupStartSplitInactive, sbGroupMiddleSplitInactive,
    sbGroupEndSplitInactive, sbGroupSingleSplitInactive] then
  begin
    if Element = sbGroupStartInactive then
      LSrcRect := System.Types.Rect(0, 110, 22 - 2, 132)
    else if Element = sbGroupEndInactive then
      LSrcRect := System.Types.Rect(22, 110, 46 - 2, 132)
    else if Element = sbGroupEndInactive then
      LSrcRect := System.Types.Rect(47, 110, 70 - 2, 132)
    else
      LSrcRect := System.Types.Rect(71, 110, 95 - 2, 132);
    DrawButton(RibbonSkin.Bitmap, LSrcRect, Canvas, Rect, 2, 2, 255);
  end
  else if Element in [sbSmallSplitHoverSplit, sbSmallSplitPressedSplit,
    sbSmallSplitInactiveSplit] then

Все еще интереснее. Вместе с условием «Element in [sbGroupStartSplitInactive, sbGroupMiddleSplitInactive, sbGroupEndSplitInactive, sbGroupSingleSplitInactive]» (строка 804), тот код, который мы нашли, совсем странно выглядит. Предположу, что это результат не очень внимательного копипаста фрагмента, который находится чуть выше (строки 794 — 802). Вероятно, на самом деле он должен был выглядеть примерно так:

  else if Element in [sbGroupStartSplitInactive, sbGroupMiddleSplitInactive,
    sbGroupEndSplitInactive, sbGroupSingleSplitInactive] then
  begin
    if Element = sbGroupStartSplitInactive then
      LSrcRect := System.Types.Rect(0, 110, 22 - 2, 132)
    else if Element = sbGroupMiddleSplitInactive then
      LSrcRect := System.Types.Rect(22, 110, 46 - 2, 132)
    else if Element = sbGroupEndSplitInactive then
      LSrcRect := System.Types.Rect(47, 110, 70 - 2, 132)
    else
      LSrcRect := System.Types.Rect(71, 110, 95 - 2, 132);
    DrawButton(RibbonSkin.Bitmap, LSrcRect, Canvas, Rect, 2, 2, 255);
  end

У меня не большой опыт работы с Ribbon-контролами, так что исправление предлагаю исключительно исходя из здравого смысла. Возможно, кому-то это удастся лучше.

Идем дальше.

[FixInsight Warning] Vcl.RibbonActnCtrls.pas(2642): W507 THEN statement is equal to ELSE statement

Опять Ribbon :)

procedure TRibbonDropDownButton.DrawBackground(var PaintRect: TRect);
var
  LPt: TPoint;
begin
  inherited;
  LPt := GetArrowPosition(PaintRect);
  if not Enabled then
    DrawArrow(LPt, clNone, clNone, -1)
  else
    DrawArrow(LPt, clNone, clNone, -1);
end;

Тут без комментариев. Очевидно, что код в then и else один и тот же. Что здесь подразумевалось, не понятно.

Аналогичный фрагмент в другом файле:

      if Assigned(Clients[I].ActionBar) then
      begin
        if Assigned(ActionProc) then
          ActionProc(Clients[I])
        else
          Clients[I].Refresh;
        UpdateActionBar(Clients[I]);
      end
      else
      begin
        if Assigned(ActionProc) then
          ActionProc(Clients[I])
        else
          Clients[I].Refresh;
        UpdateActionBar(Clients[I]);
      end;

И еще один:

        if Style <> csSimple then
          if DroppedDown then
            DrawFrameControl(C.Handle, R, DFC_SCROLL, DFCS_FLAT or DFCS_SCROLLCOMBOBOX)
          else
            DrawFrameControl(C.Handle, R, DFC_SCROLL, DFCS_FLAT or DFCS_SCROLLCOMBOBOX);

[FixInsight Warning] Vcl.Touch.Gestures.pas(1267): W503 Assignment right hand side is equal to its left hand side

function CheckPoint(const Point, Source: TPoint; Deviation,
  ErrorMargin: Integer): Double;
var
  Distance: Double;
  ErrorMarginDistance: Double;
  M, B: Double;
begin
  Deviation := Deviation;
  Distance := Sqrt(Sqr(Point.X - Source.X) + Sqr(Point.Y - Source.Y));
  ErrorMarginDistance := MulDiv(Deviation, ErrorMargin, 100);

То ли это присваивание на самом деле должно было выглядеть иначе, то ли это просто ненужный мусор.

Еще один аналогичный фрагмент:

procedure TScreenTipsWindow.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    Style := WS_POPUP;
    WindowClass.Style := WindowClass.Style;
    if NewStyleControls then
      ExStyle := WS_EX_TOOLWINDOW;
    if CheckWin32Version(5, 1) then
      WindowClass.Style := WindowClass.Style or CS_DROPSHADOW;
    if NewStyleControls then
      ExStyle := WS_EX_TOOLWINDOW;
    AddBiDiModeExStyle(ExStyle);
  end;
end;

[FixInsight Warning] Vcl.AxCtrls.pas(3070): W504 Missing INHERITED call in destructor

Подобных сообщений очень много, поэтому я не буду цитировать все.

destructor TActiveXPropertyPage.Destroy;
begin
  FPropertyPageImpl.FPropertyPage.Free;
  FPropertyPageImpl.Free;
end;

Нет inherited, деструктор предка не вызывается.

Ну вот, пожалуй, на этом закончим. Как я в начале сказал, не хочется придираться к мелочам. Пишу только про то, за что зацепился взгляд. В следующей серии, возможно, расскажу про FMX или RTL :)

Конкурс

Начался прием заявок на очередной конкурс Embarcadero про мобильную разработку. Я рекомендую поучаствовать, это весело. Особенно рекомендую тем, кто еще не пробовал ничего писать на Delphi для мобильных устройств. Гарантирую незабываемые ощущения.

В прошлом году мне даже удалось получить один из призов. В этом, к сожалению, такого шанса у меня не будет, потому что в этом году я буду в жюри. А телефон новый не помешал бы. Шутка.

Добавлю еще неофициальный приз от себя лично. Вручу лицензию FixInsight тому, чья работа мне понравится больше всех.

Самому FixInsight, кстати, недавно исполнился год. Примерно год назад я начал эту работу. Первый коммит в репозиторий датирован 7 марта 2014 года. Разработка была начата чуть раньше, но тем не менее предпочитаю считать эту дату днем рождения проекта. А потом уже от него отпочковался DelphiAST. Очень многое было сделано.

В общем, содержательно я провел прошлый год. А начался тот год именно с получения приза от Embarcadero. Вот так я замыкаю тему и возвращаюсь к разговору о конкурсе.

Удачи!

P.S. А в комментариях было бы интересно поговорить с теми, кто хочет или не хочет участвовать. И почему.

Google Maps в Delphi-приложении на Android

В связи со сменой работы теперь буду чаще сталкиваться с новыми веяниями вроде FireMonkey (FMX? FireUI? Как нынче правильно?) и мобильными приложениями. Это радует, слишком частое общение с Delphi 7 изрядно добавило мне седины :)

Так вот возникла необходимость использовать гугл-карты в приложении для Android. Судя по тому, что пишут в интернете, в Delphi-приложениях обычно ограничиваются использованием карт через TWebBrowser. Что логично и просто, но не очень красиво. Все-таки куда приятнее использовать родные для операционной системы элементы управления, если они есть.

В итоге родилось нечто под названием TDPFJMapView. Компонент оформлен как часть пакета D.P.F Delphi Android Native Components. Внутри у компонента совершенно обычный Java-класс MapView, ничего роднее для андроида и придумать нельзя.

Компонент умеет делать все самое необходимое — показывать карту по нужным координатам с нужным увеличением и расставлять на карте маркеры, в том числе нестандартные (например, на скриншоте маркеры нарисованы мной).

Но важно не забывать, что для работы с картами одного только компонента не достаточно, в манифесте приложения должен быть указан Google Maps API KEY. Без него вместо карты вы увидите серые квадраты, но к счастью получить его довольно просто. Так же не лишним будет правильно настроить разрешения.

P.S.
А еще у меня сегодня день рожденья. Принимаю поздравления.
Хороший повод купить FixInsight для тех, кто еще не успел :)