| /* |
| * Copyright 2020 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.calendar; |
| |
| import android.content.ContentResolver; |
| import android.content.pm.PackageManager; |
| import android.os.Bundle; |
| import android.os.StrictMode; |
| import android.util.Log; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| import androidx.fragment.app.FragmentActivity; |
| import androidx.lifecycle.ViewModel; |
| import androidx.lifecycle.ViewModelProvider; |
| |
| import com.android.car.calendar.common.CalendarFormatter; |
| import com.android.car.calendar.common.Dialer; |
| import com.android.car.calendar.common.Navigator; |
| |
| import com.google.common.collect.HashMultimap; |
| import com.google.common.collect.Multimap; |
| |
| import java.time.Clock; |
| import java.util.Collection; |
| import java.util.Locale; |
| |
| /** The main Activity for the Car Calendar App. */ |
| public class CarCalendarActivity extends FragmentActivity { |
| private static final String TAG = "CarCalendarActivity"; |
| private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| |
| private final Multimap<String, Runnable> mPermissionToCallbacks = HashMultimap.create(); |
| |
| // Allows tests to replace certain dependencies. |
| @VisibleForTesting Dependencies mDependencies; |
| |
| @Override |
| protected void onCreate(@Nullable Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| maybeEnableStrictMode(); |
| |
| // Tests can set fake dependencies before onCreate. |
| if (mDependencies == null) { |
| mDependencies = new Dependencies( |
| Locale.getDefault(), Clock.systemDefaultZone(), getContentResolver()); |
| } |
| |
| CarCalendarViewModel carCalendarViewModel = |
| new ViewModelProvider( |
| this, |
| new CarCalendarViewModelFactory( |
| mDependencies.mResolver, |
| mDependencies.mLocale, |
| mDependencies.mClock)) |
| .get(CarCalendarViewModel.class); |
| |
| CarCalendarView carCalendarView = |
| new CarCalendarView( |
| this, |
| carCalendarViewModel, |
| new Navigator(this), |
| new Dialer(this), |
| new CalendarFormatter( |
| this.getApplicationContext(), |
| mDependencies.mLocale, |
| mDependencies.mClock)); |
| |
| carCalendarView.show(); |
| } |
| |
| private void maybeEnableStrictMode() { |
| if (DEBUG) { |
| Log.i(TAG, "Enabling strict mode"); |
| StrictMode.setThreadPolicy( |
| new StrictMode.ThreadPolicy.Builder() |
| .detectAll() |
| .penaltyLog() |
| .penaltyDeath() |
| .build()); |
| StrictMode.setVmPolicy( |
| new StrictMode.VmPolicy.Builder() |
| .detectAll() |
| .penaltyLog() |
| .penaltyDeath() |
| .build()); |
| } |
| } |
| |
| /** |
| * Calls the given runnable only if the required permission is granted. |
| * |
| * <p>If the permission is already granted then the runnable is called immediately. Otherwise |
| * the runnable is retained and the permission is requested. If the permission is granted the |
| * runnable will be called otherwise it will be discarded. |
| */ |
| void runWithPermission(String permission, Runnable runnable) { |
| if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) { |
| // Run immediately if we already have permission. |
| if (DEBUG) Log.d(TAG, "Running with " + permission); |
| runnable.run(); |
| } else { |
| // Keep the runnable until the permission is granted. |
| if (DEBUG) Log.d(TAG, "Waiting for " + permission); |
| mPermissionToCallbacks.put(permission, runnable); |
| requestPermissions(new String[] {permission}, /* requestCode= */ 0); |
| } |
| } |
| |
| @Override |
| public void onRequestPermissionsResult( |
| int requestCode, String[] permissions, int[] grantResults) { |
| super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
| for (int i = 0; i < permissions.length; i++) { |
| String permission = permissions[i]; |
| int grantResult = grantResults[i]; |
| Collection<Runnable> callbacks = mPermissionToCallbacks.removeAll(permission); |
| if (grantResult == PackageManager.PERMISSION_GRANTED) { |
| Log.e(TAG, "Permission " + permission + " granted"); |
| callbacks.forEach(Runnable::run); |
| } else { |
| // TODO(jdp) Also allow a denied runnable. |
| Log.e(TAG, "Permission " + permission + " not granted"); |
| } |
| } |
| } |
| |
| private static class CarCalendarViewModelFactory implements ViewModelProvider.Factory { |
| private final ContentResolver mResolver; |
| private final Locale mLocale; |
| private final Clock mClock; |
| |
| CarCalendarViewModelFactory(ContentResolver resolver, Locale locale, Clock clock) { |
| mResolver = resolver; |
| mLocale = locale; |
| mClock = clock; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @NonNull |
| @Override |
| public <T extends ViewModel> T create(@NonNull Class<T> aClass) { |
| return (T) new CarCalendarViewModel(mResolver, mLocale, mClock); |
| } |
| } |
| |
| static class Dependencies { |
| private final Locale mLocale; |
| private final Clock mClock; |
| private final ContentResolver mResolver; |
| |
| Dependencies(Locale locale, Clock clock, ContentResolver resolver) { |
| mLocale = locale; |
| mClock = clock; |
| mResolver = resolver; |
| } |
| } |
| } |