@Override
  public void onScrollChanged(int deltaX, int deltaY) {
    // Reposition the header bar -- it's normally anchored to the top of the content,
    // but locks to the top of the screen on scroll
    int scrollY = mScrollView.getScrollY();

    float newTop = Math.max(mPhotoHeightPixels, scrollY);
    mHeaderBox.setTranslationY(newTop);
    mAddScheduleButtonContainer.setTranslationY(
        newTop + mHeaderHeightPixels - mAddScheduleButtonContainerHeightPixels / 2);

    float gapFillProgress = 1;
    if (mPhotoHeightPixels != 0) {
      gapFillProgress =
          Math.min(Math.max(UIUtils.getProgress(scrollY, 0, mPhotoHeightPixels), 0), 1);
    }

    ViewCompat.setElevation(mHeaderBox, gapFillProgress * mMaxHeaderElevation);
    ViewCompat.setElevation(
        mAddScheduleButtonContainer, gapFillProgress * mMaxHeaderElevation + mFABElevation);
    ViewCompat.setElevation(
        mAddScheduleButton, gapFillProgress * mMaxHeaderElevation + mFABElevation);

    // Move background photo (parallax effect)
    mPhotoViewContainer.setTranslationY(scrollY * 0.5f);
  }
Example #2
0
 @Override
 protected void onResume() {
   super.onResume();
   int actionBarSize = UIUtils.calculateActionBarSize(this);
   DrawShadowFrameLayout drawShadowFrameLayout =
       (DrawShadowFrameLayout) findViewById(R.id.main_content);
   if (drawShadowFrameLayout != null) {
     drawShadowFrameLayout.setShadowTopOffset(actionBarSize);
   }
   setContentTopClearance(actionBarSize);
 }
Example #3
0
  @Override
  public void onResume() {
    super.onResume();

    // configure fragment's top clearance to take our overlaid controls (Action Bar
    // and spinner box) into account.
    int actionBarSize = UIUtils.calculateActionBarSize(getActivity());
    DrawShadowFrameLayout drawShadowFrameLayout =
        (DrawShadowFrameLayout) getActivity().findViewById(R.id.main_content);
    if (drawShadowFrameLayout != null) {
      drawShadowFrameLayout.setShadowTopOffset(actionBarSize);
    }
    setContentTopClearance(
        actionBarSize + getResources().getDimensionPixelSize(R.dimen.explore_grid_padding));
  }
Example #4
0
  private void processCommand(Context context, NotificationCommandModel command) {
    // Check format
    if (!"1.0.00".equals(command.format)) {
      LOGW(TAG, "GCM notification command has unrecognized format: " + command.format);
      return;
    }

    // Check app version
    if (!TextUtils.isEmpty(command.minVersion) || !TextUtils.isEmpty(command.maxVersion)) {
      LOGD(TAG, "Command has version range.");
      int minVersion = 0;
      int maxVersion = Integer.MAX_VALUE;
      try {
        if (!TextUtils.isEmpty(command.minVersion)) {
          minVersion = Integer.parseInt(command.minVersion);
        }
        if (!TextUtils.isEmpty(command.maxVersion)) {
          maxVersion = Integer.parseInt(command.maxVersion);
        }
        LOGD(TAG, "Version range: " + minVersion + " - " + maxVersion);
        PackageInfo pinfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
        LOGD(TAG, "My version code: " + pinfo.versionCode);
        if (pinfo.versionCode < minVersion) {
          LOGD(
              TAG,
              "Skipping command because our version is too old, "
                  + pinfo.versionCode
                  + " < "
                  + minVersion);
          return;
        }
        if (pinfo.versionCode > maxVersion) {
          LOGD(
              TAG,
              "Skipping command because our version is too new, "
                  + pinfo.versionCode
                  + " > "
                  + maxVersion);
          return;
        }
      } catch (NumberFormatException ex) {
        LOGE(
            TAG,
            "Version spec badly formatted: min="
                + command.minVersion
                + ", max="
                + command.maxVersion);
        return;
      } catch (Exception ex) {
        LOGE(TAG, "Unexpected problem doing version check.", ex);
        return;
      }
    }

    // Check if we are the right audience
    LOGD(TAG, "Checking audience: " + command.audience);
    if ("remote".equals(command.audience)) {
      if (PrefUtils.isAttendeeAtVenue(context)) {
        LOGD(TAG, "Ignoring notification because audience is remote and attendee is on-site");
        return;
      } else {
        LOGD(TAG, "Relevant (attendee is remote).");
      }
    } else if ("local".equals(command.audience)) {
      if (!PrefUtils.isAttendeeAtVenue(context)) {
        LOGD(TAG, "Ignoring notification because audience is on-site and attendee is remote.");
        return;
      } else {
        LOGD(TAG, "Relevant (attendee is local).");
      }
    } else if ("all".equals(command.audience)) {
      LOGD(TAG, "Relevant (audience is 'all').");
    } else {
      LOGE(TAG, "Invalid audience on GCM notification command: " + command.audience);
      return;
    }

    // Check if it expired
    Date expiry = command.expiry == null ? null : TimeUtils.parseTimestamp(command.expiry);
    if (expiry == null) {
      LOGW(TAG, "Failed to parse expiry field of GCM notification command: " + command.expiry);
      return;
    } else if (expiry.getTime() < UIUtils.getCurrentTime(context)) {
      LOGW(TAG, "Got expired GCM notification command. Expiry: " + expiry.toString());
      return;
    } else {
      LOGD(TAG, "Message is still valid (expiry is in the future: " + expiry.toString() + ")");
    }

    // decide the intent that will be fired when the user clicks the notification
    Intent intent;
    if (TextUtils.isEmpty(command.dialogText)) {
      // notification leads directly to the URL, no dialog
      if (TextUtils.isEmpty(command.url)) {
        intent =
            new Intent(context, MyScheduleActivity.class)
                .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
      } else {
        intent = new Intent(Intent.ACTION_VIEW, Uri.parse(command.url));
      }
    } else {
      // use a dialog
      intent =
          new Intent(context, MyScheduleActivity.class)
              .setFlags(
                  Intent.FLAG_ACTIVITY_CLEAR_TOP
                      | Intent.FLAG_ACTIVITY_SINGLE_TOP
                      | Intent.FLAG_ACTIVITY_NEW_TASK
                      | Intent.FLAG_ACTIVITY_CLEAR_TASK);
      intent.putExtra(
          MyScheduleActivity.EXTRA_DIALOG_TITLE,
          command.dialogTitle == null ? "" : command.dialogTitle);
      intent.putExtra(
          MyScheduleActivity.EXTRA_DIALOG_MESSAGE,
          command.dialogText == null ? "" : command.dialogText);
      intent.putExtra(
          MyScheduleActivity.EXTRA_DIALOG_YES,
          command.dialogYes == null ? "OK" : command.dialogYes);
      intent.putExtra(
          MyScheduleActivity.EXTRA_DIALOG_NO, command.dialogNo == null ? "" : command.dialogNo);
      intent.putExtra(MyScheduleActivity.EXTRA_DIALOG_URL, command.url == null ? "" : command.url);
    }

    final String title =
        TextUtils.isEmpty(command.title) ? context.getString(R.string.app_name) : command.title;
    final String message = TextUtils.isEmpty(command.message) ? "" : command.message;

    // fire the notification
    ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
        .notify(
            0,
            new NotificationCompat.Builder(context)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.ic_stat_notification)
                .setTicker(command.message)
                .setContentTitle(title)
                .setContentText(message)
                // .setColor(context.getResources().getColor(R.color.theme_primary))
                // Note: setColor() is available in the support lib v21+.
                // We commented it out because we want the source to compile
                // against support lib v20. If you are using support lib
                // v21 or above on Android L, uncomment this line.
                .setContentIntent(
                    PendingIntent.getActivity(
                        context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT))
                .setAutoCancel(true)
                .build());
  }
  private void displaySpeakersData(SessionDetailModel data) {
    final ViewGroup speakersGroup =
        (ViewGroup) getActivity().findViewById(R.id.session_speakers_block);

    // Remove all existing speakers (everything but first child, which is the header)
    for (int i = speakersGroup.getChildCount() - 1; i >= 1; i--) {
      speakersGroup.removeViewAt(i);
    }

    final LayoutInflater inflater = getActivity().getLayoutInflater();

    boolean hasSpeakers = false;

    List<SessionDetailModel.Speaker> speakers = data.getSpeakers();

    for (final SessionDetailModel.Speaker speaker : speakers) {

      String speakerHeader = speaker.getName();
      if (!TextUtils.isEmpty(speaker.getCompany())) {
        speakerHeader += ", " + speaker.getCompany();
      }

      final View speakerView = inflater.inflate(R.layout.speaker_detail, speakersGroup, false);
      final TextView speakerHeaderView = (TextView) speakerView.findViewById(R.id.speaker_header);
      final ImageView speakerImageView = (ImageView) speakerView.findViewById(R.id.speaker_image);
      final TextView speakerAbstractView =
          (TextView) speakerView.findViewById(R.id.speaker_abstract);
      final ImageView plusOneIcon = (ImageView) speakerView.findViewById(R.id.gplus_icon_box);
      final ImageView twitterIcon = (ImageView) speakerView.findViewById(R.id.twitter_icon_box);

      setUpSpeakerSocialIcon(
          speaker,
          twitterIcon,
          speaker.getTwitterUrl(),
          UIUtils.TWITTER_COMMON_NAME,
          UIUtils.TWITTER_PACKAGE_NAME);

      setUpSpeakerSocialIcon(
          speaker,
          plusOneIcon,
          speaker.getPlusoneUrl(),
          UIUtils.GOOGLE_PLUS_COMMON_NAME,
          UIUtils.GOOGLE_PLUS_PACKAGE_NAME);

      // A speaker may have both a Twitter and GPlus page, only a Twitter page or only a
      // GPlus page, or neither. By default, align the Twitter icon to the right and the GPlus
      // icon to its left. If only a single icon is displayed, align it to the right.
      determineSocialIconPlacement(plusOneIcon, twitterIcon);

      if (!TextUtils.isEmpty(speaker.getImageUrl()) && mSpeakersImageLoader != null) {
        mSpeakersImageLoader.loadImage(speaker.getImageUrl(), speakerImageView);
      }

      speakerHeaderView.setText(speakerHeader);
      speakerImageView.setContentDescription(
          getString(R.string.speaker_googleplus_profile, speakerHeader));
      UIUtils.setTextMaybeHtml(speakerAbstractView, speaker.getAbstract());

      if (!TextUtils.isEmpty(speaker.getUrl())) {
        speakerImageView.setEnabled(true);
        speakerImageView.setOnClickListener(
            new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                Intent speakerProfileIntent =
                    new Intent(Intent.ACTION_VIEW, Uri.parse(speaker.getUrl()));
                speakerProfileIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                UIUtils.preferPackageForIntent(
                    getActivity(), speakerProfileIntent, UIUtils.GOOGLE_PLUS_PACKAGE_NAME);
                startActivity(speakerProfileIntent);
              }
            });
      } else {
        speakerImageView.setEnabled(false);
        speakerImageView.setOnClickListener(null);
      }

      speakersGroup.addView(speakerView);
      hasSpeakers = true;
    }

    speakersGroup.setVisibility(hasSpeakers ? View.VISIBLE : View.GONE);
    updateEmptyView(data);
  }
  private void displaySessionData(final SessionDetailModel data) {
    mTitle.setText(data.getSessionTitle());
    mSubtitle.setText(data.getSessionSubtitle());

    mPhotoViewContainer.setBackgroundColor(
        UIUtils.scaleSessionColorToDefaultBG(data.getSessionColor()));

    if (data.hasPhotoUrl()) {
      mHasPhoto = true;
      mNoPlaceholderImageLoader.loadImage(
          data.getPhotoUrl(),
          mPhotoView,
          new RequestListener<String, Bitmap>() {
            @Override
            public boolean onException(
                Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {
              mHasPhoto = false;
              recomputePhotoAndScrollingMetrics();
              return false;
            }

            @Override
            public boolean onResourceReady(
                Bitmap resource,
                String model,
                Target<Bitmap> target,
                boolean isFromMemoryCache,
                boolean isFirstResource) {
              // Trigger image transition
              recomputePhotoAndScrollingMetrics();
              return false;
            }
          });
      recomputePhotoAndScrollingMetrics();
    } else {
      mHasPhoto = false;
      recomputePhotoAndScrollingMetrics();
    }

    tryExecuteDeferredUiOperations();

    // Handle Keynote as a special case, where the user cannot remove it
    // from the schedule (it is auto added to schedule on sync)
    mAddScheduleButton.setVisibility(
        (AccountUtils.hasActiveAccount(getContext()) && !data.isKeynote())
            ? View.VISIBLE
            : View.INVISIBLE);

    displayTags(data);

    if (!data.isKeynote()) {
      showStarredDeferred(data.isInSchedule(), false);
    }

    if (!TextUtils.isEmpty(data.getSessionAbstract())) {
      UIUtils.setTextMaybeHtml(mAbstract, data.getSessionAbstract());
      mAbstract.setVisibility(View.VISIBLE);
    } else {
      mAbstract.setVisibility(View.GONE);
    }

    // Build requirements section
    final View requirementsBlock = getActivity().findViewById(R.id.session_requirements_block);
    final String sessionRequirements = data.getRequirements();
    if (!TextUtils.isEmpty(sessionRequirements)) {
      UIUtils.setTextMaybeHtml(mRequirements, sessionRequirements);
      requirementsBlock.setVisibility(View.VISIBLE);
    } else {
      requirementsBlock.setVisibility(View.GONE);
    }

    final ViewGroup relatedVideosBlock =
        (ViewGroup) getActivity().findViewById(R.id.related_videos_block);
    relatedVideosBlock.setVisibility(View.GONE);

    updateEmptyView(data);

    updateTimeBasedUi(data);

    if (data.getLiveStreamVideoWatched()) {
      mPhotoView.setColorFilter(getContext().getResources().getColor(R.color.video_scrim_watched));
      mLiveStreamPlayIconAndText.setText(getString(R.string.session_replay));
    }

    if (data.hasLiveStream()) {
      mLiveStreamPlayIconAndText.setOnClickListener(
          new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              String videoId =
                  YouTubeUtils.getVideoIdFromSessionData(
                      data.getYouTubeUrl(), data.getLiveStreamId());
              YouTubeUtils.showYouTubeVideo(videoId, getActivity());
            }
          });
    }

    fireAnalyticsScreenView(data.getSessionTitle());

    mHandler.post(
        new Runnable() {
          @Override
          public void run() {
            onScrollChanged(0, 0); // trigger scroll handling
            mScrollViewChild.setVisibility(View.VISIBLE);
            // mAbstract.setTextIsSelectable(true);
          }
        });

    mTimeHintUpdaterRunnable =
        new Runnable() {
          @Override
          public void run() {
            if (getActivity() == null) {
              // Do not post a delayed message if the activity is detached.
              return;
            }
            updateTimeBasedUi(data);
            mHandler.postDelayed(
                mTimeHintUpdaterRunnable, SessionDetailConstants.TIME_HINT_UPDATE_INTERVAL);
          }
        };
    mHandler.postDelayed(
        mTimeHintUpdaterRunnable, SessionDetailConstants.TIME_HINT_UPDATE_INTERVAL);
  }