Merge "Apply ColorStateList to external icons so that they update when preferences are disabled." into pi-car-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e61aaf2..7479f10 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -317,6 +317,23 @@
             <meta-data android:name="distractionOptimized" android:value="true"/>
         </activity>
 
+        <activity android:name=".datausage.DataWarningAndLimitActivity"
+                  android:configChanges="orientation|keyboardHidden|screenSize"
+                  android:windowSoftInputMode="adjustResize"
+                  android:exported="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
+        </activity>
+
+        <activity
+            android:name=".wifi.WifiRequestToggleActivity"
+            android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.net.wifi.action.REQUEST_ENABLE" />
+                <action android:name="android.net.wifi.action.REQUEST_DISABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <!-- This logic is copied from phone.-->
         <!-- Ensures there's lightweight fallback activity when no other MAIN/HOME activity is present.-->
         <activity android:name=".FallbackHome"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3b38e1e..6e5d4a1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -253,6 +253,12 @@
     <string name="wifi_hotspot_auto_off_title">Turn off hotspot automatically</string>
     <!-- Summary for the toggle to turn off hotspot automatically [CHAR LIMIT=100]-->
     <string name="wifi_hotspot_auto_off_summary">Wi\u2011Fi hotspot will turn off if no devices are connected</string>
+    <!-- This string asks the user whether or not to allow an app to enable WiFi. [CHAR LIMIT=NONE] -->
+    <string name="wifi_ask_enable"><xliff:g id="requester" example="FancyApp">%s</xliff:g> wants to turn on Wi-Fi</string>
+    <!-- This string asks the user whether or not to allow an app to disable WiFi. [CHAR LIMIT=NONE] -->
+    <string name="wifi_ask_disable"><xliff:g id="requester" example="FancyApp">%s</xliff:g> wants to turn off Wi-Fi</string>
+    <!-- Summary text when Wi-Fi has error -->
+    <string name="wifi_error">Error</string>
 
     <!-- Bluetooth settings --><skip/>
     <!-- Title of Bluetooth settings pages. [CHAR LIMIT=30] -->
diff --git a/res/xml/data_usage_fragment.xml b/res/xml/data_usage_fragment.xml
index fcb5590..6ca27c0 100644
--- a/res/xml/data_usage_fragment.xml
+++ b/res/xml/data_usage_fragment.xml
@@ -28,8 +28,10 @@
         android:title="@string/app_data_usage"
         settings:controller="com.android.car.settings.datausage.DataUsagePreferenceController"/>
     <Preference
-        android:fragment="com.android.car.settings.datausage.DataWarningAndLimitFragment"
         android:key="@string/pk_data_warning_and_limit"
         android:title="@string/data_warning_limit_title"
-        settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"/>
+        settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController">
+        <intent android:targetPackage="com.android.car.settings"
+                android:targetClass="com.android.car.settings.datausage.DataWarningAndLimitActivity"/>
+    </Preference>
 </PreferenceScreen>
diff --git a/src/com/android/car/settings/datausage/DataWarningAndLimitActivity.java b/src/com/android/car/settings/datausage/DataWarningAndLimitActivity.java
new file mode 100644
index 0000000..5f0aef2
--- /dev/null
+++ b/src/com/android/car/settings/datausage/DataWarningAndLimitActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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.car.settings.datausage;
+
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.android.car.settings.common.BaseCarSettingsActivity;
+
+/** Separate activity to enter the data warning and limit settings via activity name. */
+public class DataWarningAndLimitActivity extends BaseCarSettingsActivity {
+
+    @Nullable
+    @Override
+    protected Fragment getInitialFragment() {
+        NetworkTemplate template = getIntent().getParcelableExtra(
+                NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE);
+        return DataWarningAndLimitFragment.newInstance(template);
+    }
+}
diff --git a/src/com/android/car/settings/datausage/DataWarningAndLimitFragment.java b/src/com/android/car/settings/datausage/DataWarningAndLimitFragment.java
index 8aa3fc2..c8b172a 100644
--- a/src/com/android/car/settings/datausage/DataWarningAndLimitFragment.java
+++ b/src/com/android/car/settings/datausage/DataWarningAndLimitFragment.java
@@ -19,9 +19,11 @@
 import android.content.Context;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
+import android.os.Bundle;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.XmlRes;
 
 import com.android.car.settings.R;
@@ -39,6 +41,18 @@
     private NetworkPolicyEditor mPolicyEditor;
     private NetworkTemplate mNetworkTemplate;
 
+    /**
+     * Creates a new instance of {@link DataWarningAndLimitFragment} with the given template. If the
+     * template is {@code null}, the fragment will use the default data network template.
+     */
+    public static DataWarningAndLimitFragment newInstance(@Nullable NetworkTemplate template) {
+        DataWarningAndLimitFragment fragment = new DataWarningAndLimitFragment();
+        Bundle args = new Bundle();
+        args.putParcelable(NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE, template);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
     @Override
     @XmlRes
     protected int getPreferenceScreenResId() {
@@ -50,10 +64,14 @@
         super.onAttach(context);
 
         mPolicyEditor = new NetworkPolicyEditor(NetworkPolicyManager.from(context));
-        mTelephonyManager = context.getSystemService(TelephonyManager.class);
-        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
-        mNetworkTemplate = DataUsageUtils.getMobileNetworkTemplate(mTelephonyManager,
-                DataUsageUtils.getDefaultSubscriptionId(mSubscriptionManager));
+        mNetworkTemplate = getArguments().getParcelable(
+                NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE);
+        if (mNetworkTemplate == null) {
+            mTelephonyManager = context.getSystemService(TelephonyManager.class);
+            mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+            mNetworkTemplate = DataUsageUtils.getMobileNetworkTemplate(mTelephonyManager,
+                    DataUsageUtils.getDefaultSubscriptionId(mSubscriptionManager));
+        }
 
         // Loads the current policies to the policy editor cache.
         mPolicyEditor.read();
diff --git a/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java b/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java
new file mode 100644
index 0000000..0b79b4b
--- /dev/null
+++ b/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2019 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.car.settings.wifi;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import com.android.car.settings.R;
+import com.android.car.settings.common.Logger;
+
+/**
+ * Code drop from {@link com.android.settings.wifi.RequestToggleWiFiActivity}.
+ *
+ * This {@link Activity} handles requests to toggle WiFi by collecting user
+ * consent and waiting until the state change is completed.
+ */
+public class WifiRequestToggleActivity extends Activity implements DialogInterface.OnClickListener,
+        DialogInterface.OnCancelListener {
+    private static final Logger LOG = new Logger(WifiRequestToggleActivity.class);
+    private static final long TOGGLE_TIMEOUT_MILLIS = 10000; // 10 sec
+
+    private static final int STATE_UNKNOWN = -1;
+    private static final int STATE_ENABLE = 1;
+    private static final int STATE_ENABLING = 2;
+    private static final int STATE_DISABLE = 3;
+    private static final int STATE_DISABLING = 4;
+
+    private final StateChangeReceiver mReceiver = new StateChangeReceiver();
+
+    private final Runnable mTimeoutCommand = () -> {
+        if (!isFinishing() && !isDestroyed()) {
+            finish();
+        }
+    };
+
+    @NonNull
+    private CarWifiManager mCarWifiManager;
+    @NonNull
+    private CharSequence mAppLabel;
+
+    private AlertDialog mDialog;
+    private int mAction = STATE_UNKNOWN;
+    private int mState = STATE_UNKNOWN;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mCarWifiManager = new CarWifiManager(getApplicationContext());
+
+        setResult(Activity.RESULT_CANCELED);
+
+        String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        if (TextUtils.isEmpty(packageName)) {
+            finish();
+            return;
+        }
+
+        try {
+            ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(
+                    packageName,  /* flags= */ 0);
+            mAppLabel = applicationInfo.loadSafeLabel(getPackageManager());
+        } catch (PackageManager.NameNotFoundException e) {
+            LOG.e("Couldn't find app with package name " + packageName);
+            finish();
+            return;
+        }
+
+        mAction = extractActionState();
+        if (mAction == STATE_UNKNOWN) {
+            finish();
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                switch (mState) {
+                    case STATE_ENABLE:
+                        mCarWifiManager.setWifiEnabled(true);
+                        mState = STATE_ENABLING;
+                        updateUi();
+                        scheduleToggleTimeout();
+                        break;
+
+                    case STATE_DISABLE:
+                        mCarWifiManager.setWifiEnabled(false);
+                        mState = STATE_DISABLING;
+                        updateUi();
+                        scheduleToggleTimeout();
+                        break;
+                }
+                setResult(RESULT_OK);
+                break;
+            case DialogInterface.BUTTON_NEGATIVE:
+                finish();
+                break;
+        }
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        finish();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        mReceiver.register();
+
+        final int wifiState = mCarWifiManager.getWifiState();
+
+        switch (mAction) {
+            case STATE_ENABLE:
+                switch (wifiState) {
+                    case WifiManager.WIFI_STATE_ENABLED:
+                        setResult(RESULT_OK);
+                        finish();
+                        return;
+
+                    case WifiManager.WIFI_STATE_ENABLING:
+                        mState = STATE_ENABLING;
+                        scheduleToggleTimeout();
+                        break;
+                    default:
+                        mState = mAction;
+                }
+                break;
+
+            case STATE_DISABLE:
+                switch (wifiState) {
+                    case WifiManager.WIFI_STATE_DISABLED:
+                        setResult(RESULT_OK);
+                        finish();
+                        return;
+
+                    case WifiManager.WIFI_STATE_DISABLING:
+                        mState = STATE_DISABLING;
+                        scheduleToggleTimeout();
+                        break;
+                    default:
+                        mState = mAction;
+                }
+                break;
+        }
+
+        updateUi();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mReceiver.unregister();
+        unscheduleToggleTimeout();
+        if (mDialog != null) {
+            mDialog.dismiss();
+        }
+    }
+
+    private void updateUi() {
+        if (mDialog != null) {
+            mDialog.dismiss();
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(/* context= */
+                this).setOnCancelListener(/* listener= */ this);
+        switch (mState) {
+            case STATE_ENABLE:
+                builder.setPositiveButton(R.string.allow, /* listener= */ this);
+                builder.setNegativeButton(R.string.deny, /* listener= */ this);
+                builder.setMessage(getString(R.string.wifi_ask_enable, mAppLabel));
+                break;
+
+            case STATE_ENABLING:
+                builder.setMessage(getString(R.string.wifi_starting, mAppLabel));
+                break;
+
+            case STATE_DISABLE:
+                builder.setPositiveButton(R.string.allow, /* listener= */ this);
+                builder.setNegativeButton(R.string.deny, /* listener= */ this);
+                builder.setMessage(getString(R.string.wifi_ask_disable, mAppLabel));
+                break;
+
+            case STATE_DISABLING:
+                builder.setMessage(getString(R.string.wifi_stopping, mAppLabel));
+                break;
+        }
+        mDialog = builder.create();
+        mDialog.show();
+    }
+
+    private void scheduleToggleTimeout() {
+        getWindow().getDecorView().postDelayed(mTimeoutCommand, TOGGLE_TIMEOUT_MILLIS);
+    }
+
+    private void unscheduleToggleTimeout() {
+        getWindow().getDecorView().removeCallbacks(mTimeoutCommand);
+    }
+
+    private int extractActionState() {
+        String action = getIntent().getAction();
+        switch (action) {
+            case WifiManager.ACTION_REQUEST_ENABLE:
+                return STATE_ENABLE;
+
+            case WifiManager.ACTION_REQUEST_DISABLE:
+                return STATE_DISABLE;
+
+            default:
+                return STATE_UNKNOWN;
+        }
+    }
+
+    private final class StateChangeReceiver extends BroadcastReceiver {
+        private final IntentFilter mFilter = new IntentFilter(
+                WifiManager.WIFI_STATE_CHANGED_ACTION);
+
+        public void register() {
+            registerReceiver(/* receiver= */ this, mFilter);
+        }
+
+        public void unregister() {
+            unregisterReceiver(/* receiver= */ this);
+        }
+
+        public void onReceive(Context context, Intent intent) {
+            Activity activity = WifiRequestToggleActivity.this;
+            if (activity.isFinishing() || activity.isDestroyed()) {
+                return;
+            }
+            int currentState = mCarWifiManager.getWifiState();
+            switch (currentState) {
+                case WifiManager.WIFI_STATE_ENABLED:
+                case WifiManager.WIFI_STATE_DISABLED:
+                    if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
+                        activity.setResult(Activity.RESULT_OK);
+                        finish();
+                    }
+                    break;
+
+                case WifiManager.ERROR:
+                    Toast.makeText(activity, R.string.wifi_error, Toast.LENGTH_SHORT).show();
+                    finish();
+                    break;
+            }
+        }
+    }
+}