/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.talkback.tutorial;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toolbar;
import com.android.talkback.R;
import com.android.talkback.SpeechController;
import com.android.talkback.contextmenu.MenuActionInterceptor;
import com.android.talkback.contextmenu.MenuManager;
import com.android.talkback.contextmenu.MenuTransformer;
import com.android.talkback.controller.GestureActionMonitor;
import com.android.talkback.tutorial.exercise.Exercise;
import com.google.android.marvin.talkback.TalkBackService;
public class TutorialLessonFragment extends Fragment implements View.OnClickListener,
Exercise.ExerciseCallback, GestureActionMonitor.GestureActionListener {
private static final int DELAY_BEFORE_ANNOUNCE_LESSON = 100;
private static final int DELAY_BEFORE_AUTO_MOVE_TO_NEXT_LESSON = 1000;
private TutorialController mTutorialController;
private TutorialLesson mLesson;
private TutorialLessonPage mPage;
private Exercise mExercise;
private int mCurrentPage;
private TutorialNavigationCallback mCallback;
private TextView mDescription;
private SpeechController mSpeechController;
private GestureActionMonitor mActionMonitor = new GestureActionMonitor();
private Handler mHandler = new Handler(Looper.getMainLooper());
public void setLesson(TutorialLesson lesson, int currentPage) {
mLesson = lesson;
mPage = mLesson.getLessonPage(currentPage);
mCurrentPage = currentPage;
mExercise = mPage.getExercise();
}
public void setTutorialController(TutorialController tutorialController) {
mTutorialController = tutorialController;
}
public void setTutorialNavigationCallback(TutorialNavigationCallback callback) {
mCallback = callback;
}
@Override
public void onCreate(Bundle instance) {
super.onCreate(instance);
setRetainInstance(true);
setHasOptionsMenu(true);
mExercise.setExerciseCallBack(this);
TalkBackService service = TalkBackService.getInstance();
if (service != null) {
mSpeechController = service.getSpeechController();
}
mActionMonitor.setListener(this);
}
@Override
public void onStart() {
super.onStart();
MenuTransformer menuTransformer = mExercise.getContextMenuTransformer();
MenuActionInterceptor menuActionInterceptor = mExercise.getContextMenuActionInterceptor();
TalkBackService service = TalkBackService.getInstance();
if (service != null) {
MenuManager menuManager = service.getMenuManager();
menuManager.setMenuTransformer(menuTransformer);
menuManager.setMenuActionInterceptor(menuActionInterceptor);
}
// We need to post the announcements delayed in order to ensure that the view changed
// event gets sent beforehand. This makes the TalkBack speech response flow more logical.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
View view = getView();
if (view == null) {
// Something terrible has happened, e.g. the fragment is gone.
return;
}
view.announceForAccessibility(getTitle());
view.announceForAccessibility(mPage.getSubtitle());
mDescription.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
}
}, DELAY_BEFORE_ANNOUNCE_LESSON);
mExercise.onInitialized(getActivity());
}
@Override
public void onStop() {
super.onStop();
mExercise.clear();
TalkBackService service = TalkBackService.getInstance();
if (service != null) {
service.getMenuManager().setMenuTransformer(null);
service.getMenuManager().setMenuActionInterceptor(null);
service.getSpeechController().interrupt();
}
}
@Override
public void onResume() {
super.onResume();
Activity activity = getActivity();
if (activity != null) {
ActionBar actionBar = activity.getActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setCustomView(R.layout.tutorial_action_bar);
actionBar.getCustomView().findViewById(R.id.up).setOnClickListener(this);
TextView title = (TextView) actionBar.getCustomView().
findViewById(R.id.action_bar_title);
title.setText(getTitle());
LocalBroadcastManager.getInstance(activity).registerReceiver(mActionMonitor,
GestureActionMonitor.FILTER);
}
TalkBackService service = TalkBackService.getInstance();
if (service != null) {
service.addEventListener(mExercise);
}
}
private String getTitle() {
if (!TextUtils.isEmpty(mPage.getTitle())) {
return mPage.getTitle();
} else {
return mLesson.getTitle();
}
}
@Override
public void onPause() {
super.onPause();
TalkBackService service = TalkBackService.getInstance();
if (service != null) {
service.postRemoveEventListener(mExercise);
}
Activity activity = getActivity();
if (activity != null) {
LocalBroadcastManager.getInstance(activity).unregisterReceiver(mActionMonitor);
}
mHandler.removeCallbacksAndMessages(null);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
int layoutId;
if (mExercise.needScrollableContainer()) {
layoutId = R.layout.tutorial_lesson_fragment_scrollable;
} else {
layoutId = R.layout.tutorial_lesson_fragment;
}
View view = inflater.inflate(layoutId, container, false);
mDescription = (TextView) view.findViewById(R.id.description);
mDescription.setText(mPage.getDescription());
TextView subTitle = (TextView) view.findViewById(R.id.part_subtitle);
subTitle.setText(mPage.getSubtitle());
TextView currentPage = (TextView) view.findViewById(R.id.current_page);
TextView next = (TextView) view.findViewById(R.id.next);
if (mCurrentPage < mLesson.getPagesCount() - 1) {
next.setText(R.string.tutorial_next);
currentPage.setVisibility(View.VISIBLE);
currentPage.setText(getString(R.string.tutorial_page_number_of, mCurrentPage + 1,
mLesson.getPagesCount() - 1));
} else if (mTutorialController.getNextLesson(mLesson) == null) {
next.setText(R.string.tutorial_home);
} else {
next.setText(R.string.tutorial_next_lesson);
}
next.setOnClickListener(this);
View previous = view.findViewById(R.id.previous_page);
previous.setOnClickListener(this);
previous.setContentDescription(getString(R.string.tutorial_previous));
ViewGroup contentContainer = (ViewGroup) view.findViewById(R.id.practice_area);
View contentView = mPage.getExercise().getContentView(inflater, contentContainer);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
contentContainer.addView(contentView, params);
return view;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.next:
if (mCallback != null) {
mCallback.onNextPageClicked(mLesson, mCurrentPage);
}
break;
case R.id.previous_page:
if (mCallback != null) {
mCallback.onPreviousPageClicked(mLesson, mCurrentPage);
}
break;
case R.id.up:
mCallback.onNavigateUpClicked();
break;
}
}
@Override
public void onExerciseCompleted(boolean autoSwitchLesson, int completeMessageResId) {
if (mSpeechController == null) {
return;
}
SpeechController.UtteranceCompleteRunnable speechCallback = null;
if (autoSwitchLesson) {
speechCallback = new SpeechController.UtteranceCompleteRunnable() {
@Override
public void run(int status) {
moveToNextPageWithDelay(DELAY_BEFORE_AUTO_MOVE_TO_NEXT_LESSON);
}
};
}
notifyExerciseCompleted(speechCallback, completeMessageResId);
}
private void notifyExerciseCompleted(final SpeechController.UtteranceCompleteRunnable callback,
final int completeMessageResId) {
if (!isResumed()) {
return;
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mSpeechController.speak(getString(completeMessageResId), null,
null, SpeechController.QUEUE_MODE_UNINTERRUPTIBLE, 0, 0,
null, null, callback);
}
}, DELAY_BEFORE_AUTO_MOVE_TO_NEXT_LESSON);
}
private void moveToNextPageWithDelay(long delay) {
if (!isResumed()) {
return;
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (getActivity() != null) {
mCallback.onNextPageClicked(mLesson, mCurrentPage);
}
}
}, delay);
}
@Override
public void onGestureAction(String action) {
if (mExercise == null || TextUtils.isEmpty(action) || getActivity() == null) {
return;
}
mExercise.onAction(getActivity(), action);
}
}