aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/dialog
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/dialog')
-rw-r--r--src/com/android/tv/dialog/DvrHistoryDialogFragment.java130
-rw-r--r--src/com/android/tv/dialog/FullscreenDialogFragment.java2
-rw-r--r--src/com/android/tv/dialog/HalfSizedDialogFragment.java123
-rw-r--r--src/com/android/tv/dialog/PinDialogFragment.java103
-rw-r--r--src/com/android/tv/dialog/SafeDismissDialogFragment.java26
-rw-r--r--src/com/android/tv/dialog/WebDialogFragment.java17
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