• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.calendar;
18 
19 import android.content.ContentResolver;
20 import android.content.pm.PackageManager;
21 import android.os.Bundle;
22 import android.os.StrictMode;
23 import android.telephony.TelephonyManager;
24 import android.util.Log;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.VisibleForTesting;
29 import androidx.fragment.app.FragmentActivity;
30 import androidx.lifecycle.ViewModel;
31 import androidx.lifecycle.ViewModelProvider;
32 
33 import com.android.car.calendar.common.CalendarFormatter;
34 import com.android.car.calendar.common.Dialer;
35 import com.android.car.calendar.common.Navigator;
36 
37 import com.google.common.collect.HashMultimap;
38 import com.google.common.collect.Multimap;
39 
40 import java.time.Clock;
41 import java.util.Collection;
42 import java.util.Locale;
43 
44 /** The main Activity for the Car Calendar App. */
45 public class CarCalendarActivity extends FragmentActivity {
46     private static final String TAG = "CarCalendarActivity";
47     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
48 
49     private final Multimap<String, Runnable> mPermissionToCallbacks = HashMultimap.create();
50 
51     // Allows tests to replace certain dependencies.
52     @VisibleForTesting Dependencies mDependencies;
53 
54     @Override
onCreate(@ullable Bundle savedInstanceState)55     protected void onCreate(@Nullable Bundle savedInstanceState) {
56         super.onCreate(savedInstanceState);
57         maybeEnableStrictMode();
58 
59         // Tests can set fake dependencies before onCreate.
60         if (mDependencies == null) {
61             mDependencies = new Dependencies(
62                     Locale.getDefault(),
63                     Clock.systemDefaultZone(),
64                     getContentResolver(),
65                     getSystemService(TelephonyManager.class));
66         }
67 
68         CarCalendarViewModel carCalendarViewModel =
69                 new ViewModelProvider(
70                                 this,
71                                 new CarCalendarViewModelFactory(
72                                         mDependencies.mResolver,
73                                         mDependencies.mLocale,
74                                         mDependencies.mTelephonyManager,
75                                         mDependencies.mClock))
76                         .get(CarCalendarViewModel.class);
77 
78         CarCalendarView carCalendarView =
79                 new CarCalendarView(
80                         this,
81                         carCalendarViewModel,
82                         new Navigator(this),
83                         new Dialer(this),
84                         new CalendarFormatter(
85                                 this.getApplicationContext(),
86                                 mDependencies.mLocale,
87                                 mDependencies.mClock));
88 
89         carCalendarView.show();
90     }
91 
maybeEnableStrictMode()92     private void maybeEnableStrictMode() {
93         if (DEBUG) {
94             Log.i(TAG, "Enabling strict mode");
95             StrictMode.setThreadPolicy(
96                     new StrictMode.ThreadPolicy.Builder()
97                             .detectAll()
98                             .penaltyLog()
99                             .penaltyDeath()
100                             .build());
101             StrictMode.setVmPolicy(
102                     new StrictMode.VmPolicy.Builder()
103                             .detectAll()
104                             .penaltyLog()
105                             .penaltyDeath()
106                             .build());
107         }
108     }
109 
110     /**
111      * Calls the given runnable only if the required permission is granted.
112      *
113      * <p>If the permission is already granted then the runnable is called immediately. Otherwise
114      * the runnable is retained and the permission is requested. If the permission is granted the
115      * runnable will be called otherwise it will be discarded.
116      */
runWithPermission(String permission, Runnable runnable)117     void runWithPermission(String permission, Runnable runnable) {
118         if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
119             // Run immediately if we already have permission.
120             if (DEBUG) Log.d(TAG, "Running with " + permission);
121             runnable.run();
122         } else {
123             // Keep the runnable until the permission is granted.
124             if (DEBUG) Log.d(TAG, "Waiting for " + permission);
125             mPermissionToCallbacks.put(permission, runnable);
126             requestPermissions(new String[] {permission}, /* requestCode= */ 0);
127         }
128     }
129 
130     @Override
onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults)131     public void onRequestPermissionsResult(
132             int requestCode, String[] permissions, int[] grantResults) {
133         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
134         for (int i = 0; i < permissions.length; i++) {
135             String permission = permissions[i];
136             int grantResult = grantResults[i];
137             Collection<Runnable> callbacks = mPermissionToCallbacks.removeAll(permission);
138             if (grantResult == PackageManager.PERMISSION_GRANTED) {
139                 Log.e(TAG, "Permission " + permission + " granted");
140                 callbacks.forEach(Runnable::run);
141             } else {
142                 // TODO(jdp) Also allow a denied runnable.
143                 Log.e(TAG, "Permission " + permission + " not granted");
144             }
145         }
146     }
147 
148     private static class CarCalendarViewModelFactory implements ViewModelProvider.Factory {
149         private final ContentResolver mResolver;
150         private final TelephonyManager mTelephonyManager;
151         private final Locale mLocale;
152         private final Clock mClock;
153 
CarCalendarViewModelFactory( ContentResolver resolver, Locale locale, TelephonyManager telephonyManager, Clock clock)154         CarCalendarViewModelFactory(
155                 ContentResolver resolver,
156                 Locale locale,
157                 TelephonyManager telephonyManager,
158                 Clock clock) {
159             mResolver = resolver;
160             mLocale = locale;
161             mTelephonyManager = telephonyManager;
162             mClock = clock;
163         }
164 
165         @SuppressWarnings("unchecked")
166         @NonNull
167         @Override
create(@onNull Class<T> aClass)168         public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
169             return (T) new CarCalendarViewModel(mResolver, mLocale, mTelephonyManager, mClock);
170         }
171     }
172 
173     static class Dependencies {
174         private final Locale mLocale;
175         private final Clock mClock;
176         private final ContentResolver mResolver;
177         private final TelephonyManager mTelephonyManager;
178 
Dependencies( Locale locale, Clock clock, ContentResolver resolver, TelephonyManager telephonyManager)179         Dependencies(
180                 Locale locale,
181                 Clock clock,
182                 ContentResolver resolver,
183                 TelephonyManager telephonyManager) {
184             mLocale = locale;
185             mClock = clock;
186             mResolver = resolver;
187             mTelephonyManager = telephonyManager;
188         }
189     }
190 }
191