diff options
Diffstat (limited to 'src/com/android/tv/dialog')
-rw-r--r-- | src/com/android/tv/dialog/DvrHistoryDialogFragment.java | 130 | ||||
-rw-r--r-- | src/com/android/tv/dialog/FullscreenDialogFragment.java | 2 | ||||
-rw-r--r-- | src/com/android/tv/dialog/HalfSizedDialogFragment.java | 123 | ||||
-rw-r--r-- | src/com/android/tv/dialog/PinDialogFragment.java | 103 | ||||
-rw-r--r-- | src/com/android/tv/dialog/SafeDismissDialogFragment.java | 26 | ||||
-rw-r--r-- | src/com/android/tv/dialog/WebDialogFragment.java | 17 |
6 files changed, 337 insertions, 64 deletions
diff --git a/src/com/android/tv/dialog/DvrHistoryDialogFragment.java b/src/com/android/tv/dialog/DvrHistoryDialogFragment.java new file mode 100644 index 00000000..d686e6e6 --- /dev/null +++ b/src/com/android/tv/dialog/DvrHistoryDialogFragment.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2016 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.tv.dialog; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.android.tv.ApplicationSingletons; +import com.android.tv.R; +import com.android.tv.TvApplication; +import com.android.tv.data.Channel; +import com.android.tv.data.ChannelDataManager; +import com.android.tv.dvr.DvrDataManager; +import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.data.ScheduledRecording.RecordingState; +import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.util.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Displays the DVR history. + */ +@TargetApi(VERSION_CODES.N) +public class DvrHistoryDialogFragment extends SafeDismissDialogFragment { + public static final String DIALOG_TAG = DvrHistoryDialogFragment.class.getSimpleName(); + + private static final String TRACKER_LABEL = "DVR history"; + private final List<ScheduledRecording> mSchedules = new ArrayList<>(); + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); + DvrDataManager dataManager = singletons.getDvrDataManager(); + ChannelDataManager channelDataManager = singletons.getChannelDataManager(); + for (ScheduledRecording schedule : dataManager.getAllScheduledRecordings()) { + if (!schedule.isInProgress() && !schedule.isNotStarted()) { + mSchedules.add(schedule); + } + } + mSchedules.sort(ScheduledRecording.START_TIME_COMPARATOR.reversed()); + LayoutInflater inflater = LayoutInflater.from(getContext()); + ArrayAdapter adapter = new ArrayAdapter<ScheduledRecording>(getContext(), + R.layout.list_item_dvr_history, ScheduledRecording.toArray(mSchedules)) { + @NonNull + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = inflater.inflate(R.layout.list_item_dvr_history, parent, false); + ScheduledRecording schedule = mSchedules.get(position); + setText(view, R.id.state, getStateString(schedule.getState())); + setText(view, R.id.schedule_time, getRecordingTimeText(schedule)); + setText(view, R.id.program_title, DvrUiHelper.getStyledTitleWithEpisodeNumber( + getContext(), schedule, 0)); + setText(view, R.id.channel_name, getChannelNameText(schedule)); + return view; + } + + private void setText(View view, int id, CharSequence text) { + ((TextView) view.findViewById(id)).setText(text); + } + + private void setText(View view, int id, int text) { + ((TextView) view.findViewById(id)).setText(text); + } + + @SuppressLint("SwitchIntDef") + private int getStateString(@RecordingState int state) { + switch (state) { + case ScheduledRecording.STATE_RECORDING_CLIPPED: + return R.string.dvr_history_dialog_state_clip; + case ScheduledRecording.STATE_RECORDING_FAILED: + return R.string.dvr_history_dialog_state_fail; + case ScheduledRecording.STATE_RECORDING_FINISHED: + return R.string.dvr_history_dialog_state_success; + default: + break; + } + return 0; + } + + private String getChannelNameText(ScheduledRecording schedule) { + Channel channel = channelDataManager.getChannel(schedule.getChannelId()); + return channel == null ? null : + TextUtils.isEmpty(channel.getDisplayName()) ? channel.getDisplayNumber() : + channel.getDisplayName().trim() + " " + channel.getDisplayNumber(); + } + + private String getRecordingTimeText(ScheduledRecording schedule) { + return Utils.getDurationString(getContext(), schedule.getStartTimeMs(), + schedule.getEndTimeMs(), true, true, true, 0); + } + }; + ListView listView = new ListView(getActivity()); + listView.setAdapter(adapter); + return new AlertDialog.Builder(getActivity()).setTitle(R.string.dvr_history_dialog_title) + .setView(listView).create(); + } + + @Override + public String getTrackerLabel() { + return TRACKER_LABEL; + } +} diff --git a/src/com/android/tv/dialog/FullscreenDialogFragment.java b/src/com/android/tv/dialog/FullscreenDialogFragment.java index d16202a1..d00422a7 100644 --- a/src/com/android/tv/dialog/FullscreenDialogFragment.java +++ b/src/com/android/tv/dialog/FullscreenDialogFragment.java @@ -77,7 +77,7 @@ public class FullscreenDialogFragment extends SafeDismissDialogFragment { return mTrackerLabel; } - private class FullscreenDialog extends TvDialog { + private class FullscreenDialog extends Dialog { public FullscreenDialog(Context context, int theme) { super(context, theme); } diff --git a/src/com/android/tv/dialog/HalfSizedDialogFragment.java b/src/com/android/tv/dialog/HalfSizedDialogFragment.java new file mode 100644 index 00000000..315c6a93 --- /dev/null +++ b/src/com/android/tv/dialog/HalfSizedDialogFragment.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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.tv.dialog; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Handler; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.tv.R; + +import java.util.concurrent.TimeUnit; + +public class HalfSizedDialogFragment extends SafeDismissDialogFragment { + public static final String DIALOG_TAG = HalfSizedDialogFragment.class.getSimpleName(); + public static final String TRACKER_LABEL = "Half sized dialog"; + + private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30); + + private OnActionClickListener mOnActionClickListener; + + private Handler mHandler = new Handler(); + private Runnable mAutoDismisser = new Runnable() { + @Override + public void run() { + dismiss(); + } + }; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.halfsized_dialog, container, false); + } + + @Override + public void onStart() { + super.onStart(); + mHandler.postDelayed(mAutoDismisser, AUTO_DISMISS_TIME_THRESHOLD_MS); + } + + @Override + public void onPause() { + super.onPause(); + if (mOnActionClickListener != null) { + // Dismisses the dialog to prevent the callback being forgotten during + // fragment re-creating. + dismiss(); + } + } + + @Override + public void onStop() { + super.onStop(); + mHandler.removeCallbacks(mAutoDismisser); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.setOnKeyListener(new DialogInterface.OnKeyListener() { + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent keyEvent) { + mHandler.removeCallbacks(mAutoDismisser); + mHandler.postDelayed(mAutoDismisser, AUTO_DISMISS_TIME_THRESHOLD_MS); + return false; + } + }); + return dialog; + } + + @Override + public int getTheme() { + return R.style.Theme_TV_dialog_HalfSizedDialog; + } + + @Override + public String getTrackerLabel() { + return TRACKER_LABEL; + } + + /** + * Sets {@link OnActionClickListener} for the dialog fragment. If listener is set, the dialog + * will be automatically closed when it's paused to prevent the fragment being re-created by + * the framework, which will result the listener being forgotten. + */ + public void setOnActionClickListener(OnActionClickListener listener) { + mOnActionClickListener = listener; + } + + /** + * Returns {@link OnActionClickListener} for sub-classes or any inner fragments. + */ + protected OnActionClickListener getOnActionClickListener() { + return mOnActionClickListener; + } + + /** + * An interface to provide callbacks for half-sized dialogs. Subclasses or inner fragments + * should invoke {@link OnActionClickListener#onActionClick(long)} and provide the identifier + * of the action user clicked. + */ + public interface OnActionClickListener { + void onActionClick(long actionId); + } +}
\ No newline at end of file diff --git a/src/com/android/tv/dialog/PinDialogFragment.java b/src/com/android/tv/dialog/PinDialogFragment.java index d9d6c73f..d5c154da 100644 --- a/src/com/android/tv/dialog/PinDialogFragment.java +++ b/src/com/android/tv/dialog/PinDialogFragment.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.res.Resources; +import android.media.tv.TvContentRating; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; @@ -45,11 +46,13 @@ import android.widget.TextView; import android.widget.Toast; import com.android.tv.R; +import com.android.tv.TvApplication; +import com.android.tv.common.SoftPreconditions; import com.android.tv.util.TvSettings; public class PinDialogFragment extends SafeDismissDialogFragment { private static final String TAG = "PinDialogFragment"; - private static final boolean DBG = true; + private static final boolean DEBUG = true; /** * PIN code dialog for unlock channel @@ -80,18 +83,13 @@ public class PinDialogFragment extends SafeDismissDialogFragment { */ public static final int PIN_DIALOG_TYPE_UNLOCK_DVR = 5; - private static final int PIN_DIALOG_RESULT_SUCCESS = 0; - private static final int PIN_DIALOG_RESULT_FAIL = 1; - private static final int MAX_WRONG_PIN_COUNT = 5; private static final int DISABLE_PIN_DURATION_MILLIS = 60 * 1000; // 1 minute private static final String INITIAL_TEXT = "—"; private static final String TRACKER_LABEL = "Pin dialog"; - - public interface ResultListener { - void done(boolean success); - } + private static final String ARGS_TYPE = "args_type"; + private static final String ARGS_RATING = "args_rating"; public static final String DIALOG_TAG = PinDialogFragment.class.getName(); @@ -99,8 +97,9 @@ public class PinDialogFragment extends SafeDismissDialogFragment { R.id.first, R.id.second, R.id.third, R.id.fourth }; private int mType; - private ResultListener mListener; - private int mRetCode; + private int mRequestType; + private boolean mPinChecked; + private boolean mDismissSilently; private TextView mWrongPinView; private View mEnterPinView; @@ -114,29 +113,35 @@ public class PinDialogFragment extends SafeDismissDialogFragment { private long mDisablePinUntil; private final Handler mHandler = new Handler(); - public PinDialogFragment(int type, ResultListener listener) { - this(type, listener, null); + public static PinDialogFragment create(int type) { + return create(type, null); } - public PinDialogFragment(int type, ResultListener listener, String rating) { - mType = type; - mListener = listener; - mRetCode = PIN_DIALOG_RESULT_FAIL; - mRatingString = rating; + public static PinDialogFragment create(int type, String rating) { + PinDialogFragment fragment = new PinDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARGS_TYPE, type); + args.putString(ARGS_RATING, rating); + fragment.setArguments(args); + return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mRequestType = getArguments().getInt(ARGS_TYPE, PIN_DIALOG_TYPE_ENTER_PIN); + mType = mRequestType; + mRatingString = getArguments().getString(ARGS_RATING); setStyle(STYLE_NO_TITLE, 0); mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); mDisablePinUntil = TvSettings.getDisablePinUntil(getActivity()); if (ActivityManager.isUserAMonkey()) { // Skip PIN dialog half the time for monkeys if (Math.random() < 0.5) { - exit(PIN_DIALOG_RESULT_SUCCESS); + exit(true); } } + mPinChecked = false; } @Override @@ -186,7 +191,19 @@ public class PinDialogFragment extends SafeDismissDialogFragment { mTitleView.setText(R.string.pin_enter_unlock_program); break; case PIN_DIALOG_TYPE_UNLOCK_DVR: - mTitleView.setText(getString(R.string.pin_enter_unlock_dvr, mRatingString)); + TvContentRating tvContentRating = + TvContentRating.unflattenFromString(mRatingString); + if (TvContentRating.UNRATED.equals(tvContentRating)) { + mTitleView.setText(getString(R.string.pin_enter_unlock_dvr_unrated)); + } else { + mTitleView.setText( + getString( + R.string.pin_enter_unlock_dvr, + TvApplication.getSingletons(getContext()) + .getTvInputManagerHelper() + .getContentRatingsManager() + .getDisplayNameForRating(tvContentRating))); + } break; case PIN_DIALOG_TYPE_ENTER_PIN: mTitleView.setText(R.string.pin_enter_pin); @@ -217,10 +234,6 @@ public class PinDialogFragment extends SafeDismissDialogFragment { return v; } - public void setResultListener(ResultListener listener) { - mListener = listener; - } - private final Runnable mUpdateEnterPinRunnable = new Runnable() { @Override public void run() { @@ -250,18 +263,27 @@ public class PinDialogFragment extends SafeDismissDialogFragment { } } - private void exit(int retCode) { - mRetCode = retCode; + private void exit(boolean pinChecked) { + mPinChecked = pinChecked; + dismiss(); + } + + /** Dismisses the pin dialog without calling activity listener. */ + public void dismissSilently() { + mDismissSilently = true; dismiss(); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); - if (DBG) Log.d(TAG, "onDismiss: mRetCode=" + mRetCode); - if (mListener != null) { - mListener.done(mRetCode == PIN_DIALOG_RESULT_SUCCESS); + if (DEBUG) Log.d(TAG, "onDismiss: mPinChecked=" + mPinChecked); + SoftPreconditions.checkState(getActivity() instanceof OnPinCheckedListener); + if (!mDismissSilently && getActivity() instanceof OnPinCheckedListener) { + ((OnPinCheckedListener) getActivity()).onPinChecked( + mPinChecked, mRequestType, mRatingString); } + mDismissSilently = false; } private void handleWrongPin() { @@ -279,15 +301,14 @@ public class PinDialogFragment extends SafeDismissDialogFragment { } private void done(String pin) { - if (DBG) Log.d(TAG, "done: mType=" + mType + " pin=" + pin + " stored=" + getPin()); + if (DEBUG) Log.d(TAG, "done: mType=" + mType + " pin=" + pin + " stored=" + getPin()); switch (mType) { case PIN_DIALOG_TYPE_UNLOCK_CHANNEL: case PIN_DIALOG_TYPE_UNLOCK_PROGRAM: case PIN_DIALOG_TYPE_UNLOCK_DVR: case PIN_DIALOG_TYPE_ENTER_PIN: - // TODO: Implement limited number of retrials and timeout logic. if (TextUtils.isEmpty(getPin()) || pin.equals(getPin())) { - exit(PIN_DIALOG_RESULT_SUCCESS); + exit(true); } else { resetPinInput(); handleWrongPin(); @@ -301,7 +322,7 @@ public class PinDialogFragment extends SafeDismissDialogFragment { } else { if (pin.equals(mPrevPin)) { setPin(pin); - exit(PIN_DIALOG_RESULT_SUCCESS); + exit(true); } else { if (TextUtils.isEmpty(getPin())) { mTitleView.setText(R.string.pin_enter_create_pin); @@ -332,7 +353,7 @@ public class PinDialogFragment extends SafeDismissDialogFragment { } private void setPin(String pin) { - if (DBG) Log.d(TAG, "setPin: " + pin); + if (DEBUG) Log.d(TAG, "setPin: " + pin); mPin = pin; mSharedPreferences.edit().putString(TvSettings.PREF_PIN, pin).apply(); } @@ -684,4 +705,20 @@ public class PinDialogFragment extends SafeDismissDialogFragment { : (value > mMaxValue) ? value - interval : value; } } + + /** + * A listener to the result of {@link PinDialogFragment}. Any activity requiring pin code + * checking should implement this listener to receive the result. + */ + public interface OnPinCheckedListener { + /** + * Called when {@link PinDialogFragment} is dismissed. + * + * @param checked {@code true} if the pin code entered is checked to be correct, + * otherwise {@code false}. + * @param type The dialog type regarding to what pin entering is for. + * @param rating The target rating to unblock for. + */ + void onPinChecked(boolean checked, int type, String rating); + } } diff --git a/src/com/android/tv/dialog/SafeDismissDialogFragment.java b/src/com/android/tv/dialog/SafeDismissDialogFragment.java index f671a87d..e3390b0a 100644 --- a/src/com/android/tv/dialog/SafeDismissDialogFragment.java +++ b/src/com/android/tv/dialog/SafeDismissDialogFragment.java @@ -17,11 +17,7 @@ package com.android.tv.dialog; import android.app.Activity; -import android.app.Dialog; import android.app.DialogFragment; -import android.content.Context; -import android.os.Bundle; -import android.view.KeyEvent; import com.android.tv.MainActivity; import com.android.tv.TvApplication; @@ -39,11 +35,6 @@ public abstract class SafeDismissDialogFragment extends DialogFragment private Tracker mTracker; @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return new TvDialog(getActivity(), getTheme()); - } - - @Override public void onAttach(Activity activity) { super.onAttach(activity); mAttached = true; @@ -92,21 +83,4 @@ public abstract class SafeDismissDialogFragment extends DialogFragment super.dismiss(); } } - - protected class TvDialog extends Dialog { - public TvDialog(Context context, int theme) { - super(context, theme); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - // When a dialog is showing, key events are handled by the dialog instead of - // MainActivity. Therefore, unless a key is a global key, it should be handled here. - if (mAttached && keyCode == KeyEvent.KEYCODE_SEARCH && mActivity != null) { - mActivity.showSearchActivity(); - return true; - } - return super.onKeyUp(keyCode, event); - } - } } diff --git a/src/com/android/tv/dialog/WebDialogFragment.java b/src/com/android/tv/dialog/WebDialogFragment.java index 75f93bb2..171a256b 100644 --- a/src/com/android/tv/dialog/WebDialogFragment.java +++ b/src/com/android/tv/dialog/WebDialogFragment.java @@ -37,6 +37,7 @@ public class WebDialogFragment extends SafeDismissDialogFragment { private static final String TITLE = "TITLE"; private static final String TRACKER_LABEL = "TRACKER_LABEL"; + private WebView mWebView; private String mTrackerLabel; /** @@ -73,13 +74,21 @@ public class WebDialogFragment extends SafeDismissDialogFragment { String title = getArguments().getString(TITLE); getDialog().setTitle(title); - WebView webView = new WebView(getActivity()); - webView.setWebViewClient(new WebViewClient()); + mWebView = new WebView(getActivity()); + mWebView.setWebViewClient(new WebViewClient()); String url = getArguments().getString(URL); - webView.loadUrl(url); + mWebView.loadUrl(url); Log.d(TAG, "Loading web content from " + url); - return webView; + return mWebView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (mWebView != null) { + mWebView.destroy(); + } } @Override |