summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYorke Lee <yorkelee@google.com>2015-10-02 16:32:36 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-10-02 16:32:36 +0000
commit2159803d76ee72cb2561b56c17c377f19e5fdf37 (patch)
tree6b853b6348678ee8335c89988df3ee25f12ff99a /src
parent2ef15c5219c0b6edf35002d0b7a3c2cbc727f57b (diff)
parent545a779f0df2825067679bee09bc966ca12b5974 (diff)
Merge "First pass for Dialer onboarding flow" into ub-contactsdialer-a-dev
Diffstat (limited to 'src')
-rw-r--r--src/com/android/dialer/onboard/OnboardingActivity.java245
-rw-r--r--src/com/android/dialer/onboard/OnboardingController.java85
-rw-r--r--src/com/android/dialer/onboard/OnboardingFragment.java93
-rw-r--r--src/com/android/dialer/onboard/PermissionsChecker.java27
4 files changed, 450 insertions, 0 deletions
diff --git a/src/com/android/dialer/onboard/OnboardingActivity.java b/src/com/android/dialer/onboard/OnboardingActivity.java
new file mode 100644
index 000000000..75378e99d
--- /dev/null
+++ b/src/com/android/dialer/onboard/OnboardingActivity.java
@@ -0,0 +1,245 @@
+/*
+ * 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.dialer.onboard;
+
+import static android.Manifest.permission.CALL_PHONE;
+import static android.Manifest.permission.READ_CONTACTS;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.telecom.TelecomManager;
+
+import com.android.contacts.common.util.PermissionsUtil;
+import com.android.dialer.TransactionSafeActivity;
+import com.android.dialer.onboard.OnboardingController.OnboardingScreen;
+import com.android.dialer.onboard.OnboardingController.OnboardingUi;
+import com.android.dialer.util.TelecomUtil;
+import com.android.dialer.R;
+
+/**
+ * Activity hosting the onboarding UX flow that appears when you launch Dialer and you don't have
+ * the necessary permissions to run the app.
+ */
+public class OnboardingActivity extends TransactionSafeActivity implements OnboardingUi,
+ PermissionsChecker, OnboardingFragment.HostInterface {
+ public static final String KEY_ALREADY_REQUESTED_DEFAULT_DIALER =
+ "key_already_requested_default_dialer";
+
+ public static final int SCREEN_DEFAULT_DIALER = 0;
+ public static final int SCREEN_PERMISSIONS = 1;
+ public static final int SCREEN_COUNT = 2;
+
+ private OnboardingController mOnboardingController;
+
+ private DefaultDialerOnboardingScreen mDefaultDialerOnboardingScreen;
+ private PermissionsOnboardingScreen mPermissionsOnboardingScreen;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.onboarding_activity);
+ mOnboardingController = new OnboardingController(this);
+ mDefaultDialerOnboardingScreen = new DefaultDialerOnboardingScreen(this);
+ mPermissionsOnboardingScreen = new PermissionsOnboardingScreen(this);
+ mOnboardingController.addScreen(mDefaultDialerOnboardingScreen);
+ mOnboardingController.addScreen(mPermissionsOnboardingScreen);
+
+ mOnboardingController.showNextScreen();
+ }
+
+ @Override
+ public void showScreen(int screenId) {
+ if (!isSafeToCommitTransactions()) {
+ return;
+ }
+ final Fragment fragment;
+ switch (screenId) {
+ case SCREEN_DEFAULT_DIALER:
+ fragment = mDefaultDialerOnboardingScreen.getFragment();
+ break;
+ case SCREEN_PERMISSIONS:
+ fragment = mPermissionsOnboardingScreen.getFragment();
+ break;
+ default:
+ return;
+ }
+
+ final FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
+ ft.replace(R.id.onboarding_fragment_container, fragment);
+ ft.commit();
+ }
+
+ @Override
+ public void completeOnboardingFlow() {
+ final Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
+ editor.putBoolean(KEY_ALREADY_REQUESTED_DEFAULT_DIALER, true).apply();
+ finish();
+ }
+
+ @Override
+ public boolean hasPhonePermissions() {
+ return PermissionsUtil.hasPhonePermissions(this);
+ }
+
+ @Override
+ public boolean hasContactsPermissions() {
+ return PermissionsUtil.hasContactsPermissions(this);
+ }
+
+ @Override
+ public boolean isDefaultOrSystemDialer() {
+ return TelecomUtil.hasModifyPhoneStatePermission(this);
+ }
+
+ @Override
+ public boolean previouslyRequestedDefaultDialer() {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ return preferences.getBoolean(KEY_ALREADY_REQUESTED_DEFAULT_DIALER, false);
+ }
+
+ /**
+ * Triggers the screen-specific logic that should occur when the next button is clicked.
+ */
+ @Override
+ public void onNextClicked(int screenId) {
+ switch (screenId) {
+ case SCREEN_DEFAULT_DIALER:
+ mDefaultDialerOnboardingScreen.onNextClicked(this);
+ break;
+ case SCREEN_PERMISSIONS:
+ mPermissionsOnboardingScreen.onNextClicked(this);
+ break;
+ default:
+ return;
+ }
+ }
+
+ @Override
+ public void onSkipClicked(int screenId) {
+ mOnboardingController.onScreenResult(screenId, false);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == SCREEN_DEFAULT_DIALER
+ && resultCode == RESULT_OK) {
+ mOnboardingController.onScreenResult(SCREEN_DEFAULT_DIALER, true);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ boolean allPermissionsGranted = true;
+ if (requestCode == SCREEN_PERMISSIONS) {
+ if (permissions.length == 0 && grantResults.length == 0) {
+ // Cancellation of permissions dialog
+ allPermissionsGranted = false;
+ } else {
+ for (int result : grantResults) {
+ if (result == PackageManager.PERMISSION_DENIED) {
+ allPermissionsGranted = false;
+ }
+ }
+ }
+
+ if (allPermissionsGranted) {
+ mOnboardingController.onScreenResult(SCREEN_PERMISSIONS, true);
+ }
+ }
+ }
+
+ public static class DefaultDialerOnboardingScreen extends OnboardingScreen {
+ private PermissionsChecker mPermissionsChecker;
+
+ public DefaultDialerOnboardingScreen(PermissionsChecker permissionsChecker) {
+ mPermissionsChecker = permissionsChecker;
+ }
+
+ @Override
+ public boolean shouldShowScreen() {
+ return !mPermissionsChecker.previouslyRequestedDefaultDialer()
+ && !mPermissionsChecker.isDefaultOrSystemDialer();
+ }
+
+ @Override
+ public boolean canSkipScreen() {
+ return true;
+ }
+
+ public Fragment getFragment() {
+ return new OnboardingFragment(
+ SCREEN_DEFAULT_DIALER,
+ canSkipScreen(),
+ R.color.onboarding_default_dialer_screen_background_color,
+ R.string.request_default_dialer_screen_title,
+ R.string.request_default_dialer_screen_content
+ );
+ }
+
+ @Override
+ public void onNextClicked(Activity activity) {
+ final Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+ intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
+ activity.getPackageName());
+ activity.startActivityForResult(intent, SCREEN_DEFAULT_DIALER);
+ }
+ }
+
+ public static class PermissionsOnboardingScreen extends OnboardingScreen {
+ private PermissionsChecker mPermissionsChecker;
+
+ public PermissionsOnboardingScreen(PermissionsChecker permissionsChecker) {
+ mPermissionsChecker = permissionsChecker;
+ }
+
+ @Override
+ public boolean shouldShowScreen() {
+ return !(mPermissionsChecker.hasPhonePermissions()
+ && mPermissionsChecker.hasContactsPermissions());
+ }
+
+ @Override
+ public boolean canSkipScreen() {
+ return false;
+ }
+
+ public Fragment getFragment() {
+ return new OnboardingFragment(
+ SCREEN_PERMISSIONS,
+ canSkipScreen(),
+ R.color.onboarding_permissions_screen_background_color,
+ R.string.request_permissions_screen_title,
+ R.string.request_permissions_screen_content
+ );
+ }
+
+ @Override
+ public void onNextClicked(Activity activity) {
+ activity.requestPermissions(new String[] {CALL_PHONE, READ_CONTACTS},
+ SCREEN_PERMISSIONS);
+ }
+ }
+}
diff --git a/src/com/android/dialer/onboard/OnboardingController.java b/src/com/android/dialer/onboard/OnboardingController.java
new file mode 100644
index 000000000..f799479ed
--- /dev/null
+++ b/src/com/android/dialer/onboard/OnboardingController.java
@@ -0,0 +1,85 @@
+/*
+ * 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.dialer.onboard;
+
+import android.app.Activity;
+
+import java.util.ArrayList;
+
+/**
+ * Class that manages the display of various fragments that show the user prompts asking for
+ * certain privileged positions.
+ */
+public class OnboardingController {
+ public static abstract class OnboardingScreen {
+ public abstract boolean shouldShowScreen();
+ public abstract boolean canSkipScreen();
+ public abstract void onNextClicked(Activity activity);
+ }
+
+ public interface OnboardingUi {
+ public void showScreen(int screenId);
+ /**
+ * Called when all the necessary permissions have been granted and the main activity
+ * can launch.
+ */
+ public void completeOnboardingFlow();
+ }
+
+ private int mCurrentScreen = -1;
+ private OnboardingUi mOnboardingUi;
+ private ArrayList<OnboardingScreen> mScreens = new ArrayList<> ();
+
+ public OnboardingController(OnboardingUi onBoardingUi) {
+ mOnboardingUi = onBoardingUi;
+ }
+
+ public void addScreen(OnboardingScreen screen) {
+ mScreens.add(screen);
+ }
+
+ public void showNextScreen() {
+ mCurrentScreen++;
+
+ if (mCurrentScreen >= mScreens.size()) {
+ // Reached the end of onboarding flow
+ mOnboardingUi.completeOnboardingFlow();
+ return;
+ }
+
+ if (mScreens.get(mCurrentScreen).shouldShowScreen()) {
+ mOnboardingUi.showScreen(mCurrentScreen);
+ } else {
+ showNextScreen();
+ }
+ }
+
+ public void onScreenResult(int screenId, boolean success) {
+ if (screenId >= mScreens.size()) {
+ return;
+ }
+
+ // Show the next screen in the onboarding flow only under the following situations:
+ // 1) Success was indicated, and the
+ // 2) The user tried to skip the screen, and the screen can be skipped
+ if (success && !mScreens.get(mCurrentScreen).shouldShowScreen()) {
+ showNextScreen();
+ } else if (mScreens.get(mCurrentScreen).canSkipScreen()) {
+ showNextScreen();
+ }
+ }
+}
diff --git a/src/com/android/dialer/onboard/OnboardingFragment.java b/src/com/android/dialer/onboard/OnboardingFragment.java
new file mode 100644
index 000000000..77b265b2c
--- /dev/null
+++ b/src/com/android/dialer/onboard/OnboardingFragment.java
@@ -0,0 +1,93 @@
+/*
+ * 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.dialer.onboard;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.dialer.R;
+
+public class OnboardingFragment extends Fragment implements OnClickListener {
+ public static final String ARG_SCREEN_ID = "arg_screen_id";
+ public static final String ARG_CAN_SKIP_SCREEN = "arg_can_skip_screen";
+ public static final String ARG_BACKGROUND_COLOR_RESOURCE = "arg_background_color";
+ public static final String ARG_TEXT_TITLE_RESOURCE = "arg_text_title_resource";
+ public static final String ARG_TEXT_CONTENT_RESOURCE = "arg_text_content_resource";
+
+ private int mScreenId;
+
+ public interface HostInterface {
+ public void onNextClicked(int screenId);
+ public void onSkipClicked(int screenId);
+ }
+
+ public OnboardingFragment() {}
+
+ public OnboardingFragment(int screenId, boolean canSkipScreen, int backgroundColorResourceId,
+ int textTitleResourceId, int textContentResourceId) {
+ final Bundle args = new Bundle();
+ args.putInt(ARG_SCREEN_ID, screenId);
+ args.putBoolean(ARG_CAN_SKIP_SCREEN, canSkipScreen);
+ args.putInt(ARG_BACKGROUND_COLOR_RESOURCE, backgroundColorResourceId);
+ args.putInt(ARG_TEXT_TITLE_RESOURCE, textTitleResourceId);
+ args.putInt(ARG_TEXT_CONTENT_RESOURCE, textContentResourceId);
+ setArguments(args);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mScreenId = getArguments().getInt(ARG_SCREEN_ID);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.onboarding_screen_fragment, container, false);
+ view.setBackgroundColor(getResources().getColor(
+ getArguments().getInt(ARG_BACKGROUND_COLOR_RESOURCE), null));
+ ((TextView) view.findViewById(R.id.onboarding_screen_content)).
+ setText(getArguments().getInt(ARG_TEXT_CONTENT_RESOURCE));
+ ((TextView) view.findViewById(R.id.onboarding_screen_title)).
+ setText(getArguments().getInt(ARG_TEXT_TITLE_RESOURCE));
+ if (!getArguments().getBoolean(ARG_CAN_SKIP_SCREEN)) {
+ view.findViewById(R.id.onboard_skip_button).setVisibility(View.INVISIBLE);
+ }
+
+ view.findViewById(R.id.onboard_skip_button).setOnClickListener(this);
+ view.findViewById(R.id.onboard_next_button).setOnClickListener(this);
+
+ return view;
+ }
+
+ int getScreenId() {
+ return mScreenId;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.onboard_skip_button) {
+ ((HostInterface) getActivity()).onSkipClicked(getScreenId());
+ } else if (v.getId() == R.id.onboard_next_button) {
+ ((HostInterface) getActivity()).onNextClicked(getScreenId());
+ }
+ }
+}
diff --git a/src/com/android/dialer/onboard/PermissionsChecker.java b/src/com/android/dialer/onboard/PermissionsChecker.java
new file mode 100644
index 000000000..78d175e6f
--- /dev/null
+++ b/src/com/android/dialer/onboard/PermissionsChecker.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dialer.onboard;
+
+/**
+ * Defines a mockable interface used to verify whether certain permissions/privileged operations
+ * are possible.
+ */
+public interface PermissionsChecker {
+ public boolean hasPhonePermissions();
+ public boolean hasContactsPermissions();
+ public boolean isDefaultOrSystemDialer();
+ public boolean previouslyRequestedDefaultDialer();
+}